mcpField
L
load-building-optimization

load-building-optimization

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

load-building-optimization について

輸送・物流向けの積載最適化スキルです。空間と重量の利用率を高めつつ、安定性、安全性、配送順を考慮したトラック積載計画を扱います。積載戦略、制約モデル、ヒューリスティックを紹介します。

主な機能

  1. 01空間・重量利用率の最適化
  2. 023D ビンパッキングとヒューリスティック
  3. 03積荷と車軸の重量配分
  4. 04配送順とゾーン積載
  5. 05貨物の不適合制約への対応

活用シーン

  1. 01複数地点配送の積載順序の計画
  2. 02トラック容量に合わせた貨物の混載
  3. 03重量配分と貨物の適合性の確認

インストール

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

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

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

詳細

積載最適化

輸送とロジスティクスにおける積載計画およびトラック積載の最適化に精通しています。使用効率を最大化し、コストを最小限に抑え、安定性と安全性を確保し、出荷効率全体を向上させる最適なトラック積載を支援します。

初期評価

積載を最適化する前に、次の点を把握します。

  1. 車両の特性

    • トレーラーの種類(ドライバン、冷蔵車、フラットベッド)?
    • トレーラーの寸法(長さ × 幅 × 高さ)?
    • 重量容量(総重量制限)?
    • 車軸重量の制限?
    • 床の耐荷重(psi)?
    • 利用可能なトラックの台数?
  2. 出荷プロファイル

    • 1日の出荷量(パレット、ケース)?
    • 梱包の寸法と重量?
    • 積み重ねの可否と壊れやすさ?
    • 温度要件(常温、冷蔵、冷凍)?
    • 配送順序とルート?
    • 混載可能/不可の製品?
  3. ビジネス要件

    • トラックの台数を最小限に抑える?
    • 容積使用率を最大化する?
    • 配送時間枠を守る?
    • 運賃を削減する?
    • 製品の安全性を確保する(損傷を防ぐ)?
    • ルート効率(立ち寄り順)?
  4. 現状

    • 現在の容積使用率(%)?
    • 平均重量使用率(%)?
    • 積載計画の方法(手作業、ソフトウェア)?
    • 運賃とその傾向?
    • 損傷率?

積載計画のフレームワーク

積載計画の目標

主な目標:

  1. 空間使用率の最大化:トレーラーの容積を効率よく使う
  2. 重量使用率の最大化:重量容量を最大限に使う
  3. トラックの台数の最小化:混載してコストを削減する
  4. 荷重のバランス調整:適切に重量を配分し、荷崩れを防ぐ
  5. 安全性の確保:車軸の過積載を防ぎ、安定して積み重ねる
  6. ルートの最適化:積載順序を配送順に合わせる

主な指標:

  • 容積使用率(%)(実容積 / トレーラー容量)
  • 重量使用率(%)(実重量 / 容量)
  • ポンドまたは立方フィートあたりのコスト
  • 必要なトラックの台数
  • 積載時間(荷物の積み付けと確認にかかる時間)
  • 損傷率(出荷1,000件あたりのクレーム件数)
積載の戦略

1. 床から天井までの積載

  • トレーラーの高さを最大限に使う
  • 垂直方向の無駄な空間を最小限に抑える
  • メリット:容積使用率を最大化できる
  • デメリット:下段の荷物が損傷するリスクがある
  • 用途:頑丈な製品、均一なパレット

2. 層ごとの積載

  • 水平方向に層を作る
  • 次の層に進む前に、現在の層を完成させる
  • メリット:安定し、確認しやすい
  • デメリット:垂直方向の空間が無駄になる場合がある
  • 用途:壊れやすい品目、混載パレット

3. ゾーン積載(配送順)

  • 配送先ごとに出荷品をまとめる
  • 最後に降ろす荷物から先に積む(LIFO)
  • メリット:配送時の荷扱いを最小限に抑えられる
  • デメリット:容積使用率が下がる場合がある
  • 用途:複数箇所に立ち寄るルート、LTL

