mcpField
P
prefab-optimization

prefab-optimization

github.com/datadrivenconstruction生产力375101 次安装1 周前更新
代码仓库

关于 prefab-optimization

prefab-optimization 是用于优化预制和模块化施工的技能,涵盖模块设计、工厂排程、运输、现场装配顺序、设备规划和质量检查点。

主要功能

  1. 01优化模块运输设计
  2. 02安排工厂生产计划
  3. 03规划物流与运输
  4. 04优化现场装配顺序
  5. 05规划起重机和设备
  6. 06设置质量控制检查点

适用场景

  1. 01规划模块化建筑生产
  2. 02为模块分配运输车辆
  3. 03安排现场模块装配顺序
  4. 04协调设备规划与质量检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction --skill prefab-optimization

安装后在 Claude Code 中输入/prefab-optimization确认已加载。

附带文件claw.jsoninstructions.md

详细介绍

预制优化

概述

此技能实现了预制和模块化施工的优化算法。提高工厂利用率,降低运输成本,并优化现场装配顺序。

优化领域:

  • 面向运输的模块设计
  • 工厂生产排程
  • 物流与运输
  • 现场装配排序
  • 起重机与设备规划
  • 质量控制检查点

快速入门

from dataclasses import dataclass, field
from datetime import date, datetime, timedelta
from typing import List, Dict, Tuple, Optional
from enum import Enum

class ModuleStatus(Enum):
    DESIGN = "design"
    PRODUCTION = "production"
    QC = "quality_control"
    STORAGE = "storage"
    TRANSPORT = "transport"
    ON_SITE = "on_site"
    INSTALLED = "installed"

@dataclass
class PrefabModule:
    module_id: str
    name: str
    module_type: str
    dimensions: Tuple[float, float, float]  # L, W, H in meters
    weight_kg: float
    status: ModuleStatus = ModuleStatus.DESIGN
    production_hours: float = 0
    dependencies: List[str] = field(default_factory=list)

@dataclass
class ProductionSlot:
    slot_id: str
    start_time: datetime
    end_time: datetime
    bay_id: str
    module_id: str

def calculate_transport_constraints(module: PrefabModule) -> Dict:
    """Calculate transport constraints for module"""
    L, W, H = module.dimensions

    # Standard transport limits (varies by region)
    max_width = 4.0  # meters
    max_height = 4.5  # meters
    max_length = 12.0  # meters
    max_weight = 40000  # kg

    constraints = {
        'within_standard': True,
        'requires_escort': False,
        'requires_permit': False,
        'transport_type': 'standard'
    }

    if W > max_width or H > max_height:
        constraints['within_standard'] = False
        constraints['requires_escort'] = True
        constraints['requires_permit'] = True
        constraints['transport_type'] = 'wide_load'

    if L > max_length:
        constraints['requires_permit'] = True
        constraints['transport_type'] = 'long_load'

    if module.weight_kg > max_weight:
        constraints['requires_permit'] = True
        constraints['transport_type'] = 'heavy_load'

    return constraints

# Example
module = PrefabModule(
    module_id="MOD-001",
    name="Bathroom Pod Type A",
    module_type="bathroom",
    dimensions=(4.5, 3.0, 3.2),
    weight_kg=8500,
    production_hours=40
)

constraints = calculate_transport_constraints(module)
print(f"Module {module.name}: {constraints}")

综合预制系统

模块定义与分析
from dataclasses import dataclass, field
from datetime import date, datetime, timedelta
from typing import List, Dict, Tuple, Optional, Set
from enum import Enum
import numpy as np

class ModuleCategory(Enum):
    BATHROOM_POD = "bathroom_pod"
    KITCHEN_POD = "kitchen_pod"
    STRUCTURAL = "structural"
    FACADE = "facade"
    MEP = "mep"
    STAIR = "stair"
    ELEVATOR = "elevator"
    ROOM_MODULE = "room_module"

@dataclass
class ModuleConnection:
    connection_id: str
    connection_type: str  # structural, mep, electrical
    from_module: str
    to_module: str
    from_point: Tuple[float, float, float]
    to_point: Tuple[float, float, float]
    tolerance_mm: float = 10

