vehicle-loading-optimization について
トラック、バン、配送車両の積載計画を支援する物流スキルです。車両や貨物の制約に加え、複数拠点の荷下ろし順、重量配分、積載率を考慮します。
主な機能
- 01車両と貨物の制約評価
- 02単一・複数拠点の積載計画
- 03車両群への荷物割り当て
- 04軸荷重と重量配分の確認
- 05荷物の固定と安全性の考慮
活用シーン
- 01複数拠点で荷下ろししやすい積載計画
- 02複数車両への注文割り当て
- 03積載量制限に対する荷重の確認
- 04車両積載レポートの作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill vehicle-loading-optimizationインストール後、Claude Code で/vehicle-loading-optimizationを入力して読み込まれたか確認します。
詳細
車両積載の最適化
車両積載の最適化と物流の専門家として、トラック、バン、配送車両への積載を効率化し、積載率を最大化しながら、安全性を確保し、重量制限を守り、複数の配送先への配送順序に対応できるよう支援します。
初期評価
車両の積載を最適化する前に、以下を把握してください。
-
車両仕様
- 車両の種類は何ですか?(箱型トラック、バン、セミトレーラー、平ボディ)
- 荷室の寸法:長さ x 幅 x 高さ
- 積載重量(GVWR - 車両重量)
- 車軸の重量制限(前後の配分)
- ドアの構成(後部、側面、巻き上げ式)?
-
貨物の特性
- パレット積み、箱入り、またはバラ積みですか?
- パレット/荷物の寸法と重量
- 積み込む品目/注文はいくつですか?
- 積み重ね可能か、壊れやすいか
- 特別な取り扱い要件はありますか?
-
配送要件
- 配送先は1か所ですか、それとも複数の配送先を回りますか?
- 配送順序(LIFO/FIFO)?
- 荷降ろし時のアクセス方法(後部のみ、側面ドア)?
- 配送先での時間指定はありますか?
- 顧客固有の要件はありますか?
-
積載の制約
- 重量配分の要件
- 道路規則への適合に関する車軸重量制限
- 固定要件(ストラップ、ネット、バー)
- 温度管理ゾーンはありますか?
- 危険物の分離規則はありますか?
-
最適化の目標
- 車両の積載率を最大化しますか?
- 車両台数を最小限にしますか?
- 荷降ろし順序を簡単にしますか?
- 重量配分のバランスを取りますか?
- 積み込み/荷降ろし時間を最小限にしますか?
車両積載のフレームワーク
一般的な車両の種類
カーゴバン
- 代表例:Ford Transit、Mercedes Sprinter
- 荷室:長さ10~14ft x 幅6ft x 高さ6ft
- 積載量:3,000~4,500 lbs
- 用途:ラストマイル配送、サービス訪問
箱型トラック(10~16ft)
- 代表例:14ft箱型トラック
- 荷室:長さ14ft x 幅7ft x 高さ7ft
- 積載量:3,000~5,000 lbs
- 用途:地域配送、引っ越し
箱型トラック(20~26ft)
- 代表例:26ft箱型トラック
- 荷室:長さ26ft x 幅8ft x 高さ8ft
- 積載量:10,000~15,000 lbs
- 用途:地域間配送、貨物輸送
セミトレーラー(ドライバン)
- 代表例:53ftトレーラー
- 荷室:長さ53ft x 幅8.5ft x 高さ9ft
- 積載量:40,000~45,000 lbs
- 用途:長距離輸送、FTL 貨物
積載の基本原則
1. 車軸の重量配分
- 前軸:最大12,000~13,000 lbs(州によって異なります)
- 後軸:最大34,000 lbs
- 車両総重量定格(GVWR):車両によって異なります
- 目標:前部40%、後部60%(一般的な目安)
2. 複数配送先への積載順序
- 最後の配送先の荷物を最初に積み込みます(LIFO)
- 各配送先の荷物をドアから取り出せるようにします
- 配送順序に応じてゾーン別に積み込みます
- 各配送先での荷物の移動を最小限にします
3. 重量配分
- 重い荷物は下部の中央に配置します
- 左右に均等に重量を分散します
- 重量が一か所に集中するのを避けます
- 前後のバランスを取ります
4. 荷物の固定
- 輸送中に荷物がずれないようにします
- ロードバー、ストラップ、またはエアバッグを使用します
- DOT の貨物固定規則を遵守します
- 壊れやすい荷物を保護します
数学的定式化
車両積載問題
決定変数:
- x_i, y_i, z_i = 車両内での品目 i の位置
- v_i = 品目 i の割り当て先車両
- s_i = 品目 i の配送先
- used_j = 車両 j を使用する場合は1
目的関数:
-
車両台数の最小化: 最小化 Σ used_j
-
積載率の最大化: 最大化 (Σ volume_loaded) / (Σ vehicle_capacity)
-
荷役の最小化: 各配送先での荷降ろしの移動回数を最小化
制約条件:
- 車両の積載容量(容積と重量)
- 車軸の重量制限
- 積載順序(複数配送先へのアクセス性)
- 重量配分のバランス
- 固定および安全要件
- 品目の適合性
アルゴリズムと解法
1か所への配送の車両積載
class SingleStopVehicleLoader:
"""
Optimize loading for single-destination delivery
Uses 3D bin packing with vehicle-specific constraints
"""
def __init__(self, vehicle_length, vehicle_width, vehicle_height,
weight_capacity, front_axle_limit=13000, rear_axle_limit=34000):
"""
Initialize vehicle loader
Parameters:
- vehicle_length, vehicle_width, vehicle_height: cargo area dimensions (inches)
- weight_capacity: max payload weight (lbs)
- front_axle_limit, rear_axle_limit: axle weight limits (lbs)
"""
self.vehicle_dims = (vehicle_length, vehicle_width, vehicle_height)
self.weight_capacity = weight_capacity
self.front_axle_limit = front_axle_limit
self.rear_axle_limit = rear_axle_limit
self.items = []
self.solution = None
def add_item(self, length, width, height, weight, item_id=None):
"""Add item to load"""
if item_id is None:
item_id = f"Item_{len(self.items)}"
self.items.append({
'id': item_id,
'dims': (length, width, height),
'weight': weight
})
def optimize_loading(self, algorithm='weight_balanced'):
"""
Optimize vehicle loading
Algorithms:
- 'weight_balanced': Prioritize weight distribution
- 'space_efficient': Maximize space utilization
- 'easy_unload': Place items for easy access
"""
L, W, H = self.vehicle_dims
if algorithm == 'weight_balanced':
# Sort by weight (heaviest first)
sorted_items = sorted(self.items,
key=lambda x: x['weight'],
reverse=True)
elif algorithm == 'space_efficient':
# Sort by volume
sorted_items = sorted(self.items,
key=lambda x: x['dims'][0] * x['dims'][1] * x['dims'][2],
reverse=True)
else:
sorted_items = self.items
loaded_items = []
current_weight = 0
# Simple layer-based loading
current_y = 0
current_z = 0
current_x = 0
for item in sorted_items:
l, w, h = item['dims']
weight = item['weight']
# Check weight
if current_weight + weight > self.weight_capacity:
continue # Skip item if too heavy
# Try to place item
if current_x + l <= L:
# Place in current row
loaded_items.append({
'item': item,
'position': (current_x, current_y, current_z),
'dims': (l, w, h)
})
current_x += l
current_weight += weight
elif current_y + w <= W:
# Start new row
current_x = 0
current_y += w
loaded_items.append({
'item': item,
'position': (current_x, current_y, current_z),
'dims': (l, w, h)
})
current_x += l
current_weight += weight
elif current_z + h <= H:
# Start new layer
current_x = 0
current_y = 0
current_z += h
loaded_items.append({
'item': item,
'position': (current_x, current_y, current_z),
'dims': (l, w, h)
})
current_x += l
current_weight += weight
# Check axle weights
axle_check = self.check_axle_weights(loaded_items)
self.solution = {
'loaded_items': loaded_items,
'total_weight': current_weight,
'items_loaded': len(loaded_items),
'items_not_loaded': len(self.items) - len(loaded_items),
'utilization': self.calculate_utilization(loaded_items),
'axle_weights': axle_check
}
return self.solution
def check_axle_weights(self, loaded_items):
"""
Calculate axle weight distribution
Assumes:
- Front axle at 0 (front of vehicle)
- Rear axle at 60% of vehicle length
"""
L = self.vehicle_dims[0]
rear_axle_position = L * 0.6
front_axle_weight = 0
rear_axle_weight = 0
for item_data in loaded_items:
pos = item_data['position']
dims = item_data['dims']
weight = item_data['item']['weight']
# Calculate center of mass
com_x = pos[0] + dims[0] / 2
# Distance from axles
distance_from_rear = rear_axle_position - com_x
if distance_from_rear > 0:
# Weight forward of rear axle - distributes to both
front_ratio = distance_from_rear / rear_axle_position
front_axle_weight += weight * front_ratio
rear_axle_weight += weight * (1 - front_ratio)
else:
# Weight behind rear axle - all on rear
rear_axle_weight += weight
return {
'front_axle': front_axle_weight,
'rear_axle': rear_axle_weight,
'front_limit': self.front_axle_limit,
'rear_limit': self.rear_axle_limit,
'front_ok': front_axle_weight <= self.front_axle_limit,
'rear_ok': rear_axle_weight <= self.rear_axle_limit,
'balanced': abs(front_axle_weight - rear_axle_weight) / (front_axle_weight + rear_axle_weight) < 0.3
}
def calculate_utilization(self, loaded_items):
"""Calculate volume utilization"""
L, W, H = self.vehicle_dims
vehicle_volume = L * W * H
loaded_volume = sum(
item_data['dims'][0] * item_data['dims'][1] * item_data['dims'][2]
for item_data in loaded_items
)
return (loaded_volume / vehicle_volume * 100) if vehicle_volume > 0 else 0
def print_solution(self):
"""Print loading solution"""
if not self.solution:
print("No solution available")
return
print("=" * 70)
print("VEHICLE LOADING SOLUTION")
print("=" * 70)
print(f"Vehicle: {self.vehicle_dims[0]}L x {self.vehicle_dims[1]}W x {self.vehicle_dims[2]}H in")
print(f"Weight Capacity: {self.weight_capacity:,} lbs")
print()
print(f"Items Loaded: {self.solution['items_loaded']} / {len(self.items)}")
print(f"Total Weight: {self.solution['total_weight']:,} lbs")
print(f"Utilization: {self.solution['utilization']:.1f}%")
print()
print("Axle Weight Distribution:")
axle = self.solution['axle_weights']
print(f" Front Axle: {axle['front_axle']:,.0f} lbs "
f"({'OK' if axle['front_ok'] else 'OVER LIMIT'})")
print(f" Rear Axle: {axle['rear_axle']:,.0f} lbs "
f"({'OK' if axle['rear_ok'] else 'OVER LIMIT'})")
print(f" Balanced: {'Yes' if axle['balanced'] else 'No - adjust load'}")
# Example usage
if __name__ == "__main__":
# 26ft box truck
loader = SingleStopVehicleLoader(
vehicle_length=312, # 26ft in inches
vehicle_width=96, # 8ft
vehicle_height=96, # 8ft
weight_capacity=10000 # lbs
)
# Add items
loader.add_item(48, 40, 60, 800, "Pallet_1")
loader.add_item(48, 40, 50, 700, "Pallet_2")
loader.add_item(48, 40, 55, 750, "Pallet_3")
loader.add_item(36, 30, 40, 500, "Box_1")
loader.add_item(36, 30, 40, 500, "Box_2")
# Optimize
solution = loader.optimize_loading(algorithm='weight_balanced')
loader.print_solution()
複数配送先への車両積載
class MultiStopVehicleLoader:
"""
Optimize vehicle loading for multi-stop delivery routes
Ensures items are accessible in delivery sequence
"""
def __init__(self, vehicle_dims, weight_capacity):
self.vehicle_dims = vehicle_dims
self.weight_capacity = weight_capacity
self.stops = [] # List of delivery stops
self.solution = None
def add_stop(self, stop_id, items, delivery_sequence):
"""
Add delivery stop with items
Parameters:
- stop_id: stop identifier
- items: list of item dicts with 'dims' and 'weight'
- delivery_sequence: order in route (1, 2, 3, ...)
"""
self.stops.append({
'id': stop_id,
'items': items,
'sequence': delivery_sequence
})
def optimize_loading(self):
"""
Optimize loading for multi-stop delivery
Strategy:
- Zone vehicle by delivery sequence
- Last stop loaded first (at door)
- Earlier stops loaded deeper in vehicle
"""
# Sort stops by reverse delivery sequence
sorted_stops = sorted(self.stops,
key=lambda s: s['sequence'],
reverse=True)
L, W, H = self.vehicle_dims
# Calculate zones
num_stops = len(sorted_stops)
zone_length = L / num_stops
zones = []
current_weight = 0
for idx, stop in enumerate(sorted_stops):
zone_start = idx * zone_length
zone_end = (idx + 1) * zone_length
zone_items = []
# Load items for this stop in this zone
for item in stop['items']:
if current_weight + item['weight'] <= self.weight_capacity:
# Simple placement (could be more sophisticated)
zone_items.append({
'item': item,
'stop_id': stop['id'],
'zone': (zone_start, zone_end)
})
current_weight += item['weight']
zones.append({
'stop': stop,
'zone': (zone_start, zone_end),
'items': zone_items
})
self.solution = {
'zones': zones,
'total_weight': current_weight,
'stops_loaded': len(zones),
'total_items': sum(len(z['items']) for z in zones)
}
return self.solution
def print_solution(self):
"""Print multi-stop loading plan"""
if not self.solution:
print("No solution available")
return
print("=" * 70)
print("MULTI-STOP VEHICLE LOADING PLAN")
print("=" * 70)
print(f"Total Weight: {self.solution['total_weight']:,} lbs")
print(f"Stops: {self.solution['stops_loaded']}")
print(f"Total Items: {self.solution['total_items']}")
print()
for zone_data in self.solution['zones']:
stop = zone_data['stop']
zone = zone_data['zone']
print(f"Stop {stop['sequence']}: {stop['id']}")
print(f" Zone: {zone[0]:.0f}-{zone[1]:.0f} inches from front")
print(f" Items: {len(zone_data['items'])}")
print()
車両群の積載最適化
def optimize_fleet_loading(orders, vehicles):
"""
Optimize loading across multiple vehicles
Assigns orders to vehicles to minimize:
- Number of vehicles used
- Total distance traveled
- Loading/unloading complexity
Parameters:
- orders: list of order dicts with items, destination, priority
- vehicles: list of available vehicle specs
Returns: assignment of orders to vehicles
"""
from pulp import *
n_orders = len(orders)
n_vehicles = len(vehicles)
# Create problem
prob = LpProblem("Fleet_Loading", LpMinimize)
# Decision variables
# x[i,j] = 1 if order i assigned to vehicle j
x = LpVariable.dicts("assign",
[(i, j) for i in range(n_orders) for j in range(n_vehicles)],
cat='Binary')
# y[j] = 1 if vehicle j is used
y = LpVariable.dicts("use_vehicle", range(n_vehicles), cat='Binary')
# Objective: Minimize vehicles used + routing cost
prob += lpSum([y[j] * vehicles[j]['cost']
for j in range(n_vehicles)]), "Total_Cost"
# Constraints
# 1. Each order assigned to exactly one vehicle
for i in range(n_orders):
prob += lpSum([x[i,j] for j in range(n_vehicles)]) == 1, f"Order_{i}"
# 2. Vehicle capacity (weight)
for j in range(n_vehicles):
prob += (lpSum([orders[i]['weight'] * x[i,j] for i in range(n_orders)]) <=
vehicles[j]['weight_capacity']), f"Weight_{j}"
# 3. Vehicle capacity (volume)
for j in range(n_vehicles):
prob += (lpSum([orders[i]['volume'] * x[i,j] for i in range(n_orders)]) <=
vehicles[j]['volume_capacity']), f"Volume_{j}"
# 4. Vehicle used if orders assigned
for j in range(n_vehicles):
for i in range(n_orders):
prob += x[i,j] <= y[j], f"VehicleUsed_{i}_{j}"
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
# Extract solution
assignments = [[] for _ in range(n_vehicles)]
for i in range(n_orders):
for j in range(n_vehicles):
if x[i,j].varValue and x[i,j].varValue > 0.5:
assignments[j].append(i)
return {
'status': LpStatus[prob.status],
'vehicles_used': sum(1 for a in assignments if a),
'assignments': assignments
}
よくある課題と解決策
課題:車軸重量の違反
問題:
- 前軸または後軸が法定重量制限を超過する
- 路上検査に不合格となる
- 安全上の問題が発生する
解決策:
- 最適化で車軸重量の計算を使用する
- 重い荷物を車軸の間に配置する
- 前方または後方に荷物を積みすぎない
- ロードバーを使って重量を移動させる
- 荷物の追加または取り出しを検討する
課題:複数箇所での荷物の取り出しやすさ
問題:
- 早い順番の配送先向けの荷物が奥に埋もれている
- 各配送先で荷物を降ろしたり積み直したりする必要がある
- 時間が無駄になり、荷物を破損するリスクがある
解決策:
- 配送順に合わせてゾーンごとに積載する
- 最後の配送先向けの荷物をドア側に、最初の配送先向けの荷物を前方に置く
- 各ゾーン内で垂直に積み重ねる
- 取り出しやすいよう、軽くて小さい荷物を上に載せる
- サイドドアからアクセスできる車両を検討する
課題:サイズが混在する荷物
問題:
- パレットとバラ積みの箱がある
- 高さが異なるため、スペースが無駄になる
- 固定が難しい
解決策:
- 似た荷物をまとめる
- パレットをバラ積みの荷物の土台として使う
- 空いた隙間を柔らかい品物や緩衝材で埋める
- 小さい荷物を大きな荷物の上に積み重ねる
- 荷物用ネットまたはストラップを使う
出力形式
車両積載レポート
車両:26ft ボックストラック
- 寸法:26'L x 8'W x 8'H
- 積載重量:12,000 lbs
- ルート:5か所
積載計画:
ゾーン1(0-5ft)- 停車地点5(最後):
- パレット3個:P045、P046、P047
- 重量:2,100 lbs
- リアドアから取り出し可能
ゾーン2(5-10ft)- 停車地点4:
- パレット2個+箱8個
- 重量:1,850 lbs
ゾーン3(10-15ft)- 停車地点3:
- パレット4個
- 重量:2,800 lbs
ゾーン4(15-20ft)- 停車地点2:
- パレット3個+箱12個
- 重量:2,400 lbs
ゾーン5(20-26ft)- 停車地点1(最初):
- パレット2個
- 重量:1,600 lbs
概要:
- 総重量:10,750 lbs(積載容量の90%)
- 容積使用率:82%
- 車軸重量:前軸4,200 lbs、後軸6,550 lbs ✓
- 荷物の固定:はい(ロードバー4本、ストレッチラップ)
確認する質問
- 車両の種類は何ですか?(バン、ボックストラック、セミトレーラーなど)
- 貨物の寸法と積載重量はどれくらいですか?
- 配送先は1か所ですか、それとも複数ですか?
- 複数箇所への配送の場合、配送順はどうなっていますか?
- 車軸重量に関する懸念や制限はありますか?
- 荷物はパレット積みですか、それともバラ積みですか?
- 特別な取り扱い要件はありますか?
関連スキル
- route-optimization:配送ルートの計画
- 3d-bin-packing:一般的な3D積載アルゴリズム
- pallet-loading:パレット単位の最適化
- container-loading-optimization:コンテナへの積載
- fleet-management:車両フリートの最適化
- last-mile-delivery:最終配送の最適化
コメント
Markdown 対応