4. 重量物を前方に積載

  • 重い品目を前方(駆動軸の上)に置く
  • 軽い品目を後方に置く
  • メリット:重量配分とハンドリングが改善する
  • デメリット:容積を最大限に使えない場合がある
  • 用途:重量物やかさばる荷物、長距離輸送

5. テトリス/3Dビンパッキング

  • テトリスのように最適な配置を行う
  • アルゴリズムを使って積載密度を最大化する
  • メリット:容積使用率が最も高くなる
  • デメリット:複雑で、実際の作業では実行が難しい場合がある
  • 用途:不定形の荷物、高価な貨物

数理モデル

3Dビンパッキング問題

決定変数:

  • x[i,j] = 品目 i をトラック j に積載する場合は 1、そうでない場合は 0
  • pos[i] = トラック内における品目 i の位置 (x, y, z)
  • orient[i] = 品目 i の向き(回転)

パラメーター:

  • L[i], W[i], H[i] = 品目 i の長さ、幅、高さ
  • weight[i] = 品目 i の重量
  • TL, TW, TH = トラックの長さ、幅、高さ
  • T_weight = トラックの重量容量
  • n = 品目数
  • m = 利用可能なトラック数

目的関数:

Minimize: Number of trucks used

Subject to:
  All items assigned
  No overlapping items
  Weight limits respected
  Stability constraints satisfied

制約条件:

# 1. Each item assigned to exactly one truck
for i in items:
    Σ x[i,j] = 1  for all j in trucks

# 2. Items within truck boundaries
for i in items:
    for j in trucks:
        if x[i,j] = 1:
            pos[i].x + L[i] <= TL
            pos[i].y + W[i] <= TW
            pos[i].z + H[i] <= TH

# 3. No overlapping (simplified)
for i, k in items (i != k):
    for j in trucks:
        if x[i,j] = 1 and x[k,j] = 1:
            # One of these must be true:
            pos[i].x + L[i] <= pos[k].x  OR
            pos[k].x + L[k] <= pos[i].x  OR
            pos[i].y + W[i] <= pos[k].y  OR
            pos[k].y + W[k] <= pos[i].y  OR
            pos[i].z + H[i] <= pos[k].z  OR
            pos[k].z + H[k] <= pos[i].z

# 4. Weight capacity
for j in trucks:
    Σ (weight[i] × x[i,j]) <= T_weight  for all i

# 5. Support constraint (item must rest on floor or another item)
for i in items:
    if pos[i].z > 0:
        must have sufficient support area underneath

# 6. Stackability
for i, k in items:
    if item i on top of item k:
        stackable[k] must be True
        weight_on_top[k] <= max_stack_weight[k]

これは NP 困難な問題であるため、実用的な解法ではヒューリスティックを使用します。


積載アルゴリズム

First-Fit Decreasing(FFD)ヒューリスティック
import numpy as np
import pandas as pd

def first_fit_decreasing_load_building(shipments, truck_capacity):
    """
    First-Fit Decreasing heuristic for load building

    Algorithm:
    1. Sort shipments by volume (largest first)
    2. For each shipment, try to fit in first truck with space
    3. If doesn't fit, create new truck

    Parameters:
    -----------
    shipments : DataFrame
        Columns: shipment_id, weight, volume, length, width, height
    truck_capacity : dict
        {'weight': max_weight, 'volume': max_volume}

    Returns:
    --------
    Load plan with truck assignments
    """

    # Sort by volume (largest first)
    shipments_sorted = shipments.sort_values('volume', ascending=False).copy()

    trucks = []
    current_truck = {
        'truck_id': 1,
        'shipments': [],
        'total_weight': 0,
        'total_volume': 0
    }

    for idx, shipment in shipments_sorted.iterrows():
        ship_weight = shipment['weight']
        ship_volume = shipment['volume']

        # Try to fit in current truck
        if (current_truck['total_weight'] + ship_weight <= truck_capacity['weight'] and
            current_truck['total_volume'] + ship_volume <= truck_capacity['volume']):

            # Fits in current truck
            current_truck['shipments'].append(shipment['shipment_id'])
            current_truck['total_weight'] += ship_weight
            current_truck['total_volume'] += ship_volume

        else:
            # Doesn't fit, start new truck
            trucks.append(current_truck)

            current_truck = {
                'truck_id': len(trucks) + 1,
                'shipments': [shipment['shipment_id']],
                'total_weight': ship_weight,
                'total_volume': ship_volume
            }

    # Add last truck
    if current_truck['shipments']:
        trucks.append(current_truck)

    # Calculate utilization
    for truck in trucks:
        truck['weight_util'] = (truck['total_weight'] / truck_capacity['weight']) * 100
        truck['volume_util'] = (truck['total_volume'] / truck_capacity['volume']) * 100

    return pd.DataFrame(trucks)