@dataclass
class ModuleDesign:
    module_id: str
    name: str
    category: ModuleCategory
    version: str

    # Dimensions and weight
    length_m: float
    width_m: float
    height_m: float
    weight_kg: float

    # Production
    production_hours: float
    required_skills: List[str]
    materials_list: List[Dict]

    # Connections
    connections: List[ModuleConnection] = field(default_factory=list)

    # Dependencies
    required_modules: List[str] = field(default_factory=list)  # Must be installed before
    blocks_modules: List[str] = field(default_factory=list)  # Cannot install until this is done

    # Metadata
    floor_level: int = 0
    grid_position: Tuple[str, str] = ('', '')  # Grid reference
    zone: str = ''

    @property
    def volume_m3(self) -> float:
        return self.length_m * self.width_m * self.height_m

    @property
    def footprint_m2(self) -> float:
        return self.length_m * self.width_m

class ModuleAnalyzer:
    """Analyze prefab modules for optimization"""

    def __init__(self):
        self.transport_limits = {
            'standard': {'width': 2.55, 'height': 4.0, 'length': 12.0, 'weight': 25000},
            'wide_load': {'width': 4.0, 'height': 4.5, 'length': 16.0, 'weight': 40000},
            'special': {'width': 6.0, 'height': 5.0, 'length': 25.0, 'weight': 100000}
        }

    def analyze_transportability(self, module: ModuleDesign) -> Dict:
        """Analyze module transportability"""
        dims = (module.length_m, module.width_m, module.height_m)

        # Check against limits
        for transport_type, limits in self.transport_limits.items():
            if (max(dims[0], dims[1]) <= limits['length'] and
                min(dims[0], dims[1]) <= limits['width'] and
                dims[2] <= limits['height'] and
                module.weight_kg <= limits['weight']):

                analysis = {
                    'feasible': True,
                    'transport_type': transport_type,
                    'orientation': 'length_first' if dims[0] >= dims[1] else 'width_first',
                    'utilization': {
                        'length': max(dims[0], dims[1]) / limits['length'],
                        'width': min(dims[0], dims[1]) / limits['width'],
                        'height': dims[2] / limits['height'],
                        'weight': module.weight_kg / limits['weight']
                    }
                }

                if transport_type == 'standard':
                    analysis['cost_factor'] = 1.0
                elif transport_type == 'wide_load':
                    analysis['cost_factor'] = 1.5
                    analysis['requirements'] = ['escort_vehicle', 'permit', 'route_survey']
                else:
                    analysis['cost_factor'] = 3.0
                    analysis['requirements'] = ['police_escort', 'special_permit', 'night_transport']

                return analysis

        return {
            'feasible': False,
            'reason': 'Exceeds maximum transport dimensions',
            'max_dimension': max(dims),
            'recommendation': 'Consider splitting into smaller modules'
        }

    def analyze_lifting(self, module: ModuleDesign) -> Dict:
        """Analyze lifting requirements"""
        # Estimate crane capacity needed (with safety factor)
        safety_factor = 1.25
        required_capacity = module.weight_kg * safety_factor / 1000  # tonnes

        # Estimate boom length based on typical building heights
        floor_height = 3.5  # meters per floor
        estimated_height = module.floor_level * floor_height + 10  # +10m clearance

        # Rough crane selection
        if required_capacity <= 50 and estimated_height <= 30:
            crane_type = 'mobile_50t'
        elif required_capacity <= 100 and estimated_height <= 50:
            crane_type = 'mobile_100t'
        elif required_capacity <= 200:
            crane_type = 'crawler_200t'
        else:
            crane_type = 'tower_crane'

        return {
            'required_capacity_tonnes': required_capacity,
            'estimated_lift_height_m': estimated_height,
            'recommended_crane': crane_type,
            'lift_points': self._calculate_lift_points(module),
            'center_of_gravity': (module.length_m / 2, module.width_m / 2, module.height_m / 3)
        }

    def _calculate_lift_points(self, module: ModuleDesign) -> List[Tuple[float, float]]:
        """Calculate optimal lift point positions"""
        L, W = module.length_m, module.width_m

        # Standard 4-point lift
        offset = 0.2  # 20% from edges
        return [
            (L * offset, W * offset),
            (L * (1 - offset), W * offset),
            (L * offset, W * (1 - offset)),
            (L * (1 - offset), W * (1 - offset))
        ]
生产排程
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import heapq

@dataclass
class ProductionBay:
    bay_id: str
    bay_type: str  # assembly, finishing, storage
    capacity_m2: float
    available_from: datetime
    skills_available: List[str]

