mcpField
V
vehicle-loading-optimization

vehicle-loading-optimization

github.com/kishorkukrejaその他8454 回インストール3 週間前に更新
リポジトリ

vehicle-loading-optimization について

トラック、バン、配送車両の積載計画を支援する物流スキルです。車両や貨物の制約に加え、複数拠点の荷下ろし順、重量配分、積載率を考慮します。

主な機能

  1. 01車両と貨物の制約評価
  2. 02単一・複数拠点の積載計画
  3. 03車両群への荷物割り当て
  4. 04軸荷重と重量配分の確認
  5. 05荷物の固定と安全性の考慮

活用シーン

  1. 01複数拠点で荷下ろししやすい積載計画
  2. 02複数車両への注文割り当て
  3. 03積載量制限に対する荷重の確認
  4. 04車両積載レポートの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill vehicle-loading-optimization

インストール後、Claude Code で/vehicle-loading-optimizationを入力して読み込まれたか確認します。

詳細

車両積載の最適化

車両積載の最適化と物流の専門家として、トラック、バン、配送車両への積載を効率化し、積載率を最大化しながら、安全性を確保し、重量制限を守り、複数の配送先への配送順序に対応できるよう支援します。

初期評価

車両の積載を最適化する前に、以下を把握してください。

  1. 車両仕様

    • 車両の種類は何ですか?(箱型トラック、バン、セミトレーラー、平ボディ)
    • 荷室の寸法:長さ x 幅 x 高さ
    • 積載重量(GVWR - 車両重量)
    • 車軸の重量制限(前後の配分)
    • ドアの構成(後部、側面、巻き上げ式)?
  2. 貨物の特性

    • パレット積み、箱入り、またはバラ積みですか?
    • パレット/荷物の寸法と重量
    • 積み込む品目/注文はいくつですか?
    • 積み重ね可能か、壊れやすいか
    • 特別な取り扱い要件はありますか?
  3. 配送要件

    • 配送先は1か所ですか、それとも複数の配送先を回りますか?
    • 配送順序(LIFO/FIFO)?
    • 荷降ろし時のアクセス方法(後部のみ、側面ドア)?
    • 配送先での時間指定はありますか?
    • 顧客固有の要件はありますか?
  4. 積載の制約

    • 重量配分の要件
    • 道路規則への適合に関する車軸重量制限
    • 固定要件(ストラップ、ネット、バー)
    • 温度管理ゾーンはありますか?
    • 危険物の分離規則はありますか?
  5. 最適化の目標

    • 車両の積載率を最大化しますか?
    • 車両台数を最小限にしますか?
    • 荷降ろし順序を簡単にしますか?
    • 重量配分のバランスを取りますか?
    • 積み込み/荷降ろし時間を最小限にしますか?

車両積載のフレームワーク

一般的な車両の種類

カーゴバン

  • 代表例: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

目的関数:

  1. 車両台数の最小化: 最小化 Σ used_j

  2. 積載率の最大化: 最大化 (Σ volume_loaded) / (Σ vehicle_capacity)

  3. 荷役の最小化: 各配送先での荷降ろしの移動回数を最小化

制約条件:

  1. 車両の積載容量(容積と重量)
  2. 車軸の重量制限
  3. 積載順序(複数配送先へのアクセス性)
  4. 重量配分のバランス
  5. 固定および安全要件
  6. 品目の適合性

アルゴリズムと解法

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. 車両の種類は何ですか?(バン、ボックストラック、セミトレーラーなど)
  2. 貨物の寸法と積載重量はどれくらいですか?
  3. 配送先は1か所ですか、それとも複数ですか?
  4. 複数箇所への配送の場合、配送順はどうなっていますか?
  5. 車軸重量に関する懸念や制限はありますか?
  6. 荷物はパレット積みですか、それともバラ積みですか?
  7. 特別な取り扱い要件はありますか?

関連スキル

  • route-optimization:配送ルートの計画
  • 3d-bin-packing:一般的な3D積載アルゴリズム
  • pallet-loading:パレット単位の最適化
  • container-loading-optimization:コンテナへの積載
  • fleet-management:車両フリートの最適化
  • last-mile-delivery:最終配送の最適化

コメント