# Example usage
shipments = pd.DataFrame({
    'shipment_id': [f'S{i:03d}' for i in range(1, 51)],
    'weight': np.random.uniform(500, 5000, 50),  # lbs
    'volume': np.random.uniform(50, 500, 50),    # cubic feet
    'length': np.random.uniform(3, 8, 50),       # feet
    'width': np.random.uniform(3, 4, 50),
    'height': np.random.uniform(3, 6, 50)
})

truck_capacity = {
    'weight': 45000,  # lbs (typical 53' dry van)
    'volume': 4000    # cubic feet
}

load_plan = first_fit_decreasing_load_building(shipments, truck_capacity)

print("Load Building Results (First-Fit Decreasing):")
print(f"Total Trucks: {len(load_plan)}")
print(f"\nTruck Summary:")
print(load_plan[['truck_id', 'total_weight', 'total_volume',
                 'weight_util', 'volume_util']])

print(f"\nAverage Utilization:")
print(f"  Weight: {load_plan['weight_util'].mean():.1f}%")
print(f"  Volume: {load_plan['volume_util'].mean():.1f}%")
配送順を考慮した Best-Fit Decreasing
def best_fit_with_route_sequence(shipments, truck_capacity, route_sequence):
    """
    Best-fit load building considering delivery sequence

    Ensure items delivered first are loaded last (LIFO)

    Parameters:
    -----------
    shipments : DataFrame
        With route_stop column indicating delivery order
    truck_capacity : dict
    route_sequence : list
        Ordered list of stops

    Returns:
    --------
    Load plan with sequence constraints
    """

    # Group shipments by route stop
    shipments_by_stop = shipments.groupby('route_stop')

    trucks = []

    # Reverse route sequence (load last stop first)
    for stop in reversed(route_sequence):
        if stop not in shipments_by_stop.groups:
            continue

        stop_shipments = shipments_by_stop.get_group(stop)
        stop_shipments_sorted = stop_shipments.sort_values('volume', ascending=False)

        # Try to fit stop's shipments into existing trucks
        for idx, shipment in stop_shipments_sorted.iterrows():
            ship_weight = shipment['weight']
            ship_volume = shipment['volume']

            placed = False

            # Try existing trucks (prefer truck with most remaining space)
            for truck in sorted(trucks,
                              key=lambda t: (truck_capacity['volume'] - t['total_volume']),
                              reverse=True):

                if (truck['total_weight'] + ship_weight <= truck_capacity['weight'] and
                    truck['total_volume'] + ship_volume <= truck_capacity['volume']):

                    # Fits in this truck
                    truck['shipments'].append(shipment['shipment_id'])
                    truck['stops'].add(stop)
                    truck['total_weight'] += ship_weight
                    truck['total_volume'] += ship_volume
                    placed = True
                    break

            if not placed:
                # Create new truck
                trucks.append({
                    'truck_id': len(trucks) + 1,
                    'shipments': [shipment['shipment_id']],
                    'stops': {stop},
                    'total_weight': ship_weight,
                    'total_volume': ship_volume
                })

    # Calculate utilization
    for truck in trucks:
        truck['weight_util'] = (truck['total_weight'] / truck_capacity['weight']) * 100
        truck['volume_util'] = (truck['total_volume'] / truck_capacity['volume']) * 100
        truck['num_stops'] = len(truck['stops'])
        truck['stops'] = list(truck['stops'])

    return pd.DataFrame(trucks)