@dataclass
class ProductionOrder:
    module_id: str
    required_date: date
    priority: int  # 1 = highest
    production_hours: float
    required_bay_type: str
    required_skills: List[str]

class ProductionScheduler:
    """Schedule prefab module production"""

    def __init__(self, work_hours_per_day: float = 8):
        self.bays: Dict[str, ProductionBay] = {}
        self.schedule: Dict[str, List[ProductionSlot]] = {}
        self.work_hours = work_hours_per_day

    def add_bay(self, bay: ProductionBay):
        """Add production bay"""
        self.bays[bay.bay_id] = bay
        self.schedule[bay.bay_id] = []

    def schedule_production(self, orders: List[ProductionOrder]) -> Dict:
        """Schedule all production orders"""
        # Sort by priority and required date
        sorted_orders = sorted(orders, key=lambda o: (o.priority, o.required_date))

        scheduled = []
        unscheduled = []

        for order in sorted_orders:
            slot = self._find_best_slot(order)
            if slot:
                self.schedule[slot.bay_id].append(slot)
                scheduled.append({
                    'module_id': order.module_id,
                    'bay_id': slot.bay_id,
                    'start': slot.start_time.isoformat(),
                    'end': slot.end_time.isoformat(),
                    'duration_hours': order.production_hours
                })
            else:
                unscheduled.append(order.module_id)

        return {
            'scheduled': scheduled,
            'unscheduled': unscheduled,
            'utilization': self._calculate_utilization()
        }

    def _find_best_slot(self, order: ProductionOrder) -> Optional[ProductionSlot]:
        """Find best production slot for order"""
        best_slot = None
        best_end_time = datetime.max

        for bay_id, bay in self.bays.items():
            if bay.bay_type != order.required_bay_type:
                continue

            if not set(order.required_skills).issubset(set(bay.skills_available)):
                continue

            # Find earliest available time
            existing_slots = self.schedule.get(bay_id, [])
            if existing_slots:
                last_end = max(s.end_time for s in existing_slots)
                start_time = max(bay.available_from, last_end)
            else:
                start_time = bay.available_from

            # Calculate end time
            production_days = order.production_hours / self.work_hours
            end_time = start_time + timedelta(days=production_days)

            # Check if this meets deadline
            required_datetime = datetime.combine(order.required_date, datetime.min.time())
            if end_time <= required_datetime and end_time < best_end_time:
                best_end_time = end_time
                best_slot = ProductionSlot(
                    slot_id=f"SLOT-{bay_id}-{len(existing_slots)}",
                    start_time=start_time,
                    end_time=end_time,
                    bay_id=bay_id,
                    module_id=order.module_id
                )

        return best_slot

    def _calculate_utilization(self) -> Dict[str, float]:
        """Calculate bay utilization"""
        utilization = {}

        for bay_id, slots in self.schedule.items():
            if not slots:
                utilization[bay_id] = 0.0
                continue

            total_time = (max(s.end_time for s in slots) -
                         min(s.start_time for s in slots)).total_seconds()
            used_time = sum((s.end_time - s.start_time).total_seconds() for s in slots)

            utilization[bay_id] = used_time / total_time if total_time > 0 else 0

        return utilization

    def get_gantt_data(self) -> List[Dict]:
        """Get data for Gantt chart visualization"""
        gantt_data = []

        for bay_id, slots in self.schedule.items():
            for slot in slots:
                gantt_data.append({
                    'bay': bay_id,
                    'module': slot.module_id,
                    'start': slot.start_time.isoformat(),
                    'end': slot.end_time.isoformat()
                })

        return sorted(gantt_data, key=lambda x: x['start'])
装配顺序优化
from collections import defaultdict, deque
from typing import List, Dict, Set

