pallet-loading について
倉庫、梱包、輸送計画でのパレット積載を最適化するスキルです。箱の寸法や重量、積み重ね、安定性の制約を考慮し、重心と圧縮強度の確認方法も扱います。
主な機能
- 01単一SKUと交互積みパターンの生成
- 02混載SKUのパレット構成の最適化
- 03パレット寸法と重量制限の確認
- 04重心安定性と圧縮強度の分析
- 05高さ・はみ出し・輸送条件への対応
活用シーン
- 01パレット当たりのケース数の増加
- 02混載SKU注文で必要なパレット数の削減
- 03積載の安定性と重量支持の確認
- 04輸送時の高さ・重量制限に合わせた計画
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill pallet-loadingインストール後、Claude Code で/pallet-loadingを入力して読み込まれたか確認します。
詳細
パレット積載の最適化
パレット積載と最適化の専門家として、積載率を最大化し、荷崩れを防ぎ、重量制限を守り、輸送要件を満たしながら、箱、ケース、商品をパレットに効率よく積載できるよう支援します。
初期評価
パレット積載を最適化する前に、以下を確認してください。
-
パレットの仕様
- パレットの種類は何ですか?(GMA/CHEP 48x40、Euro 47.2x39.4、特注など)
- パレットの寸法:長さ x 幅 x 高さ
- パレットの耐荷重はどのくらいですか?(通常2,500~4,600 lbs)
- パレット自体の重量はどのくらいですか?(通常30~70 lbs)
- はみ出しは許容されますか?(通常0~3インチ)
-
積載する商品
- 商品/ケースはいくつありますか?寸法はどのくらいですか?
- 商品の重量と重量配分
- 積み重ねに関する制約はありますか?(積み重ね上限、圧縮強度)
- 特別な配置が必要な壊れやすい商品はありますか?
- すべて同じサイズですか、それともSKUが混在していますか?
-
積載の制約
- パレットの最大高さはどのくらいですか?(多くの場合72"、84"、96")
- 重量配分の要件はありますか?
- かみ合わせる積み方が必要ですか?
- ストレッチフィルム、シュリンクフィルム、またはバンド掛けを使用しますか?
- 滑り止めシートは必要ですか?
-
輸送要件
- トラック輸送ですか、それとも鉄道輸送ですか?
- パレットを積み重ねますか?(二段積み)
- フォークリフトでアクセスする方向はどちらですか?
- 積み込みドックの高さに制約はありますか?
- 輸送時間と荷役作業はどのようなものですか?
-
最適化の目標
- 1パレットあたりのケース数を最大化しますか?
- パレット数を最小化しますか?
- 安定性を確保しますか(はみ出しを最小化)?
- 倉庫スペースを最適化しますか?
パレット積載のフレームワーク
標準パレットサイズ
北米:
- GMA/CHEP:48" x 40"(1219mm x 1016mm)— 最も一般的
- 42" x 42" — 電気通信、塗料
- 48" x 48" — ドラム缶、バルク資材
- 40" x 48" — 軍事、セメント
ヨーロッパ:
- EUR/EUR1:47.2" x 31.5"(1200mm x 800mm)— 欧州標準
- EUR2:47.2" x 23.6"(1200mm x 600mm)
- EUR3:39.4" x 47.2"(1000mm x 1200mm)
- EUR6:31.5" x 47.2"(800mm x 1200mm)
アジア:
- 43.3" x 43.3"(1100mm x 1100mm)— アジア標準
- 39.4" x 39.4"(1000mm x 1000mm)
ISO規格:
- ISO 1:47.2" x 39.4"(1200mm x 1000mm)
- ISO 2:39.4" x 47.2"(1000mm x 1200mm)
主な概念
1. パレットパターン
- 各段における箱の配置
- 安定性と効率を左右します
- 代表的なパターン:ブロック、列、風車、分割列
2. かみ合わせ
- 段ごとに箱を重ね合わせます(90°回転)
- 安定性が向上します
- 荷崩れのリスクを軽減します
3. 列積み
- 箱を真上に積み重ねます
- かみ合わせはありません
- 安定性は低くなりますが、必要となる場合があります
4. はみ出し
- 箱がパレットの端からはみ出している状態
- 通常は2~3インチ以内に制限されます
- 安定性が低下し、破損リスクが高まります
5. 圧縮強度
- 箱が上から支えられる重量
- 積み重ね高さを決めるうえで重要です
- 時間の経過や湿度によって低下します
数式による定式化
パレット積載問題
目的: 積載する箱の数を最大化する、またはパレット数を最小化する
変数:
- x_i、y_i = パレット上における箱iの位置
- z_i = 箱iの段番号
- o_i = 箱iの向き(0~5)
- pattern_j = 段jに使用するパレットパターン
制約条件:
- パレット寸法:箱がパレットの範囲内に収まること(はみ出し許容範囲を含む)
- 重なりなし:同じ段の箱同士が重ならないこと
- 重量:総重量 ≤ パレットの耐荷重
- 安定性:下から十分に支えられ、重心が適切であること
- 積み重ね高さ:高さ ≤ 許容最大値
- 圧縮:各箱にかかる重量 ≤ その箱の圧縮強度
アルゴリズムと解法
パターン生成方法
シンプルなブロックパターン
def generate_block_pattern(box_dims, pallet_dims, allow_overhang=2):
"""
Generate simple block pattern for single box size
Calculates optimal orientation and layout
Parameters:
- box_dims: (length, width, height)
- pallet_dims: (length, width, max_height)
- allow_overhang: maximum overhang in inches
Returns pattern with max boxes per layer
"""
box_l, box_w, box_h = box_dims
pallet_l, pallet_w, pallet_h = pallet_dims
best_pattern = None
max_boxes = 0
# Try both orientations
orientations = [
(box_l, box_w, box_h, 0), # Original
(box_w, box_l, box_h, 1) # Rotated 90°
]
for l, w, h, rotation in orientations:
# Calculate how many fit in each direction
effective_pallet_l = pallet_l + 2 * allow_overhang
effective_pallet_w = pallet_w + 2 * allow_overhang
boxes_length = int(effective_pallet_l // l)
boxes_width = int(effective_pallet_w // w)
boxes_per_layer = boxes_length * boxes_width
if boxes_per_layer > max_boxes:
max_boxes = boxes_per_layer
# Calculate actual overhang
actual_overhang_l = (boxes_length * l - pallet_l) / 2
actual_overhang_w = (boxes_width * w - pallet_w) / 2
best_pattern = {
'boxes_per_layer': boxes_per_layer,
'boxes_length': boxes_length,
'boxes_width': boxes_width,
'rotation': rotation,
'box_dims': (l, w, h),
'overhang': (actual_overhang_l, actual_overhang_w),
'positions': []
}
# Generate positions
for i in range(boxes_length):
for j in range(boxes_width):
x = i * l - actual_overhang_l
y = j * w - actual_overhang_w
best_pattern['positions'].append((x, y, l, w))
return best_pattern
# Example
box = (12, 10, 8) # inches
pallet = (48, 40, 72) # GMA pallet
pattern = generate_block_pattern(box, pallet, allow_overhang=2)
print(f"Boxes per layer: {pattern['boxes_per_layer']}")
print(f"Layout: {pattern['boxes_length']} x {pattern['boxes_width']}")
print(f"Overhang: {pattern['overhang']}")
かみ合わせパターン生成器
def generate_interlocking_pattern(box_dims, pallet_dims, num_layers):
"""
Generate interlocking pattern for stability
Alternates orientation between layers
Parameters:
- box_dims: (length, width, height)
- pallet_dims: (length, width, max_height)
- num_layers: number of layers to generate
Returns multi-layer interlocking pattern
"""
box_l, box_w, box_h = box_dims
pallet_l, pallet_w, pallet_h = pallet_dims
patterns = []
for layer in range(num_layers):
# Alternate orientation by layer
if layer % 2 == 0:
# Layer 0, 2, 4, ... : original orientation
pattern = generate_block_pattern(
(box_l, box_w, box_h),
(pallet_l, pallet_w, pallet_h)
)
else:
# Layer 1, 3, 5, ... : rotated orientation
pattern = generate_block_pattern(
(box_w, box_l, box_h),
(pallet_l, pallet_w, pallet_h)
)
# Add z-coordinate (layer height)
z = layer * box_h
pattern['layer'] = layer
pattern['z'] = z
patterns.append(pattern)
total_boxes = sum(p['boxes_per_layer'] for p in patterns)
return {
'num_layers': num_layers,
'total_boxes': total_boxes,
'total_height': num_layers * box_h,
'layers': patterns
}
# Example
box = (16, 12, 10)
pallet = (48, 40, 72)
max_layers = int(pallet[2] // box[2]) # 7 layers fit
interlock = generate_interlocking_pattern(box, pallet, max_layers)
print(f"Total boxes: {interlock['total_boxes']}")
print(f"Height: {interlock['total_height']} inches")
SKU混在パターンの最適化
from pulp import *
def optimize_mixed_sku_pallet(items, pallet_dims, max_weight):
"""
Optimize pallet loading with mixed SKUs
Uses integer programming to select best combination
Parameters:
- items: list of dicts with 'id', 'dims', 'weight', 'quantity'
- pallet_dims: (length, width, max_height)
- max_weight: weight capacity
Returns optimal loading pattern
"""
pallet_l, pallet_w, pallet_h = pallet_dims
# Pre-calculate patterns for each item type
item_patterns = {}
for item in items:
item_id = item['id']
dims = item['dims']
pattern = generate_block_pattern(dims, pallet_dims)
item_patterns[item_id] = pattern
# Create optimization problem
prob = LpProblem("Pallet_Loading", LpMaximize)
# Decision variables: how many of each item to load
x = LpVariable.dicts("load",
[item['id'] for item in items],
lowBound=0,
cat='Integer')
# Objective: maximize total value/volume loaded
prob += lpSum([
x[item['id']] * item['dims'][0] * item['dims'][1] * item['dims'][2]
for item in items
]), "Total_Volume"
# Weight constraint
prob += lpSum([
x[item['id']] * item['weight']
for item in items
]) <= max_weight, "Weight_Capacity"
# Quantity constraint
for item in items:
prob += x[item['id']] <= item['quantity'], f"Quantity_{item['id']}"
# Height constraint (simplified - assumes stacking)
# This is a simplification; full solution needs layer-by-layer check
prob += lpSum([
x[item['id']] * item['dims'][2]
for item in items
]) <= pallet_h * 10, "Height_Limit" # Rough approximation
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
# Extract solution
solution = {
'status': LpStatus[prob.status],
'items_loaded': {},
'total_weight': 0,
'total_volume': 0
}
for item in items:
qty = int(x[item['id']].varValue) if x[item['id']].varValue else 0
if qty > 0:
solution['items_loaded'][item['id']] = qty
solution['total_weight'] += qty * item['weight']
volume = item['dims'][0] * item['dims'][1] * item['dims'][2]
solution['total_volume'] += qty * volume
return solution
安定性の分析
重心の確認
import numpy as np
def check_center_of_gravity(loaded_items, pallet_dims):
"""
Calculate center of gravity and check stability
Parameters:
- loaded_items: list of dicts with 'position' (x,y,z), 'dims', 'weight'
- pallet_dims: (length, width, height)
Returns COG coordinates and stability score
"""
if not loaded_items:
return None
total_weight = sum(item['weight'] for item in loaded_items)
# Calculate weighted average position
cog_x = sum(
(item['position'][0] + item['dims'][0]/2) * item['weight']
for item in loaded_items
) / total_weight
cog_y = sum(
(item['position'][1] + item['dims'][1]/2) * item['weight']
for item in loaded_items
) / total_weight
cog_z = sum(
(item['position'][2] + item['dims'][2]/2) * item['weight']
for item in loaded_items
) / total_weight
# Check if COG is within pallet bounds
pallet_center_x = pallet_dims[0] / 2
pallet_center_y = pallet_dims[1] / 2
offset_x = abs(cog_x - pallet_center_x)
offset_y = abs(cog_y - pallet_center_y)
# Stability score (lower offset = higher stability)
# Good: offset < 10% of pallet dimension
max_offset_x = pallet_dims[0] * 0.1
max_offset_y = pallet_dims[1] * 0.1
stability_x = max(0, 100 * (1 - offset_x / max_offset_x))
stability_y = max(0, 100 * (1 - offset_y / max_offset_y))
stability_score = min(stability_x, stability_y)
return {
'cog': (cog_x, cog_y, cog_z),
'pallet_center': (pallet_center_x, pallet_center_y),
'offset': (offset_x, offset_y),
'stability_score': stability_score,
'is_stable': stability_score > 70
}
# Example
items = [
{'position': (0, 0, 0), 'dims': (12, 10, 8), 'weight': 25},
{'position': (12, 0, 0), 'dims': (12, 10, 8), 'weight': 25},
{'position': (0, 10, 0), 'dims': (12, 10, 8), 'weight': 25},
{'position': (12, 10, 0), 'dims': (12, 10, 8), 'weight': 25},
]
pallet = (48, 40, 72)
cog_result = check_center_of_gravity(items, pallet)
print(f"COG: {cog_result['cog']}")
print(f"Stability Score: {cog_result['stability_score']:.1f}")
print(f"Stable: {cog_result['is_stable']}")
圧縮強度の確認
def check_compression_strength(layers, box_compression_strength):
"""
Verify each box can support weight above it
Parameters:
- layers: list of layer patterns (bottom to top)
- box_compression_strength: max weight box can support (lbs)
Returns safety analysis
"""
issues = []
safe = True
for layer_idx in range(len(layers) - 1):
layer = layers[layer_idx]
# Calculate weight above this layer
weight_above = 0
for upper_layer_idx in range(layer_idx + 1, len(layers)):
upper_layer = layers[upper_layer_idx]
weight_above += sum(
item['weight'] for item in upper_layer['items']
)
# Check each box in this layer
num_boxes_in_layer = len(layer['items'])
weight_per_box = weight_above / num_boxes_in_layer if num_boxes_in_layer > 0 else 0
if weight_per_box > box_compression_strength:
issues.append({
'layer': layer_idx,
'weight_per_box': weight_per_box,
'max_allowed': box_compression_strength,
'safety_factor': weight_per_box / box_compression_strength
})
safe = False
return {
'safe': safe,
'issues': issues,
'max_safe_layers': len(layers) if safe else layer_idx
}
パレットの総合最適化ツール
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
class PalletOptimizer:
"""
Comprehensive Pallet Loading Optimizer
Handles single and mixed SKU loading with stability checks
"""
def __init__(self, pallet_type='GMA', custom_dims=None,
weight_capacity=2500, max_height=72):
"""
Initialize pallet optimizer
Parameters:
- pallet_type: 'GMA', 'EUR', 'EUR1', or 'custom'
- custom_dims: (length, width) if pallet_type='custom'
- weight_capacity: max weight in lbs
- max_height: max height in inches
"""
# Standard pallet dimensions (inches)
pallet_dims = {
'GMA': (48, 40),
'CHEP': (48, 40),
'EUR': (47.2, 31.5),
'EUR1': (47.2, 31.5),
'EUR2': (47.2, 23.6),
'ISO1': (47.2, 39.4),
'ISO2': (39.4, 47.2)
}
if pallet_type == 'custom':
if custom_dims is None:
raise ValueError("Must provide custom_dims for custom pallet")
self.pallet_dims = (*custom_dims, max_height)
else:
dims = pallet_dims.get(pallet_type, (48, 40))
self.pallet_dims = (*dims, max_height)
self.pallet_type = pallet_type
self.weight_capacity = weight_capacity
self.items = []
self.solution = None
def add_item(self, length, width, height, weight,
quantity=1, item_id=None, compression_strength=None,
fragile=False):
"""Add item type to be loaded"""
if item_id is None:
item_id = f"SKU_{len(self.items)}"
# Default compression strength (stack limit)
if compression_strength is None:
compression_strength = weight * 10 # Conservative estimate
self.items.append({
'id': item_id,
'dims': (length, width, height),
'weight': weight,
'quantity': quantity,
'compression_strength': compression_strength,
'fragile': fragile
})
def optimize_single_sku(self, allow_overhang=2, interlock=True):
"""
Optimize loading for single SKU
Parameters:
- allow_overhang: max overhang in inches
- interlock: use interlocking pattern
Returns optimal pallet configuration
"""
if len(self.items) != 1:
raise ValueError("Use optimize_single_sku only for single SKU")
item = self.items[0]
box_dims = item['dims']
box_weight = item['weight']
available_qty = item['quantity']
# Calculate max layers by height
max_layers_height = int(self.pallet_dims[2] // box_dims[2])
# Calculate max layers by weight
max_layers_weight = int(self.weight_capacity //
(box_weight * 100)) # rough estimate
# Calculate max layers by compression
max_layers_compression = int(item['compression_strength'] / box_weight) + 1
# Take minimum
max_layers = min(max_layers_height, max_layers_weight,
max_layers_compression, 15) # Cap at 15 for safety
if interlock:
pattern = generate_interlocking_pattern(
box_dims, self.pallet_dims, max_layers
)
else:
# Simple block stacking
base_pattern = generate_block_pattern(
box_dims, self.pallet_dims, allow_overhang
)
pattern = {
'num_layers': max_layers,
'total_boxes': base_pattern['boxes_per_layer'] * max_layers,
'layers': [base_pattern] * max_layers
}
# Check if we have enough quantity
if pattern['total_boxes'] > available_qty:
# Reduce layers to fit available quantity
boxes_per_layer = pattern['layers'][0]['boxes_per_layer']
actual_layers = int(available_qty // boxes_per_layer)
pattern['num_layers'] = actual_layers
pattern['total_boxes'] = boxes_per_layer * actual_layers
# Calculate weight
total_weight = pattern['total_boxes'] * box_weight
# Check stability
loaded_items = []
for layer_idx in range(pattern['num_layers']):
z = layer_idx * box_dims[2]
layer_pattern = pattern['layers'][layer_idx % len(pattern['layers'])]
for pos in layer_pattern['positions']:
loaded_items.append({
'position': (pos[0], pos[1], z),
'dims': box_dims,
'weight': box_weight
})
cog = check_center_of_gravity(loaded_items, self.pallet_dims)
self.solution = {
'pattern': pattern,
'total_boxes': pattern['total_boxes'],
'total_weight': total_weight,
'total_height': pattern['num_layers'] * box_dims[2],
'num_layers': pattern['num_layers'],
'utilization': self.calculate_utilization(pattern, box_dims),
'stability': cog,
'loaded_items': loaded_items
}
return self.solution
def optimize_mixed_sku(self):
"""
Optimize loading for mixed SKUs
Uses layer-by-layer heuristic
"""
if len(self.items) < 2:
raise ValueError("Use optimize_mixed_sku for multiple SKUs")
# Sort items by volume (largest first)
sorted_items = sorted(self.items,
key=lambda x: x['dims'][0] * x['dims'][1] * x['dims'][2],
reverse=True)
loaded_items = []
current_height = 0
total_weight = 0
# Load layer by layer
while current_height < self.pallet_dims[2]:
# Select item for this layer
layer_item = None
for item in sorted_items:
if item['quantity'] > 0:
# Check if item fits
if current_height + item['dims'][2] <= self.pallet_dims[2]:
layer_item = item
break
if layer_item is None:
break # No more items fit
# Generate pattern for this layer
layer_pattern = generate_block_pattern(
layer_item['dims'],
self.pallet_dims
)
boxes_to_load = min(
layer_pattern['boxes_per_layer'],
layer_item['quantity']
)
# Check weight
if total_weight + boxes_to_load * layer_item['weight'] > self.weight_capacity:
# Reduce boxes to fit weight
boxes_to_load = int((self.weight_capacity - total_weight) / layer_item['weight'])
if boxes_to_load == 0:
break
# Load boxes
for i, pos in enumerate(layer_pattern['positions'][:boxes_to_load]):
loaded_items.append({
'item_id': layer_item['id'],
'position': (pos[0], pos[1], current_height),
'dims': layer_item['dims'],
'weight': layer_item['weight']
})
layer_item['quantity'] -= boxes_to_load
total_weight += boxes_to_load * layer_item['weight']
current_height += layer_item['dims'][2]
# Calculate metrics
cog = check_center_of_gravity(loaded_items, self.pallet_dims)
self.solution = {
'total_boxes': len(loaded_items),
'total_weight': total_weight,
'total_height': current_height,
'loaded_items': loaded_items,
'stability': cog
}
return self.solution
def calculate_utilization(self, pattern, box_dims):
"""Calculate volume utilization percentage"""
box_volume = box_dims[0] * box_dims[1] * box_dims[2]
total_box_volume = pattern['total_boxes'] * box_volume
pallet_volume = (self.pallet_dims[0] * self.pallet_dims[1] *
pattern['num_layers'] * box_dims[2])
return (total_box_volume / pallet_volume * 100) if pallet_volume > 0 else 0
def visualize_3d(self, save_path=None):
"""3D visualization of pallet load"""
if self.solution is None:
raise ValueError("No solution to visualize. Run optimize first.")
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# Draw pallet base
pallet_l, pallet_w, pallet_h = self.pallet_dims
self._draw_pallet_base(ax, pallet_l, pallet_w)
# Draw loaded items
colors = plt.cm.tab20(np.linspace(0, 1, 20))
for idx, item in enumerate(self.solution['loaded_items']):
pos = item['position']
dims = item['dims']
color = colors[idx % 20]
self._draw_box_3d(ax, pos, dims, color, alpha=0.7)
# Draw COG
if self.solution.get('stability'):
cog = self.solution['stability']['cog']
ax.scatter([cog[0]], [cog[1]], [cog[2]],
c='red', s=200, marker='*',
label='Center of Gravity')
ax.set_xlabel('Length (in)')
ax.set_ylabel('Width (in)')
ax.set_zlabel('Height (in)')
ax.set_title(f'Pallet Load: {self.solution["total_boxes"]} boxes\n'
f'Weight: {self.solution["total_weight"]:.0f} lbs | '
f'Height: {self.solution["total_height"]:.1f} in')
ax.legend()
# Set equal aspect
max_dim = max(pallet_l, pallet_w, self.solution['total_height'])
ax.set_xlim(-5, max_dim + 5)
ax.set_ylim(-5, max_dim + 5)
ax.set_zlim(0, max_dim + 5)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
plt.show()
def _draw_pallet_base(self, ax, length, width):
"""Draw pallet base"""
vertices = np.array([
[0, 0, 0], [length, 0, 0], [length, width, 0], [0, width, 0]
])
face = [vertices]
pallet_face = Poly3DCollection(face, facecolors='brown',
linewidths=2, edgecolors='black',
alpha=0.3)
ax.add_collection3d(pallet_face)
def _draw_box_3d(self, ax, position, dimensions, color, alpha=0.7):
"""Draw 3D box"""
x, y, z = position
l, w, h = dimensions
vertices = np.array([
[x, y, z], [x+l, y, z], [x+l, y+w, z], [x, y+w, z],
[x, y, z+h], [x+l, y, z+h], [x+l, y+w, z+h], [x, y+w, z+h]
])
faces = [
[vertices[0], vertices[1], vertices[5], vertices[4]],
[vertices[2], vertices[3], vertices[7], vertices[6]],
[vertices[0], vertices[3], vertices[7], vertices[4]],
[vertices[1], vertices[2], vertices[6], vertices[5]],
[vertices[0], vertices[1], vertices[2], vertices[3]],
[vertices[4], vertices[5], vertices[6], vertices[7]]
]
face_collection = Poly3DCollection(faces, facecolors=color,
linewidths=0.5, edgecolors='black',
alpha=alpha)
ax.add_collection3d(face_collection)
def visualize_top_view(self, layer_index=0, save_path=None):
"""2D top-down view of specific layer"""
if self.solution is None:
raise ValueError("No solution to visualize")
fig, ax = plt.subplots(figsize=(10, 8))
# Draw pallet outline
pallet_l, pallet_w = self.pallet_dims[:2]
pallet_rect = patches.Rectangle((0, 0), pallet_l, pallet_w,
linewidth=2, edgecolor='black',
facecolor='lightgray', alpha=0.3)
ax.add_patch(pallet_rect)
# Filter items for this layer
if 'pattern' in self.solution:
# Single SKU
if layer_index < len(self.solution['pattern']['layers']):
layer = self.solution['pattern']['layers'][layer_index]
box_dims = self.items[0]['dims']
for pos in layer['positions']:
rect = patches.Rectangle(
(pos[0], pos[1]), pos[2], pos[3],
linewidth=1, edgecolor='blue',
facecolor='lightblue', alpha=0.7
)
ax.add_patch(rect)
# Add dimensions label
cx = pos[0] + pos[2]/2
cy = pos[1] + pos[3]/2
ax.text(cx, cy, f'{pos[2]:.0f}x{pos[3]:.0f}',
ha='center', va='center', fontsize=8)
ax.set_xlim(-5, pallet_l + 5)
ax.set_ylim(-5, pallet_w + 5)
ax.set_aspect('equal')
ax.set_xlabel('Length (in)')
ax.set_ylabel('Width (in)')
ax.set_title(f'Pallet Top View - Layer {layer_index + 1}')
ax.grid(True, alpha=0.3)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
plt.show()
def print_solution(self):
"""Print solution summary"""
if self.solution is None:
print("No solution available")
return
print("=" * 70)
print("PALLET LOADING SOLUTION")
print("=" * 70)
print(f"Pallet type: {self.pallet_type}")
print(f"Pallet dimensions: {self.pallet_dims[0]} x {self.pallet_dims[1]} x {self.pallet_dims[2]} in")
print(f"Weight capacity: {self.weight_capacity} lbs")
print()
print(f"Total boxes loaded: {self.solution['total_boxes']}")
print(f"Total weight: {self.solution['total_weight']:.1f} lbs "
f"({self.solution['total_weight']/self.weight_capacity*100:.1f}% of capacity)")
print(f"Total height: {self.solution['total_height']:.1f} in "
f"({self.solution['total_height']/self.pallet_dims[2]*100:.1f}% of max)")
if 'utilization' in self.solution:
print(f"Volume utilization: {self.solution['utilization']:.1f}%")
if self.solution.get('stability'):
stab = self.solution['stability']
print()
print("Stability Analysis:")
print(f" Center of Gravity: ({stab['cog'][0]:.1f}, {stab['cog'][1]:.1f}, {stab['cog'][2]:.1f})")
print(f" Offset from center: ({stab['offset'][0]:.1f}, {stab['offset'][1]:.1f}) in")
print(f" Stability Score: {stab['stability_score']:.1f}/100")
print(f" Status: {'STABLE' if stab['is_stable'] else 'UNSTABLE - REVIEW REQUIRED'}")
# Example usage
if __name__ == "__main__":
# Example 1: Single SKU optimization
print("Example 1: Single SKU Pallet Loading")
print("-" * 50)
pallet = PalletOptimizer(pallet_type='GMA', weight_capacity=2500, max_height=72)
# Add boxes (12x10x8 inches, 25 lbs each, 100 available)
pallet.add_item(length=12, width=10, height=8, weight=25,
quantity=100, item_id='BOX_A',
compression_strength=250)
# Optimize
solution = pallet.optimize_single_sku(interlock=True)
# Print results
pallet.print_solution()
# Visualize
print("\nGenerating 3D visualization...")
pallet.visualize_3d()
print("\nGenerating top view...")
pallet.visualize_top_view(layer_index=0)
ツールとライブラリ
Python ライブラリ
palletizing - パレットパターンの生成 py3dbp - パレットへの積載に使用できます rectpack - 長方形のパッキング(層の生成用)
商用ソフトウェア
- CAPE PACK:プロ向けパレタイズソフトウェア
- TOPS Pro:パレット最適化
- PalletStacking:オンラインパレット計算ツール
- LoadCalc:積載計画ソフトウェア
- Pallet Design System (PDS):NWPCA ソフトウェア
オンラインツール
- PackEx:無料のパレット計算ツール
- PalletConfigr:パレットパターン生成ツール
- Boxologic:無料の 3D パッキングツール
よくある課題と解決策
課題:パレット積載効率が低い(<75%)
問題:
- 箱を効率よく配置できない
- 箱の間に大きな隙間ができる
- 垂直方向のスペースが無駄になる
解決策:
- 別の向きを試します(90°回転)
- インターロッキングパターンを使用します
- 同じパレットにサイズの近い SKU を混載します
- より隙間なく並べられる特注のケースサイズを検討します
- 小さい商品で隙間を埋めます
課題:積荷が不安定
問題:
- 輸送中にパレットが傾く
- 箱がずれたり、崩れたりする
- 大きくはみ出している
解決策:
- はみ出しを <3 inches に制限します
- インターロッキングパターンを使用します
- 重い商品を下段の中央に置きます
- 重心の計算を確認します
- 層の間にスリップシートを使用します
- ストレッチフィルムを適切に巻きます
- 支えとしてコーナーボードを追加します
課題:圧縮による損傷
問題:
- 下段の箱が潰れる
- 保管中に積み重ねが崩れる
- 湿度によって段ボール箱が弱くなる
解決策:
- 圧縮強度の仕様を確認します
- ECT 評価に基づいて積み重ねの高さを制限します
- 強度の低い箱には縦積みを使用します
- 荷物の上部に天板を追加します
- 保管場所の湿度を管理します
- 下段には強度の高い箱を使用します
課題:複数 SKU の複雑さ
問題:
- サイズの異なる箱をうまく一緒に梱包できない
- 高さの違いにより不安定になる
- 効率的なパターンを生成しにくい
解決策:
- 同じ層には高さの近い箱をまとめます
- 層ごとに積み付ける方法を使用します
- 最も大きい箱、重い箱を下段に置きます
- 緩衝材で小さな隙間を埋めます
- サイズが大きく異なる場合は、2枚のパレットを使用することを検討します
課題:輸送上の制約
問題:
- パレットがトラックに積める高さを超えている
- 荷役設備の耐荷重を超えている
- ドアを通過できない
解決策:
- 最大高さの制約を設定します(通常は 72" または 84")
- 重量制限を守ります(フォークリフトの積載能力は 3,000~5,000 lbs)
- 最適化の前にドアの有効寸法を確認します
- 2段積みが可能か検討します
- 使用するトラック/貨車の寸法に合わせて計画します
出力形式
パレット積載レポート
概要:
- パレットタイプ:GMA(48" x 40")
- 積載箱数:84 ケース
- 総重量:2,100 lbs
- 全高:64 inches
- 積載効率:89.5%
- 安定性:合格(COG のずれ:1.2")
積載パターン:
層 1(最下段):
- パターン:4 x 5 = 20 箱
- 向き:12"L x 10"W
- 層の高さ:8"
- 層の重量:500 lbs
層 2:
- パターン:5 x 4 = 20 箱(90°回転)
- 向き:10"L x 12"W
- 層の高さ:16"
- 層の重量:500 lbs
層 3:
- パターン:4 x 5 = 20 箱
- 向き:12"L x 10"W
- 層の高さ:24"
- 層の重量:500 lbs
層 4:
- パターン:5 x 4 = 20 箱(90°回転)
- 向き:10"L x 12"W
- 層の高さ:32"
- 層の重量:500 lbs
層 5(最上段):
- パターン:2 x 2 = 4 箱
- 向き:12"L x 10"W
- 層の高さ:40"
- 層の重量:100 lbs
安定性分析:
- 重心:(24.0", 20.0", 20.5")
- 中心からのずれ:水平方向に 1.2"
- 状態:安定 ✓
- 推奨:ストレッチフィルムを 3~4 周巻きます
輸送時の指示:
- 積載後すぐにストレッチフィルムを巻きます
- フォークリフトの四方差しに対応させます
- 2段積み可能(重量の条件を満たしています)
- 最大積み重ね高さ:2段(合計 128")
- 4面すべてにラベルを貼ります
確認する質問
より詳しい情報が必要な場合:
- どのパレットサイズを使用していますか?(GMA 48x40、ユーロパレット、カスタムサイズなど)
- 箱の寸法と重量を教えてください。
- パレットごとに何箱積載する必要がありますか?
- 高さ制限はありますか?
- 箱の向きを変えたり、同じパレットに異なる種類の箱を混載したりできますか?
- 安定性や圧縮強度に関する懸念はありますか?
- 輸送方法は何ですか?(トラック、鉄道、コンテナ)
- 安定性を高めるために、かみ合わせパターンが必要ですか?
関連スキル
- 3d-bin-packing:一般的な3D梱包問題向け
- container-loading-optimization:コンテナへのパレット積載向け
- vehicle-loading-optimization:パレットを使ったトラックへの積載向け
- 2d-bin-packing:段積みパターンの生成向け
- load-building-optimization:受注処理とパレット積み付け向け
- warehouse-slotting-optimization:パレットの保管場所の最適化向け
コメント
Markdown 対応