# Example with route stops
shipments_routed = shipments.copy()
shipments_routed['route_stop'] = np.random.choice(['Stop_A', 'Stop_B', 'Stop_C', 'Stop_D'], 50)

route_sequence = ['Stop_A', 'Stop_B', 'Stop_C', 'Stop_D']

load_plan_routed = best_fit_with_route_sequence(
    shipments_routed, truck_capacity, route_sequence
)

print("\nLoad Building with Route Sequence:")
print(load_plan_routed[['truck_id', 'num_stops', 'total_weight', 'weight_util']])
3Dビンパッキングのヒューリスティック
class Container3D:
    """Represent a truck/container for 3D packing"""

    def __init__(self, length, width, height, weight_limit):
        self.length = length
        self.width = width
        self.height = height
        self.weight_limit = weight_limit
        self.items = []
        self.total_weight = 0
        self.spaces = [(0, 0, 0, length, width, height)]  # Available spaces

    def can_fit(self, item_length, item_width, item_height, item_weight):
        """Check if item can fit in any available space"""

        if self.total_weight + item_weight > self.weight_limit:
            return False

        for space in self.spaces:
            sx, sy, sz, sl, sw, sh = space

            # Try all 6 orientations
            orientations = [
                (item_length, item_width, item_height),
                (item_length, item_height, item_width),
                (item_width, item_length, item_height),
                (item_width, item_height, item_length),
                (item_height, item_length, item_width),
                (item_height, item_width, item_length)
            ]

            for l, w, h in orientations:
                if l <= sl and w <= sw and h <= sh:
                    return True, space, (l, w, h)

        return False, None, None

    def place_item(self, item_id, item_length, item_width, item_height, item_weight):
        """Place item in best available space"""

        can_fit_result = self.can_fit(item_length, item_width, item_height, item_weight)

        if can_fit_result[0]:
            space, orientation = can_fit_result[1], can_fit_result[2]
            sx, sy, sz, sl, sw, sh = space
            l, w, h = orientation

            # Place item at corner of space
            self.items.append({
                'item_id': item_id,
                'position': (sx, sy, sz),
                'dimensions': (l, w, h),
                'weight': item_weight
            })

            self.total_weight += item_weight

            # Remove used space and create new available spaces
            self.spaces.remove(space)

            # Create new spaces (simplified - guillotine cuts)
            # Right space
            if sl - l > 0:
                self.spaces.append((sx + l, sy, sz, sl - l, sw, sh))

            # Front space
            if sw - w > 0:
                self.spaces.append((sx, sy + w, sz, l, sw - w, sh))

            # Top space
            if sh - h > 0:
                self.spaces.append((sx, sy, sz + h, l, w, sh - h))

            # Sort spaces by position (bottom-left-front first)
            self.spaces.sort(key=lambda s: (s[2], s[1], s[0]))

            return True

        return False


def pack_3d_containers(items, container_dims, container_weight_limit):
    """
    Pack items into containers using 3D bin packing

    Parameters:
    -----------
    items : DataFrame
        Columns: item_id, length, width, height, weight
    container_dims : tuple
        (length, width, height) of container
    container_weight_limit : float

    Returns:
    --------
    List of packed containers
    """

    # Sort items by volume (largest first)
    items_sorted = items.copy()
    items_sorted['volume'] = (items_sorted['length'] *
                             items_sorted['width'] *
                             items_sorted['height'])
    items_sorted = items_sorted.sort_values('volume', ascending=False)

    containers = []
    current_container = Container3D(*container_dims, container_weight_limit)

    for idx, item in items_sorted.iterrows():
        placed = current_container.place_item(
            item['item_id'],
            item['length'],
            item['width'],
            item['height'],
            item['weight']
        )

        if not placed:
            # Start new container
            containers.append(current_container)
            current_container = Container3D(*container_dims, container_weight_limit)

            # Try again with new container
            placed = current_container.place_item(
                item['item_id'],
                item['length'],
                item['width'],
                item['height'],
                item['weight']
            )

            if not placed:
                print(f"Warning: Could not place item {item['item_id']}")

    # Add last container
    if current_container.items:
        containers.append(current_container)

    return containers