class AssemblySequencer:
    """Optimize on-site module assembly sequence"""

    def __init__(self, modules: List[ModuleDesign]):
        self.modules = {m.module_id: m for m in modules}
        self.dependency_graph = self._build_dependency_graph()

    def _build_dependency_graph(self) -> Dict[str, Set[str]]:
        """Build dependency graph from module dependencies"""
        graph = defaultdict(set)

        for module_id, module in self.modules.items():
            for dep_id in module.required_modules:
                graph[module_id].add(dep_id)

        return graph

    def calculate_sequence(self) -> List[List[str]]:
        """Calculate optimal assembly sequence using topological sort"""
        # Calculate in-degree for each node
        in_degree = defaultdict(int)
        for module_id in self.modules:
            in_degree[module_id] = 0

        for deps in self.dependency_graph.values():
            for dep in deps:
                in_degree[dep] += 1

        # Start with modules that have no dependencies
        queue = deque([
            m_id for m_id in self.modules
            if len(self.dependency_graph[m_id]) == 0
        ])

        sequence = []
        current_level = []

        while queue:
            # Process current level
            current_level = list(queue)
            queue.clear()
            sequence.append(current_level)

            # Find next level
            for module_id in current_level:
                for dependent in self.modules:
                    if module_id in self.dependency_graph[dependent]:
                        self.dependency_graph[dependent].remove(module_id)
                        if len(self.dependency_graph[dependent]) == 0:
                            queue.append(dependent)

        # Check for cycles
        remaining = [m_id for m_id in self.modules if m_id not in
                    [item for sublist in sequence for item in sublist]]
        if remaining:
            print(f"Warning: Circular dependencies detected for: {remaining}")

        return sequence

    def optimize_for_crane(self, crane_positions: List[Tuple[float, float]]) -> List[Dict]:
        """Optimize sequence considering crane movement"""
        base_sequence = self.calculate_sequence()
        optimized = []

        for level in base_sequence:
            # Sort modules in level by proximity to crane positions
            level_with_positions = []
            for module_id in level:
                module = self.modules[module_id]
                # Use grid position to calculate distance
                grid_x = ord(module.grid_position[0]) if module.grid_position[0] else 0
                grid_y = int(module.grid_position[1]) if module.grid_position[1].isdigit() else 0

                # Find nearest crane
                min_dist = float('inf')
                for cx, cy in crane_positions:
                    dist = ((grid_x - cx) ** 2 + (grid_y - cy) ** 2) ** 0.5
                    min_dist = min(min_dist, dist)

                level_with_positions.append({
                    'module_id': module_id,
                    'floor': module.floor_level,
                    'distance_to_crane': min_dist
                })

            # Sort by floor (bottom-up) then by crane distance
            level_with_positions.sort(key=lambda x: (x['floor'], x['distance_to_crane']))
            optimized.append(level_with_positions)

        return optimized

    def generate_installation_plan(self) -> List[Dict]:
        """Generate detailed installation plan"""
        sequence = self.calculate_sequence()
        plan = []
        day = 1
        modules_per_day = 4  # Adjust based on crane capacity

        for level_idx, level in enumerate(sequence):
            for i in range(0, len(level), modules_per_day):
                batch = level[i:i + modules_per_day]

                for module_id in batch:
                    module = self.modules[module_id]
                    plan.append({
                        'day': day,
                        'sequence': len(plan) + 1,
                        'module_id': module_id,
                        'module_name': module.name,
                        'floor': module.floor_level,
                        'grid': module.grid_position,
                        'weight_kg': module.weight_kg,
                        'connections': len(module.connections),
                        'level': level_idx + 1
                    })

                day += 1

        return plan
运输优化
from scipy.optimize import linear_sum_assignment
import numpy as np

@dataclass
class TransportVehicle:
    vehicle_id: str
    capacity_kg: float
    max_length_m: float
    max_width_m: float
    max_height_m: float
    cost_per_km: float
    available_from: datetime

