site-logistics-optimization について
建設現場の物流計画を支援する Python スキルです。納入、設備、保管、現場内の移動を調整するチーム向けに、スケジュールや配置の最適化、クレーン配置、交通シミュレーションを扱います。
主な機能
- 01資材納入スケジュール
- 02保管エリアの割り当て
- 03クレーン・設備の配置
- 04現場交通流のシミュレーション
- 05作業員のルート計画
- 06ジャストインタイム納入計画
活用シーン
- 01建設現場への資材納入を計画
- 02プロジェクト資材の保管場所を割り当て
- 03現場のクレーン配置を決定
- 04現場交通をシミュレーションして最適化
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ datadrivenconstruction/ ddc_skills_for_ai_agents_in_construction --skill site-logistics-optimizationインストール後、Claude Code で/site-logistics-optimizationを入力して読み込まれたか確認します。
同梱ファイルclaw.jsoninstructions.md
詳細
現場物流の最適化
概要
このスキルでは、建設現場の物流向けに最適化アルゴリズムを実装します。データに基づいて資材の配送、機材の配置、資材の保管を計画し、遅延とコストを削減するとともに、安全性を高めます。
最適化の対象:
- 資材の配送スケジュール
- クレーンと機材の配置
- 保管エリアの割り当て
- 現場内の交通流
- 作業員の移動経路
- ジャストインタイム配送
クイックスタート
from dataclasses import dataclass
from typing import List, Dict, Tuple
from datetime import datetime, timedelta
import heapq
@dataclass
class Delivery:
delivery_id: str
material_type: str
quantity: float
required_date: datetime
unload_duration_min: int
storage_area: str
priority: int = 1 # 1=highest
@dataclass
class TimeSlot:
start: datetime
end: datetime
is_available: bool = True
delivery_id: str = None
def schedule_deliveries(deliveries: List[Delivery],
slots_per_day: int = 8,
unload_bays: int = 2) -> Dict[str, TimeSlot]:
"""Simple delivery scheduling"""
# Sort by priority and required date
sorted_deliveries = sorted(deliveries, key=lambda d: (d.priority, d.required_date))
schedule = {}
bay_schedules = {i: [] for i in range(unload_bays)}
for delivery in sorted_deliveries:
# Find available slot
target_date = delivery.required_date.replace(hour=8, minute=0)
for bay in range(unload_bays):
# Check if bay has capacity
bay_end = max([s.end for s in bay_schedules[bay]], default=target_date)
if bay_end <= target_date:
slot_start = target_date
else:
slot_start = bay_end
slot_end = slot_start + timedelta(minutes=delivery.unload_duration_min)
# Check if within working hours (8:00-18:00)
if slot_end.hour <= 18:
slot = TimeSlot(
start=slot_start,
end=slot_end,
is_available=False,
delivery_id=delivery.delivery_id
)
bay_schedules[bay].append(slot)
schedule[delivery.delivery_id] = {
'bay': bay,
'slot': slot
}
break
return schedule
# Example
deliveries = [
Delivery("D001", "concrete", 50, datetime(2024, 1, 15, 9, 0), 45, "Zone-A", 1),
Delivery("D002", "rebar", 10, datetime(2024, 1, 15, 10, 0), 30, "Zone-B", 2),
Delivery("D003", "formwork", 20, datetime(2024, 1, 15, 9, 0), 60, "Zone-A", 1),
]
schedule = schedule_deliveries(deliveries)
for d_id, info in schedule.items():
print(f"{d_id}: Bay {info['bay']}, {info['slot'].start.strftime('%H:%M')}-{info['slot'].end.strftime('%H:%M')}")
物流の包括的な最適化
現場レイアウトモデル
from dataclasses import dataclass, field
from typing import List, Dict, Tuple, Optional
from datetime import datetime, date, timedelta
from enum import Enum
import numpy as np
from scipy.optimize import linear_sum_assignment
import heapq
class ZoneType(Enum):
CONSTRUCTION = "construction"
STORAGE = "storage"
UNLOADING = "unloading"
STAGING = "staging"
ACCESS = "access"
EQUIPMENT = "equipment"
OFFICE = "office"
@dataclass
class SiteZone:
zone_id: str
zone_type: ZoneType
area_sqm: float
capacity: float # Depends on type (tons, units, etc.)
current_usage: float = 0
position: Tuple[float, float] = (0, 0) # x, y coordinates
access_points: List[Tuple[float, float]] = field(default_factory=list)
restrictions: List[str] = field(default_factory=list)
@dataclass
class Equipment:
equipment_id: str
equipment_type: str # crane, forklift, etc.
max_reach: float # meters
capacity: float # tons
position: Tuple[float, float] = (0, 0)
operating_radius: float = 0
@dataclass
class DeliveryRequest:
request_id: str
material_type: str
quantity: float
unit: str
required_date: date
required_time_window: Tuple[int, int] # (start_hour, end_hour)
unload_duration_min: int
vehicle_type: str
destination_zone: str
priority: int = 1
requires_crane: bool = False
class SiteLogisticsModel:
"""Construction site logistics model"""
def __init__(self, site_name: str):
self.site_name = site_name
self.zones: Dict[str, SiteZone] = {}
self.equipment: Dict[str, Equipment] = {}
self.deliveries: List[DeliveryRequest] = []
self.routes: Dict[str, List[Tuple[float, float]]] = {}
def add_zone(self, zone: SiteZone):
"""Add zone to site"""
self.zones[zone.zone_id] = zone
def add_equipment(self, equipment: Equipment):
"""Add equipment to site"""
self.equipment[equipment.equipment_id] = equipment
def add_delivery(self, delivery: DeliveryRequest):
"""Add delivery request"""
self.deliveries.append(delivery)
def calculate_distance(self, point1: Tuple[float, float],
point2: Tuple[float, float]) -> float:
"""Calculate Euclidean distance"""
return np.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)
def get_zone_distances(self) -> Dict[Tuple[str, str], float]:
"""Calculate distances between all zones"""
distances = {}
zone_ids = list(self.zones.keys())
for i, z1 in enumerate(zone_ids):
for z2 in zone_ids[i+1:]:
dist = self.calculate_distance(
self.zones[z1].position,
self.zones[z2].position
)
distances[(z1, z2)] = dist
distances[(z2, z1)] = dist
return distances
def check_crane_coverage(self, crane_id: str, zone_id: str) -> bool:
"""Check if crane can reach zone"""
crane = self.equipment.get(crane_id)
zone = self.zones.get(zone_id)
if not crane or not zone:
return False
distance = self.calculate_distance(crane.position, zone.position)
return distance <= crane.max_reach
配送スケジュール最適化ツール
from datetime import datetime, date, timedelta
from typing import List, Dict, Optional
import numpy as np
@dataclass
class ScheduledDelivery:
delivery: DeliveryRequest
scheduled_date: date
scheduled_time: datetime
assigned_bay: str
assigned_crane: Optional[str]
estimated_completion: datetime
class DeliveryScheduler:
"""Optimize delivery scheduling"""
def __init__(self, site: SiteLogisticsModel):
self.site = site
self.schedule: Dict[date, List[ScheduledDelivery]] = {}
self.bay_capacity = 2 # Simultaneous unloading bays
self.working_hours = (7, 18) # 7 AM to 6 PM
def schedule_deliveries(self, deliveries: List[DeliveryRequest],
planning_horizon_days: int = 14) -> List[ScheduledDelivery]:
"""Schedule all deliveries optimally"""
# Sort by priority and required date
sorted_deliveries = sorted(
deliveries,
key=lambda d: (d.priority, d.required_date, -d.quantity)
)
scheduled = []
bay_schedules = {f"bay_{i}": [] for i in range(self.bay_capacity)}
for delivery in sorted_deliveries:
best_slot = self._find_best_slot(delivery, bay_schedules)
if best_slot:
sched = ScheduledDelivery(
delivery=delivery,
scheduled_date=best_slot['date'],
scheduled_time=best_slot['start_time'],
assigned_bay=best_slot['bay'],
assigned_crane=best_slot.get('crane'),
estimated_completion=best_slot['end_time']
)
scheduled.append(sched)
# Update bay schedule
bay_schedules[best_slot['bay']].append({
'delivery_id': delivery.request_id,
'start': best_slot['start_time'],
'end': best_slot['end_time']
})
return scheduled
def _find_best_slot(self, delivery: DeliveryRequest,
bay_schedules: Dict) -> Optional[Dict]:
"""Find optimal delivery slot"""
target_date = delivery.required_date
time_window = delivery.required_time_window
# Try target date first, then surrounding days
for day_offset in range(0, 7): # Look up to 7 days ahead
check_date = target_date + timedelta(days=day_offset)
for bay_id, bay_schedule in bay_schedules.items():
slot = self._find_slot_in_bay(
delivery, check_date, time_window, bay_id, bay_schedule
)
if slot:
# Check crane availability if needed
if delivery.requires_crane:
crane = self._find_available_crane(
delivery.destination_zone,
slot['start_time'],
slot['end_time']
)
if crane:
slot['crane'] = crane
else:
continue # No crane available
return slot
return None
def _find_slot_in_bay(self, delivery: DeliveryRequest,
check_date: date,
time_window: Tuple[int, int],
bay_id: str,
bay_schedule: List[Dict]) -> Optional[Dict]:
"""Find available slot in specific bay"""
start_hour = max(self.working_hours[0], time_window[0])
end_hour = min(self.working_hours[1], time_window[1])
# Get existing bookings for this date
date_bookings = [
b for b in bay_schedule
if b['start'].date() == check_date
]
# Sort by start time
date_bookings.sort(key=lambda x: x['start'])
# Find gaps
current_time = datetime.combine(check_date, datetime.min.time().replace(hour=start_hour))
end_time = datetime.combine(check_date, datetime.min.time().replace(hour=end_hour))
for booking in date_bookings:
if booking['start'] > current_time:
gap_duration = (booking['start'] - current_time).seconds // 60
if gap_duration >= delivery.unload_duration_min:
return {
'bay': bay_id,
'date': check_date,
'start_time': current_time,
'end_time': current_time + timedelta(minutes=delivery.unload_duration_min)
}
current_time = max(current_time, booking['end'])
# Check remaining time at end of day
if current_time < end_time:
remaining = (end_time - current_time).seconds // 60
if remaining >= delivery.unload_duration_min:
return {
'bay': bay_id,
'date': check_date,
'start_time': current_time,
'end_time': current_time + timedelta(minutes=delivery.unload_duration_min)
}
return None
def _find_available_crane(self, zone_id: str,
start_time: datetime,
end_time: datetime) -> Optional[str]:
"""Find available crane that can reach zone"""
for crane_id, crane in self.site.equipment.items():
if crane.equipment_type != 'crane':
continue
if self.site.check_crane_coverage(crane_id, zone_id):
# Simplified availability check
# In practice, would check crane schedule
return crane_id
return None
def get_daily_schedule(self, target_date: date) -> List[Dict]:
"""Get schedule for specific date"""
schedule = []
for sched in self.schedule.get(target_date, []):
schedule.append({
'time': sched.scheduled_time.strftime('%H:%M'),
'material': sched.delivery.material_type,
'quantity': f"{sched.delivery.quantity} {sched.delivery.unit}",
'bay': sched.assigned_bay,
'destination': sched.delivery.destination_zone,
'crane': sched.assigned_crane,
'duration': f"{sched.delivery.unload_duration_min} min"
})
return sorted(schedule, key=lambda x: x['time'])
保管エリアの最適化
class StorageOptimizer:
"""Optimize storage area allocation"""
def __init__(self, site: SiteLogisticsModel):
self.site = site
self.storage_assignments: Dict[str, List[Dict]] = {}
def allocate_storage(self, materials: List[Dict]) -> Dict[str, str]:
"""Allocate materials to storage zones
materials: List of {material_id, material_type, quantity, destination_zone, arrival_date}
"""
# Get storage zones
storage_zones = {
zid: zone for zid, zone in self.site.zones.items()
if zone.zone_type == ZoneType.STORAGE
}
# Calculate zone scores for each material
allocations = {}
zone_usage = {zid: zone.current_usage for zid, zone in storage_zones.items()}
for material in materials:
best_zone = None
best_score = -float('inf')
for zone_id, zone in storage_zones.items():
score = self._calculate_allocation_score(
material, zone, zone_usage[zone_id]
)
if score > best_score:
best_score = score
best_zone = zone_id
if best_zone:
allocations[material['material_id']] = best_zone
zone_usage[best_zone] += material['quantity']
return allocations
def _calculate_allocation_score(self, material: Dict,
zone: SiteZone,
current_usage: float) -> float:
"""Calculate score for allocating material to zone"""
# Check capacity
remaining_capacity = zone.capacity - current_usage
if remaining_capacity < material['quantity']:
return -float('inf')
score = 0
# Distance to destination (closer is better)
dest_zone = self.site.zones.get(material['destination_zone'])
if dest_zone:
distance = self.site.calculate_distance(zone.position, dest_zone.position)
score += 100 / (1 + distance) # Closer = higher score
# Available capacity (more space is slightly better)
capacity_ratio = remaining_capacity / zone.capacity
score += capacity_ratio * 20
# Material type restrictions
if zone.restrictions:
if material['material_type'] in zone.restrictions:
score -= 100 # Penalize restricted materials
return score
def get_storage_utilization(self) -> Dict[str, Dict]:
"""Get storage utilization report"""
report = {}
for zone_id, zone in self.site.zones.items():
if zone.zone_type != ZoneType.STORAGE:
continue
utilization = zone.current_usage / zone.capacity * 100 if zone.capacity > 0 else 0
report[zone_id] = {
'capacity': zone.capacity,
'used': zone.current_usage,
'available': zone.capacity - zone.current_usage,
'utilization_pct': utilization,
'status': 'critical' if utilization > 90 else 'normal' if utilization < 70 else 'high'
}
return report
クレーン配置最適化ツール
from scipy.optimize import minimize
import numpy as np
class CranePositionOptimizer:
"""Optimize crane placement on site"""
def __init__(self, site: SiteLogisticsModel):
self.site = site
def optimize_single_crane(self, crane: Equipment,
priority_zones: List[str],
constraints: Dict = None) -> Tuple[float, float]:
"""Find optimal position for a single crane"""
# Get zone positions
zone_positions = [
self.site.zones[zid].position
for zid in priority_zones
if zid in self.site.zones
]
if not zone_positions:
return crane.position
# Objective: minimize weighted distance to priority zones
def objective(pos):
total_dist = 0
for i, zone_pos in enumerate(zone_positions):
dist = np.sqrt((pos[0] - zone_pos[0])**2 + (pos[1] - zone_pos[1])**2)
weight = len(zone_positions) - i # Higher weight for earlier zones
total_dist += dist * weight
return total_dist
# Initial position
x0 = np.array([crane.position[0], crane.position[1]])
# Bounds (site boundaries)
bounds = constraints.get('bounds', [(0, 100), (0, 100)]) if constraints else [(0, 100), (0, 100)]
result = minimize(objective, x0, method='L-BFGS-B', bounds=bounds)
return tuple(result.x)
def optimize_multiple_cranes(self, cranes: List[Equipment],
zones: List[str]) -> Dict[str, Tuple[float, float]]:
"""Optimize positions for multiple cranes to maximize coverage"""
positions = {}
# Assignment problem: which crane covers which zones
zone_list = list(zones)
crane_list = list(cranes)
# Create cost matrix
n_cranes = len(crane_list)
n_zones = len(zone_list)
cost_matrix = np.zeros((n_cranes, n_zones))
for i, crane in enumerate(crane_list):
for j, zone_id in enumerate(zone_list):
zone = self.site.zones.get(zone_id)
if zone:
dist = self.site.calculate_distance(crane.position, zone.position)
# Penalize if out of reach
if dist > crane.max_reach:
cost_matrix[i, j] = dist * 10
else:
cost_matrix[i, j] = dist
# Solve assignment
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# Assign zones to cranes
crane_zones = {c.equipment_id: [] for c in crane_list}
for i, j in zip(row_ind, col_ind):
crane_zones[crane_list[i].equipment_id].append(zone_list[j])
# Optimize each crane position based on assigned zones
for crane in crane_list:
assigned_zones = crane_zones[crane.equipment_id]
if assigned_zones:
optimal_pos = self.optimize_single_crane(crane, assigned_zones)
positions[crane.equipment_id] = optimal_pos
else:
positions[crane.equipment_id] = crane.position
return positions
def visualize_coverage(self, output_path: str = None):
"""Generate crane coverage visualization data"""
coverage_data = []
for crane_id, crane in self.site.equipment.items():
if crane.equipment_type != 'crane':
continue
# Check coverage for each zone
for zone_id, zone in self.site.zones.items():
dist = self.site.calculate_distance(crane.position, zone.position)
is_covered = dist <= crane.max_reach
coverage_data.append({
'crane_id': crane_id,
'crane_x': crane.position[0],
'crane_y': crane.position[1],
'crane_reach': crane.max_reach,
'zone_id': zone_id,
'zone_x': zone.position[0],
'zone_y': zone.position[1],
'distance': dist,
'is_covered': is_covered
})
return coverage_data
交通流シミュレーション
from collections import defaultdict
import random
class TrafficSimulator:
"""Simulate and optimize site traffic flow"""
def __init__(self, site: SiteLogisticsModel):
self.site = site
self.routes: Dict[str, List[str]] = {} # route_id -> [zone_ids]
self.traffic_data: List[Dict] = []
def define_route(self, route_id: str, zones: List[str]):
"""Define a traffic route through zones"""
self.routes[route_id] = zones
def simulate_day(self, deliveries: List[ScheduledDelivery],
n_iterations: int = 100) -> Dict:
"""Simulate a day's traffic and identify bottlenecks"""
# Track zone congestion over time
congestion = defaultdict(list)
for _ in range(n_iterations):
time_slots = defaultdict(set)
for delivery in deliveries:
# Simulate vehicle movement
arrival = delivery.scheduled_time
departure = delivery.estimated_completion
# Entry route
entry_zones = ['gate', 'main_road', delivery.assigned_bay]
for zone in entry_zones:
slot = arrival.hour
time_slots[(zone, slot)].add(delivery.delivery.request_id)
# Unloading
slot = arrival.hour
time_slots[(delivery.assigned_bay, slot)].add(delivery.delivery.request_id)
# Exit route
exit_zones = [delivery.assigned_bay, 'main_road', 'gate']
for zone in exit_zones:
slot = departure.hour
time_slots[(zone, slot)].add(delivery.delivery.request_id)
# Record congestion
for (zone, slot), vehicles in time_slots.items():
congestion[(zone, slot)].append(len(vehicles))
# Analyze results
bottlenecks = []
for (zone, slot), counts in congestion.items():
avg_count = sum(counts) / len(counts)
max_count = max(counts)
if avg_count > 2 or max_count > 4: # Threshold for bottleneck
bottlenecks.append({
'zone': zone,
'time_slot': f"{slot}:00-{slot+1}:00",
'avg_vehicles': avg_count,
'max_vehicles': max_count,
'severity': 'high' if avg_count > 3 else 'medium'
})
return {
'bottlenecks': sorted(bottlenecks, key=lambda x: x['avg_vehicles'], reverse=True),
'total_deliveries': len(deliveries),
'simulation_runs': n_iterations
}
def suggest_improvements(self, simulation_results: Dict) -> List[str]:
"""Suggest traffic flow improvements"""
suggestions = []
for bottleneck in simulation_results['bottlenecks']:
zone = bottleneck['zone']
time_slot = bottleneck['time_slot']
if bottleneck['severity'] == 'high':
suggestions.append(
f"Critical congestion at {zone} during {time_slot}. "
f"Consider adding alternative access route or spreading deliveries."
)
else:
suggestions.append(
f"Moderate congestion at {zone} during {time_slot}. "
f"Consider adjusting delivery schedule."
)
return suggestions
クイックリファレンス
| 最適化 | 手法 | 計算量 |
|---|---|---|
| 配送スケジュール | 優先度ベースのヒューリスティック | O(n log n) |
| 保管場所の割り当て | スコアリング+貪欲法 | O(n × m) |
| クレーンの配置 | BFGS最適化 | 反復処理 |
| 交通シミュレーション | モンテカルロ法 | O(反復回数 × n) |
リソース
- SciPy最適化:https://docs.scipy.org/doc/scipy/reference/optimize.html
- DDCウェブサイト:https://datadrivenconstruction.io
次のステップ
- スケジュール統合については
4d-simulationを参照してください - リアルタイム追跡については
material-tracking-iotを参照してください - 物流ダッシュボードについては
data-visualizationを参照してください
コメント
Markdown 対応