# Example
container_dims = (53, 8.5, 9)  # 53' trailer: length, width, height (feet)
container_weight_limit = 45000  # lbs

# Convert shipments to feet for dimensions
items_3d = shipments.copy()

containers = pack_3d_containers(items_3d, container_dims, container_weight_limit)

print(f"\n3D Bin Packing Results:")
print(f"Total Containers: {len(containers)}")

for i, container in enumerate(containers):
    volume_used = sum(
        item['dimensions'][0] * item['dimensions'][1] * item['dimensions'][2]
        for item in container.items
    )
    total_volume = container.length * container.width * container.height

    print(f"\nContainer {i+1}:")
    print(f"  Items: {len(container.items)}")
    print(f"  Weight: {container.total_weight:,.0f} lbs "
          f"({container.total_weight / container_weight_limit * 100:.1f}%)")
    print(f"  Volume: {volume_used / total_volume * 100:.1f}% utilized")

高度な積載手法

重量配分と車軸荷重
def calculate_axle_weights(items_in_truck, truck_length=53):
    """
    Calculate weight on each axle to ensure compliance

    Truck has:
    - Steer axle (front)
    - Drive axles (middle)
    - Trailer axles (rear)

    Parameters:
    -----------
    items_in_truck : list
        Items with position and weight
    truck_length : float
        Truck length in feet

    Returns:
    --------
    Axle weights and compliance
    """

    # Axle positions (simplified)
    steer_position = 0
    drive_position = 20  # feet from front
    trailer_position = 43  # feet from front

    steer_weight = 0
    drive_weight = 0
    trailer_weight = 0

    # Calculate center of gravity for each item
    for item in items_in_truck:
        position_x = item['position'][0]  # Distance from front
        item_cog_x = position_x + item['dimensions'][0] / 2
        item_weight = item['weight']

        # Distribute weight to axles based on distance
        # Simplified: closest axle gets weight

        dist_to_steer = abs(item_cog_x - steer_position)
        dist_to_drive = abs(item_cog_x - drive_position)
        dist_to_trailer = abs(item_cog_x - trailer_position)

        min_dist = min(dist_to_steer, dist_to_drive, dist_to_trailer)

        if min_dist == dist_to_steer:
            steer_weight += item_weight
        elif min_dist == dist_to_drive:
            drive_weight += item_weight
        else:
            trailer_weight += item_weight

    # Legal limits (example - varies by jurisdiction)
    limits = {
        'steer': 12000,   # lbs
        'drive': 34000,   # lbs (tandem)
        'trailer': 34000  # lbs (tandem)
    }

    compliant = (
        steer_weight <= limits['steer'] and
        drive_weight <= limits['drive'] and
        trailer_weight <= limits['trailer']
    )

    return {
        'steer_weight': steer_weight,
        'drive_weight': drive_weight,
        'trailer_weight': trailer_weight,
        'total_weight': steer_weight + drive_weight + trailer_weight,
        'compliant': compliant,
        'violations': {
            'steer': max(0, steer_weight - limits['steer']),
            'drive': max(0, drive_weight - limits['drive']),
            'trailer': max(0, trailer_weight - limits['trailer'])
        }
    }


# Example
if len(containers) > 0:
    axle_weights = calculate_axle_weights(containers[0].items)

    print("\nAxle Weight Distribution:")
    print(f"  Steer Axle: {axle_weights['steer_weight']:,.0f} lbs")
    print(f"  Drive Axle: {axle_weights['drive_weight']:,.0f} lbs")
    print(f"  Trailer Axle: {axle_weights['trailer_weight']:,.0f} lbs")
    print(f"  Total: {axle_weights['total_weight']:,.0f} lbs")
    print(f"  Compliant: {'Yes' if axle_weights['compliant'] else 'No'}")