class TransportOptimizer:
    """Optimize module transportation logistics"""

    def __init__(self, factory_location: Tuple[float, float],
                 site_location: Tuple[float, float]):
        self.factory = factory_location
        self.site = site_location
        self.distance_km = self._calculate_distance()
        self.vehicles: List[TransportVehicle] = []
        self.routes: List[Dict] = []

    def _calculate_distance(self) -> float:
        """Calculate distance between factory and site"""
        # Simplified Haversine for demonstration
        lat1, lon1 = self.factory
        lat2, lon2 = self.site
        R = 6371  # Earth radius in km

        dlat = np.radians(lat2 - lat1)
        dlon = np.radians(lon2 - lon1)
        a = (np.sin(dlat/2)**2 +
             np.cos(np.radians(lat1)) * np.cos(np.radians(lat2)) *
             np.sin(dlon/2)**2)
        c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a))

        return R * c

    def add_vehicle(self, vehicle: TransportVehicle):
        self.vehicles.append(vehicle)

    def optimize_loading(self, modules: List[ModuleDesign],
                        required_dates: Dict[str, date]) -> Dict:
        """Optimize which modules go on which vehicle"""
        # Sort modules by required date
        sorted_modules = sorted(
            modules,
            key=lambda m: required_dates.get(m.module_id, date.max)
        )

        assignments = []
        remaining_modules = list(sorted_modules)

        while remaining_modules:
            # Find best vehicle for next batch
            for vehicle in self.vehicles:
                batch = self._pack_vehicle(vehicle, remaining_modules)
                if batch:
                    assignments.append({
                        'vehicle_id': vehicle.vehicle_id,
                        'modules': [m.module_id for m in batch],
                        'total_weight_kg': sum(m.weight_kg for m in batch),
                        'capacity_used': sum(m.weight_kg for m in batch) / vehicle.capacity_kg,
                        'cost': vehicle.cost_per_km * self.distance_km * 2  # Round trip
                    })

                    for m in batch:
                        remaining_modules.remove(m)
                    break
            else:
                # No vehicle can take remaining modules
                break

        return {
            'assignments': assignments,
            'total_trips': len(assignments),
            'total_cost': sum(a['cost'] for a in assignments),
            'unassigned': [m.module_id for m in remaining_modules]
        }

    def _pack_vehicle(self, vehicle: TransportVehicle,
                     modules: List[ModuleDesign]) -> List[ModuleDesign]:
        """Pack modules onto vehicle (simplified bin packing)"""
        packed = []
        total_weight = 0

        for module in modules:
            # Check if module fits
            dims = sorted([module.length_m, module.width_m])

            if (dims[0] <= vehicle.max_width_m and
                dims[1] <= vehicle.max_length_m and
                module.height_m <= vehicle.max_height_m and
                total_weight + module.weight_kg <= vehicle.capacity_kg):

                packed.append(module)
                total_weight += module.weight_kg

                # Simple: one module per trip for large items
                if max(dims) > 6 or module.weight_kg > vehicle.capacity_kg * 0.7:
                    break

        return packed

    def schedule_deliveries(self, assignments: List[Dict],
                           site_constraints: Dict = None) -> List[Dict]:
        """Schedule deliveries considering site constraints"""
        schedule = []

        # Default constraints
        if site_constraints is None:
            site_constraints = {
                'unload_start_hour': 7,
                'unload_end_hour': 17,
                'max_deliveries_per_day': 4,
                'unload_time_minutes': 60
            }

        current_date = date.today()
        deliveries_today = 0
        current_hour = site_constraints['unload_start_hour']

        for assignment in assignments:
            if deliveries_today >= site_constraints['max_deliveries_per_day']:
                current_date += timedelta(days=1)
                deliveries_today = 0
                current_hour = site_constraints['unload_start_hour']

            if current_hour >= site_constraints['unload_end_hour']:
                current_date += timedelta(days=1)
                deliveries_today = 0
                current_hour = site_constraints['unload_start_hour']

            schedule.append({
                **assignment,
                'delivery_date': current_date.isoformat(),
                'arrival_time': f"{current_hour:02d}:00",
                'departure_time': f"{current_hour + 1:02d}:00"
            })

            current_hour += 2  # 1 hour unload + 1 hour buffer
            deliveries_today += 1

        return schedule

快速参考

模块类型典型尺寸(长×宽×高)典型重量生产时间
浴室模块3.0 x 2.4 x 2.7m4,000-8,000 kg30-50 hours
厨房模块4.0 x 2.4 x 2.7m5,000-10,000 kg40-60 hours
房间模块6.0 x 3.0 x 3.0m15,000-25,000 kg60-100 hours
外墙板6.0 x 3.0 x 0.3m2,000-4,000 kg15-25 hours
楼梯模块4.0 x 2.5 x 3.5m8,000-12,000 kg35-50 hours

各地区运输限制

地区最大宽度最大高度最大长度最大重量
欧盟标准2.55m4.0m12.0m40t
欧盟超宽载荷4.0m4.5m16.5m60t
美国标准2.6m4.1m14.6m36t
美国超限尺寸4.3m4.6m22.0m60t

资源

后续步骤

  • 参阅 site-logistics-optimization,了解现场交付排程
  • 参阅 4d-simulation,了解装配顺序可视化
  • 参阅 bim-validation-pipeline,了解模块质量检查

评论

支持 Markdown