混載不可条件を考慮した積載最適化
def load_with_incompatibilities(shipments, truck_capacity, incompatible_pairs):
    """
    Load building with product incompatibility constraints

    Some products cannot be shipped together:
    - Food and chemicals
    - Different temperature requirements
    - Hazmat restrictions

    Parameters:
    -----------
    shipments : DataFrame
    truck_capacity : dict
    incompatible_pairs : list of tuples
        [(product_type_1, product_type_2), ...]

    Returns:
    --------
    Load plan respecting incompatibilities
    """

    from pulp import *

    # Create optimization model
    prob = LpProblem("Load_Building_Incompatibility", LpMinimize)

    # Maximum trucks needed (upper bound)
    max_trucks = len(shipments)
    trucks = range(max_trucks)

    # Decision variables
    # x[i,j] = 1 if shipment i in truck j
    x = LpVariable.dicts("load",
                        [(i, j) for i in shipments.index for j in trucks],
                        cat='Binary')

    # y[j] = 1 if truck j is used
    y = LpVariable.dicts("use_truck", trucks, cat='Binary')

    # Objective: minimize number of trucks
    prob += lpSum([y[j] for j in trucks]), "Minimize_Trucks"

    # Constraints

    # 1. Each shipment in exactly one truck
    for i in shipments.index:
        prob += lpSum([x[i, j] for j in trucks]) == 1, f"Shipment_{i}"

    # 2. Weight capacity
    for j in trucks:
        prob += lpSum([
            shipments.loc[i, 'weight'] * x[i, j]
            for i in shipments.index
        ]) <= truck_capacity['weight'] * y[j], f"Weight_{j}"

    # 3. Volume capacity
    for j in trucks:
        prob += lpSum([
            shipments.loc[i, 'volume'] * x[i, j]
            for i in shipments.index
        ]) <= truck_capacity['volume'] * y[j], f"Volume_{j}"

    # 4. Incompatibility constraints
    for (type1, type2) in incompatible_pairs:
        # Find shipments of each type
        shipments_type1 = shipments[shipments['product_type'] == type1].index
        shipments_type2 = shipments[shipments['product_type'] == type2].index

        # For each truck, can't have both types
        for j in trucks:
            # If any type1 in truck j, no type2 can be in truck j
            for i1 in shipments_type1:
                for i2 in shipments_type2:
                    prob += x[i1, j] + x[i2, j] <= 1, \
                            f"Incompatible_{i1}_{i2}_{j}"

    # 5. Link shipment assignment to truck usage
    for j in trucks:
        for i in shipments.index:
            prob += x[i, j] <= y[j], f"Link_{i}_{j}"

    # Solve
    prob.solve(PULP_CBC_CMD(msg=0))

    # Extract solution
    trucks_used = []
    for j in trucks:
        if y[j].varValue > 0.5:
            truck_shipments = [
                i for i in shipments.index
                if x[i, j].varValue > 0.5
            ]

            if truck_shipments:
                total_weight = shipments.loc[truck_shipments, 'weight'].sum()
                total_volume = shipments.loc[truck_shipments, 'volume'].sum()

                trucks_used.append({
                    'truck_id': j + 1,
                    'shipments': truck_shipments,
                    'num_shipments': len(truck_shipments),
                    'total_weight': total_weight,
                    'total_volume': total_volume,
                    'weight_util': total_weight / truck_capacity['weight'] * 100,
                    'volume_util': total_volume / truck_capacity['volume'] * 100
                })

    return {
        'status': LpStatus[prob.status],
        'trucks': pd.DataFrame(trucks_used),
        'num_trucks': len(trucks_used)
    }


# Example
shipments_typed = shipments.copy()
shipments_typed['product_type'] = np.random.choice(
    ['Food', 'Chemical', 'Electronics', 'Apparel'], 50
)

incompatible_pairs = [
    ('Food', 'Chemical'),
    ('Electronics', 'Chemical')
]

result_incomp = load_with_incompatibilities(
    shipments_typed, truck_capacity, incompatible_pairs
)

print(f"\nLoad Building with Incompatibilities:")
print(f"Status: {result_incomp['status']}")
print(f"Trucks Required: {result_incomp['num_trucks']}")
print("\nTruck Details:")
print(result_incomp['trucks'][['truck_id', 'num_shipments', 'weight_util', 'volume_util']])

ツールとライブラリ

積載計画ソフトウェア

輸送管理システム(TMS):

  • MercuryGate TMS:積載の最適化と計画
  • Oracle Transportation Management:積載計画モジュール
  • Blue Yonder (JDA) TMS:積載最適化
  • E2open TMS:積載の自動最適化

専用の積載計画ソフトウェア:

  • TOPS Load Planning:3D 積載最適化
  • CargoWiz:コンテナとトラックの積載計画
  • CubiScan:寸法測定と積載計画
  • Cargo Optimizer:高度な積載計画
  • LoadMaster:トラックとコンテナへの積載

倉庫管理システム:

  • Manhattan WMS:積載計画の統合
  • SAP EWM:積載計画と最適化
Python ライブラリ
# Optimization
from pulp import *  # MIP modeling
from ortools.linear_solver import pywraplp  # Google OR-Tools

# 3D Packing
from py3dbp import Packer, Bin, Item  # 3D bin packing library

# Geometry and Visualization
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd

よくある課題と解決策

課題:容積と重量のトレードオフ

問題:

  • 容積を満たす前に重量上限に達する(高密度の製品)
  • または、重量に余裕がある状態で容積上限に達する(かさばる軽量品)
  • 「容積上限に達する」か「重量上限に達する」か

解決策:

  • 同じ積載物に高密度の貨物と軽量の貨物を混載する
  • 貨物クラスを使って不均衡を特定する
  • 運賃($/lb と $/cuft)に基づいて優先順位を付ける
  • 貨物の種類を組み合わせるために、複数箇所に立ち寄るルートを検討する
  • 容積データを正確に取得するため、寸法測定装置を使用する
課題:ルート順序の競合

問題:

  • 最適な積載配置が配送順序と合わない
  • 1番目の停車地より先に3番目の停車地の貨物を降ろす必要がある(順序が逆)
  • 配送時の積み直しが過剰になる

解決策:

  • 配送順序と逆の順番で積み込む(後入れ先出し:LIFO)
  • 区画ごとに積み込む(停車地ごとにエリアを割り当てる)
  • 複数箇所に立ち寄るルートでは、積載率の低下を許容する
  • 順序が競合する停車地には、直送を検討する
  • 停車地ごとにパレット化する(停車地1=パレット1~5など)
課題:製品の混載不可条件

問題:

  • 食品は化学薬品と一緒に輸送できない(汚染)
  • 温度帯(冷凍、冷蔵、常温)
  • 危険物規制(混載不可のクラス)
  • 臭いの移り(魚、香水)

解決策:

  • 混載できない製品には別々のトラックを使用する
  • トラック内に物理的な仕切りを設ける
  • 積載区画を厳格に分ける(前方=食品、後方=食品以外)
  • 適切な梱包と密封を行う
  • TMS/WMS で混載不可マトリクスを管理する
課題:形状が不規則な荷物

問題:

  • 直方体ではない荷物(ドラム缶、コイル、機械)
  • 標準的なアルゴリズムでは効率よく積み込めない
  • 不規則な形状の周囲に空間が無駄になる

解決策:

  • 複雑な荷物にはカスタム3Dモデリングを使用する
  • 緩衝材や隙間充填材を使用する
  • 規格外の荷物専用のトラックを用意する
  • 重量のある不規則な荷物は床積みする
  • 正確なモデルを作成するため、写真撮影と3Dスキャンを行う
課題:直前の変更

問題:

  • 積載計画の作成後に注文が追加またはキャンセルされる
  • その場で再最適化が必要になる
  • 積み込み済みのトラックを調整しなければならない

解決策:

  • 変更に備えて、容量の10~15%を確保する
  • リアルタイムの再最適化アルゴリズムを使用する
  • 積載計画の「時間枠」を設ける(出荷の2時間前に確定する)
  • 柔軟な積載順序にする(追加分を所定の場所に積む)
  • 締め切り時刻を明確に伝える

出力形式

積載計画レポート

トラック積載計画 - トラック #TRK-001

トラック仕様:

  • 種類:53' ドライバン
  • 積載量:45,000 lbs、4,000 cu ft
  • 寸法:53' L × 8.5' W × 9' H

積載概要:

指標値積載率
総重量42,850 lbs95%
総容積3,420 cu ft86%
パレット数24
出荷数18
停車数3

対象出荷:

出荷顧客停車地重量容積寸法
S001Acme Corp12,400 lbs180 cuft4×4×6 ft
S002Beta Inc11,850 lbs145 cuft4×4×5 ft
S005Gamma LLC23,200 lbs220 cuft4×4×7 ft
..................

積載順序(前方から後方):

Zone 1 (Stop 3 - Last Off): Shipments S015-S018
  - 8 pallets
  - 12,450 lbs
  - Positions: Rows 1-2

Zone 2 (Stop 2): Shipments S005-S014
  - 10 pallets
  - 18,200 lbs
  - Positions: Rows 3-5

Zone 3 (Stop 1 - First Off): Shipments S001-S004
  - 6 pallets
  - 12,200 lbs
  - Positions: Rows 6-7 (rear)

重量配分:

  • 操舵軸:11,200 lbs(上限12,000 lbsの93%) ✓
  • 駆動軸:32,850 lbs(上限34,000 lbsの97%) ✓
  • トレーラー軸:31,650 lbs(上限34,000 lbsの93%) ✓
  • 合計:42,850 lbs - 適合

積載図:

Front (Cab)                                           Rear (Doors)
|------------|------------|------------|------------|------------|
| Zone 3     | Zone 2                  | Zone 1                 |
| Stop 1     | Stop 2                  | Stop 3                 |
| (First Off)| (Second Off)            | (Last Off)             |
|------------|------------|------------|------------|------------|
  Rows 6-7     Rows 3-5                  Rows 1-2
  6 pallets    10 pallets                8 pallets

代替計画とのパフォーマンス比較:

計画トラック数平均重量積載率平均容積積載率コスト
最適化(現在)394%85%$2,850
ルート優先積載478%72%$3,800
手動積載481%68%$3,800
削減額25%$950

推奨事項:

  1. 積載率は優秀です。バランスの取れた積載です
  2. パレットをあと2つ追加することを検討してください(積載余力があります)
  3. 積載中は車軸重量を監視してください

確認事項

さらに詳しい情報が必要な場合:

  1. トラック/トレーラーの種類は何ですか(ドライバン、冷蔵車、フラットベッドなど)?
  2. トレーラーの寸法と重量制限を教えてください
  3. 1日あたりの出荷量はどのくらいですか?
  4. 積載は単一停車地向けですか、それとも複数停車地向けですか?
  5. 現在の容積/重量積載率はどのくらいですか?
  6. 製品の相性による問題や制限はありますか?
  7. 現在、どのような積載計画方法を使用していますか?
  8. 出荷品の寸法は正確に把握できていますか?

関連スキル

  • pallet-loading:パレット配置の最適化
  • container-loading-optimization:輸送コンテナ向け
  • vehicle-loading-optimization:各種車両向け
  • 3d-bin-packing:3D 梱包アルゴリズム向け
  • route-optimization:積載計画とルートの調整
  • freight-optimization:貨物コストの最適化
  • knapsack-problems:梱包に関する理論的背景
  • warehouse-slotting-optimization:積載前の出荷品の仮置き

コメント