picker-routing-optimization について
倉庫運営チーム向けに、レイアウトやピッキング制約を評価し、ピッカーの経路方式を比較して推奨ルートを検討するスキルです。グラフや TSP による定式化、混雑対応、複数階の倉庫も扱います。
主な機能
- 01倉庫レイアウトとピッキング条件の評価
- 02S字・往復・中間通路・最大ギャップ方式
- 03TSPによるルート定式化
- 04混雑対応と複数階ルーティング
- 05ルート比較とレポート
活用シーン
- 01倉庫レイアウトに合う方式の選定
- 02ルート距離と推定時間の比較
- 03一方通行通路や混雑を考慮した計画
- 04複数階倉庫のルート確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill picker-routing-optimizationインストール後、Claude Code で/picker-routing-optimizationを入力して読み込まれたか確認します。
詳細
ピッカーのルート最適化
倉庫内のピッカーのルート設計と移動経路の最適化に精通した専門家です。移動距離の最小化、ピッキング時間の短縮、ピッカーの生産性向上、倉庫効率の最大化を実現する、最適なピッキングルートの設計を支援します。
初期評価
ピッカーのルートを最適化する前に、以下を確認してください。
-
倉庫レイアウト
- レイアウトの種類(グリッド型、斜め型、混合型)?
- 通路の数と長さ?
- 通路幅(双方向または一方通行)?
- 横断通路(中間地点、両端のみ)?
- ピッキング面の構成(片面、両面)?
- デポ/ステージング場所?
-
ピッキングの制約
- ピッキング方式(シングル、バッチ、ゾーン)?
- 使用機器(徒歩、ピッカーカート、フォークリフト、リーチトラック)?
- ピッキング対象がない通路をスキップできますか?
- 通路を双方向に通行できますか?
- 通路の途中で横断できますか?
- ピッキングリストの順序は変更できますか?
-
注文の特性
- 注文/バッチあたりの平均ピッキング数?
- ピッキング密度(通路あたりのピッキング数)?
- 倉庫内のピッキング対象の分布?
- 商品の保管場所のパターン?
-
現在のパフォーマンス
- 現在のルート設定方法?
- 注文あたりの平均移動距離?
- 1 時間あたりのピッキング数?
- ルートに関するピッカーからのフィードバック?
ピッカールートのフレームワーク
ルート設定戦略
1. S 字型(通過)ルート
- ピッキング対象がある各通路に入り、端まで通り抜けます
- 反対側の端から出て、ピッキング対象がある次の通路へ移動します
- 長所:シンプルで、通路内の往復がありません
- 短所:ピッキング対象がない通路の区間も通過する場合があります
- 効率:中程度(最適ルートの 60~70%)
2. 折り返しルート
- 通路に入り、商品をピッキングして、同じ端から戻ります
- 次の通路へ移動します
- 長所:非常にシンプルで、予測しやすい方法です
- 短所:往復が多く、移動距離が最長になります
- 効率:低い(最適ルートの 40~50%)
- 用途:狭い通路、または一方通行のみの場合
3. 中間地点ルート
- ピッキング対象が手前半分にある場合は、手前側から折り返します
- ピッキング対象が奥半分にある場合は、奥まで通り抜けます
- 中間地点に横断通路が必要です
- 長所:S 字型ルートや折り返しルートのみの場合より優れています
- 短所:横断通路の整備が必要です
- 効率:良好(最適ルートの 70~80%)
4. 最大ギャップルート
- 通路内のピッキング対象間で最も大きな間隔を特定します
- 最大の間隔を通らずに済むよう、通路への進入・退出を決めます
- 長所:ピッキング対象の分布に応じて調整できます
- 短所:より複雑で、計算が必要です
- 効率:非常に良好(最適ルートの 80~90%)
5. 最適ルート(TSP ベース)
- 巡回セールスマン問題として解きます
- すべてのピッキング対象を訪問する最短経路を求めます
- 長所:可能な限り最良のルートです
- 短所:計算が複雑です(NP 困難)
- 効率:最適(100%)
ルート設定の目的
Primary Goal:
Minimize total travel distance
Secondary Goals:
- Minimize pick time (travel + access)
- Balance picker workload
- Respect aisle traffic constraints
- Maintain pick accuracy (logical sequence)
Constraints:
- Aisle layout (can't cut through racks)
- One-way aisles (directional constraints)
- Congestion (avoid other pickers)
- Equipment limitations (turning radius, height)
数学的定式化
グラフとしての倉庫
倉庫を有向グラフ G = (V, E) としてモデル化します。
頂点(V):
- ピッキング場所
- 通路の端点
- 横断通路の交差点
- デポ(開始/終了地点)
辺(E):
- 頂点間の移動区間
- 辺の重み = 距離または時間
- 一方通行の通路には有向辺を使用
ルート設定問題:
Find shortest path from depot visiting all pick locations
and returning to depot
This is a variant of the Traveling Salesman Problem (TSP)
with special structure (rectilinear geometry)
倉庫向け TSP の定式化
決定変数:
- x[i,j] = ピッカーが場所 i から j へ移動する場合は 1、それ以外は 0
- u[i] = ルート内での場所 i の順序(部分巡回路の排除用)
パラメーター:
- d[i,j] = 場所 i から j までの距離
- P = ピッキング場所の集合
- depot = 開始/終了地点
目的関数:
Minimize: Σ Σ (d[i,j] × x[i,j]) for all i,j in (P ∪ {depot})
制約条件:
# 1. Leave each location exactly once (except depot twice - start and end)
for i in P:
Σ x[i,j] = 1 for all j != i
# 2. Enter each location exactly once
for j in P:
Σ x[i,j] = 1 for all i != j
# 3. Flow conservation
for k in P:
Σ x[i,k] = Σ x[k,j] for all i,j
# 4. Start and end at depot
Σ x[depot,j] = 1 for all j in P
Σ x[i,depot] = 1 for all i in P
# 5. Subtour elimination (Miller-Tucker-Zemlin)
for i,j in P:
u[i] - u[j] + n × x[i,j] <= n - 1
# 6. Aisle constraints (can't pass through racks)
for i,j not adjacent:
if no path exists:
x[i,j] = 0
ルート設定アルゴリズム
S 字型ルート
import numpy as np
import pandas as pd
def s_shape_routing(picks, aisles, cross_aisle_locations):
"""
S-Shape routing algorithm
Parameters:
-----------
picks : DataFrame
Columns: pick_id, aisle, position_in_aisle, side (left/right)
aisles : dict
{aisle_id: {'length': length, 'width': width}}
cross_aisle_locations : dict
{aisle_id: [positions...]} # Where cross-aisles exist
Returns:
--------
Route sequence and total distance
"""
# Group picks by aisle
picks_by_aisle = picks.groupby('aisle')
route = []
total_distance = 0
current_position = 0 # Start at position 0 (front of warehouse)
current_aisle = 0 # Start at aisle 0
# Get aisles with picks (sorted)
aisles_with_picks = sorted(picks_by_aisle.groups.keys())
for aisle_id in aisles_with_picks:
aisle_picks = picks_by_aisle.get_group(aisle_id).sort_values('position_in_aisle')
# Move to aisle (cross-aisle travel)
cross_aisle_distance = abs(aisle_id - current_aisle) * aisles[0].get('width', 10)
total_distance += cross_aisle_distance
# Determine entry point (front or back)
# For S-shape: alternate front and back entry
aisle_index = aisles_with_picks.index(aisle_id)
if aisle_index % 2 == 0:
# Enter from front, traverse to back
entry_position = 0
exit_position = aisles[aisle_id]['length']
picks_order = aisle_picks.sort_values('position_in_aisle')
else:
# Enter from back, traverse to front
entry_position = aisles[aisle_id]['length']
exit_position = 0
picks_order = aisle_picks.sort_values('position_in_aisle', ascending=False)
# Move to entry point
total_distance += abs(entry_position - current_position)
# Traverse aisle and pick items
for idx, pick in picks_order.iterrows():
route.append({
'pick_id': pick['pick_id'],
'aisle': aisle_id,
'position': pick['position_in_aisle'],
'sequence': len(route) + 1
})
# Add traversal distance
total_distance += abs(exit_position - entry_position)
current_position = exit_position
current_aisle = aisle_id
# Return to depot
total_distance += abs(current_position - 0)
total_distance += abs(current_aisle - 0) * aisles[0].get('width', 10)
return {
'route': pd.DataFrame(route),
'total_distance': total_distance,
'num_picks': len(picks)
}
# Example usage
picks = pd.DataFrame({
'pick_id': [f'P{i:03d}' for i in range(1, 21)],
'aisle': np.random.randint(1, 11, 20), # 10 aisles
'position_in_aisle': np.random.uniform(0, 100, 20), # 100 ft aisles
'side': np.random.choice(['left', 'right'], 20)
})
aisles = {i: {'length': 100, 'width': 12} for i in range(1, 11)}
cross_aisles = {i: [0, 100] for i in range(1, 11)} # Front and back only
result = s_shape_routing(picks, aisles, cross_aisles)
print(f"S-Shape Routing:")
print(f"Total Distance: {result['total_distance']:.2f} ft")
print(f"Number of Picks: {result['num_picks']}")
print(f"\nFirst 5 picks in sequence:")
print(result['route'].head())
最大ギャップルート
def largest_gap_routing(picks, aisles):
"""
Largest gap routing algorithm
For each aisle:
- If picks only in front half: enter and return from front
- If picks only in back half: enter and return from back
- If picks in both halves: enter from one side, exit from other (S-shape)
but choose entry/exit to minimize distance
Parameters:
-----------
picks : DataFrame
Pick locations
aisles : dict
Aisle specifications
Returns:
--------
Optimized route
"""
picks_by_aisle = picks.groupby('aisle')
route = []
total_distance = 0
current_position = 0
current_aisle = 0
aisles_with_picks = sorted(picks_by_aisle.groups.keys())
for aisle_id in aisles_with_picks:
aisle_picks = picks_by_aisle.get_group(aisle_id).sort_values('position_in_aisle')
aisle_length = aisles[aisle_id]['length']
# Find largest gap between consecutive picks
positions = sorted(aisle_picks['position_in_aisle'].tolist())
# Add aisle endpoints to gap calculation
gaps = []
gaps.append({'gap_size': positions[0] - 0,
'start': 0, 'end': positions[0]})
for i in range(len(positions) - 1):
gaps.append({'gap_size': positions[i+1] - positions[i],
'start': positions[i], 'end': positions[i+1]})
gaps.append({'gap_size': aisle_length - positions[-1],
'start': positions[-1], 'end': aisle_length})
largest_gap = max(gaps, key=lambda x: x['gap_size'])
# Determine routing strategy based on largest gap location
if largest_gap['start'] == 0:
# Largest gap at front, enter from back
entry_position = aisle_length
exit_position = aisle_length
picks_order = aisle_picks.sort_values('position_in_aisle', ascending=False)
elif largest_gap['end'] == aisle_length:
# Largest gap at back, enter from front
entry_position = 0
exit_position = 0
picks_order = aisle_picks.sort_values('position_in_aisle')
else:
# Largest gap in middle, traverse around it
# Enter from front, exit from back (or vice versa)
# Choose direction that minimizes total travel
# Option 1: Front to back
dist1 = aisle_length - largest_gap['gap_size']
# Option 2: Back to front
dist2 = aisle_length - largest_gap['gap_size']
# Both same for middle gaps, so use S-shape logic
if current_position < aisle_length / 2:
entry_position = 0
exit_position = aisle_length
picks_order = aisle_picks.sort_values('position_in_aisle')
else:
entry_position = aisle_length
exit_position = 0
picks_order = aisle_picks.sort_values('position_in_aisle', ascending=False)
# Move to aisle
cross_aisle_distance = abs(aisle_id - current_aisle) * aisles[0].get('width', 10)
total_distance += cross_aisle_distance
# Move to entry point
total_distance += abs(entry_position - current_position)
# Pick items
for idx, pick in picks_order.iterrows():
route.append({
'pick_id': pick['pick_id'],
'aisle': aisle_id,
'position': pick['position_in_aisle'],
'sequence': len(route) + 1
})
# Calculate actual travel distance in aisle (excluding largest gap if avoided)
if entry_position == exit_position:
# Return routing
furthest_pick = picks_order.iloc[-1]['position_in_aisle']
aisle_travel = 2 * abs(furthest_pick - entry_position)
else:
# Traversal routing (minus largest gap if skipped)
aisle_travel = abs(exit_position - entry_position)
total_distance += aisle_travel
current_position = exit_position
current_aisle = aisle_id
# Return to depot
total_distance += abs(current_position - 0)
total_distance += abs(current_aisle - 0) * aisles[0].get('width', 10)
return {
'route': pd.DataFrame(route) if route else pd.DataFrame(),
'total_distance': total_distance,
'num_picks': len(picks)
}
result_lg = largest_gap_routing(picks, aisles)
print(f"\nLargest Gap Routing:")
print(f"Total Distance: {result_lg['total_distance']:.2f} ft")
print(f"Improvement vs S-Shape: {(result['total_distance'] - result_lg['total_distance']) / result['total_distance'] * 100:.1f}%")
TSP ベースの最適ルート
from scipy.spatial.distance import pdist, squareform
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def tsp_optimal_routing(picks, warehouse_graph):
"""
Optimal routing using TSP solver (Google OR-Tools)
Parameters:
-----------
picks : DataFrame
Pick locations with x, y coordinates
warehouse_graph : dict
Distance matrix considering warehouse layout constraints
Returns:
--------
Optimal route
"""
# Create distance matrix
# In real warehouse, distance is constrained by aisles (not Euclidean)
# Use warehouse_graph or calculate Manhattan distance
locations = picks[['x', 'y']].values
n_locations = len(locations)
# Add depot (0, 0)
depot_location = np.array([[0, 0]])
all_locations = np.vstack([depot_location, locations])
# Calculate distance matrix (Manhattan distance for warehouse)
def manhattan_distance(loc1, loc2):
return abs(loc1[0] - loc2[0]) + abs(loc1[1] - loc2[1])
n = len(all_locations)
distance_matrix = np.zeros((n, n))
for i in range(n):
for j in range(n):
if i != j:
distance_matrix[i][j] = manhattan_distance(
all_locations[i], all_locations[j]
)
# Convert to integer (OR-Tools requirement)
distance_matrix = (distance_matrix * 100).astype(int)
# Create routing model
manager = pywrapcp.RoutingIndexManager(n, 1, 0) # n locations, 1 vehicle, depot=0
routing = pywrapcp.RoutingModel(manager)
# Create distance callback
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return distance_matrix[from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Search parameters
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.seconds = 10
# Solve
solution = routing.SolveWithParameters(search_parameters)
if solution:
# Extract route
route = []
index = routing.Start(0)
total_distance = 0
while not routing.IsEnd(index):
node = manager.IndexToNode(index)
if node > 0: # Skip depot at start
route.append({
'pick_id': picks.iloc[node - 1]['pick_id'],
'x': picks.iloc[node - 1]['x'],
'y': picks.iloc[node - 1]['y'],
'sequence': len(route) + 1
})
next_index = solution.Value(routing.NextVar(index))
total_distance += routing.GetArcCostForVehicle(index, next_index, 0)
index = next_index
return {
'route': pd.DataFrame(route),
'total_distance': total_distance / 100, # Convert back from integer
'num_picks': len(picks),
'optimal': True
}
return {'route': pd.DataFrame(), 'total_distance': 0, 'optimal': False}
# Example with coordinates
picks_with_coords = pd.DataFrame({
'pick_id': [f'P{i:03d}' for i in range(1, 21)],
'x': np.random.uniform(0, 100, 20), # x coordinate (across aisles)
'y': np.random.uniform(0, 100, 20), # y coordinate (down aisle)
})
# Note: In real implementation, would use actual warehouse graph
# that respects aisle structure
result_tsp = tsp_optimal_routing(picks_with_coords, warehouse_graph={})
if result_tsp['optimal']:
print(f"\nTSP Optimal Routing:")
print(f"Total Distance: {result_tsp['total_distance']:.2f} ft")
print(f"First 5 picks:")
print(result_tsp['route'].head())
高度なルート設定手法
混雑を回避する動的ルート設定
class DynamicRouter:
"""
Dynamic routing that adapts to warehouse congestion
"""
def __init__(self, warehouse_layout, real_time_tracking=False):
self.warehouse_layout = warehouse_layout
self.real_time_tracking = real_time_tracking
self.picker_locations = {} # {picker_id: (aisle, position)}
self.congestion_map = {} # {aisle: congestion_level}
def update_picker_location(self, picker_id, aisle, position):
"""Update picker location for congestion tracking"""
self.picker_locations[picker_id] = (aisle, position)
self.update_congestion_map()
def update_congestion_map(self):
"""Calculate congestion level for each aisle"""
aisle_counts = {}
for picker_id, (aisle, position) in self.picker_locations.items():
aisle_counts[aisle] = aisle_counts.get(aisle, 0) + 1
# Congestion level: number of pickers / aisle capacity
for aisle in self.warehouse_layout['aisles']:
count = aisle_counts.get(aisle, 0)
capacity = 3 # Assume max 3 pickers per aisle comfortably
self.congestion_map[aisle] = count / capacity
def route_with_congestion(self, picks, picker_id):
"""
Calculate route considering current congestion
Penalize aisles with high congestion
"""
# Start with base routing algorithm (e.g., largest gap)
base_route = largest_gap_routing(picks, self.warehouse_layout['aisles'])
if not self.real_time_tracking:
return base_route
# Adjust route based on congestion
# Re-sequence to visit less congested aisles first
route_df = base_route['route'].copy()
route_df['aisle'] = picks.set_index('pick_id').loc[route_df['pick_id'], 'aisle'].values
route_df['congestion'] = route_df['aisle'].map(self.congestion_map).fillna(0)
# Sort picks within each priority group by congestion
route_df = route_df.sort_values(['congestion', 'aisle'])
route_df['sequence'] = range(1, len(route_df) + 1)
return {
'route': route_df,
'total_distance': base_route['total_distance'] * 1.05, # Slight penalty for re-sequencing
'congestion_adjusted': True
}
# Example usage
warehouse_layout = {
'aisles': list(range(1, 11)),
'aisle_specs': {i: {'length': 100, 'width': 12} for i in range(1, 11)}
}
router = DynamicRouter(warehouse_layout, real_time_tracking=True)
# Simulate other pickers
router.update_picker_location('picker_1', aisle=3, position=50)
router.update_picker_location('picker_2', aisle=3, position=30)
router.update_picker_location('picker_3', aisle=7, position=60)
# Route new picker
route_dynamic = router.route_with_congestion(picks, 'picker_4')
print("\nDynamic Routing with Congestion:")
print(f"Congestion Map: {router.congestion_map}")
多層階倉庫のルート設定
def multi_level_routing(picks, levels, vertical_travel_time=30):
"""
Routing for multi-level warehouses (mezzanines, multi-story)
Parameters:
-----------
picks : DataFrame
Picks with level, aisle, position
levels : list
Available levels
vertical_travel_time : float
Seconds to move between levels
Returns:
--------
Optimized route considering vertical travel
"""
# Group picks by level
picks_by_level = picks.groupby('level')
# Strategy: Minimize level changes
# Visit all picks on one level before moving to next
route = []
total_distance = 0
total_time = 0
current_level = 1 # Start at ground level
# Determine optimal level sequence
# Heuristic: Ground level first, then upper levels in order
level_sequence = sorted(picks_by_level.groups.keys())
for level in level_sequence:
level_picks = picks_by_level.get_group(level)
# Move to level (vertical travel)
if level != current_level:
level_changes = abs(level - current_level)
total_time += level_changes * vertical_travel_time
current_level = level
# Route within level (use largest gap or TSP)
level_route = largest_gap_routing(
level_picks,
aisles={i: {'length': 100, 'width': 12} for i in range(1, 11)}
)
# Add to total route
level_route_df = level_route['route']
level_route_df['level'] = level
route.append(level_route_df)
total_distance += level_route['total_distance']
# Combine all levels
full_route = pd.concat(route, ignore_index=True)
full_route['sequence'] = range(1, len(full_route) + 1)
return {
'route': full_route,
'total_distance': total_distance,
'total_time': total_time,
'num_picks': len(picks),
'levels_visited': len(level_sequence)
}
# Example
picks_multi_level = pd.DataFrame({
'pick_id': [f'P{i:03d}' for i in range(1, 31)],
'level': np.random.choice([1, 2, 3], 30),
'aisle': np.random.randint(1, 11, 30),
'position_in_aisle': np.random.uniform(0, 100, 30),
})
result_ml = multi_level_routing(picks_multi_level, levels=[1, 2, 3])
print("\nMulti-Level Routing:")
print(f"Total Distance: {result_ml['total_distance']:.2f} ft")
print(f"Vertical Travel Time: {result_ml['total_time']:.0f} sec")
print(f"Levels Visited: {result_ml['levels_visited']}")
ツールとライブラリ
ルーティングソフトウェア
倉庫管理システム:
- Manhattan WMS:最適化されたピック経路を生成
- Blue Yonder (JDA) WMS:AI 駆動のルーティング
- SAP EWM:オーダー別およびウェーブ別のピッキングルーティング
- HighJump WMS:リアルタイム更新による動的ルーティング
特化型最適化:
- Lucas Systems:最適化されたルートによる音声指示ピッキング
- Voxware:インテリジェントルーティングによる音声ピッキング
- Google OR-Tools:オープンソースのルーティング最適化
- Gurobi/CPLEX:商用最適化ソルバー
Python ライブラリ
# Routing Optimization
from ortools.constraint_solver import routing_enums_pb2, pywrapcp
from scipy.optimize import linear_sum_assignment
import networkx as nx # Graph algorithms
# TSP Solvers
from python_tsp.exact import solve_tsp_dynamic_programming
from python_tsp.heuristics import solve_tsp_simulated_annealing
# Distance Calculations
from scipy.spatial.distance import pdist, squareform, cityblock
import numpy as np
# Visualization
import matplotlib.pyplot as plt
import matplotlib.patches as patches
よくある課題と解決策
課題:一方通行の通路
問題:
- 通路が狭く、通行は一方通行のみ
- S 字ルートは使えません(両方向に通行できません)
- 入口まで戻る必要があります
解決策:
- リターンルーティングを基準として使用します
- 入口/出口の位置を最適化します(前方と後方)
- 横断通路がある場合は、中間地点を利用します
- 双方向通行のため、主要な通路の拡幅を検討します
- 通路の進行方向を交互に設定します(奇数/偶数)
課題:混雑と通行妨害
問題:
- 複数のピッカーが同じ通路にいます
- 通行妨害や待ち時間が発生します
- 回避によってルートが長くなります
解決策:
- 混雑状況を考慮したリアルタイムルーティングを使用します
- ピッカーの開始時刻をずらします(5~10 分間隔)
- ゾーン別ピッキングを行います(通路をピッカーごとに割り当てます)
- モバイルアプリ/音声システムを使って動的にルートを再設定します
- 交通流を分析してボトルネックを特定します
課題:ピックリストが大きい
問題:
- 1 つのオーダー/バッチに 100 か所以上のピック位置があります
- TSP の計算コストが高くなります
- リアルタイムルーティングが実現できません
解決策:
- TSP の代わりにヒューリスティック(最大ギャップ法、S 字ルート)を使用します
- 小さなバッチに分割します
- ルートをオフラインで事前計算します(繰り返し発生するオーダー向け)
- 近似 TSP(LKH、Christofides)を使用します
- 最適化の時間制限を設定します(5 秒で最適なルートを算出)
課題:ピッキング時間のばらつき
問題:
- 5 秒で終わるピックもあれば、60 秒かかるピックもあります
- 重量物、高い棚、数量の多いピックがあります
- 距離ベースのルーティングでは時間のばらつきが考慮されません
解決策:
- エッジに距離ではなく所要時間の重みを付けます
- ルート最適化にピッキング時間を含めます
- 難しいピックを先に行います(ピッカーが元気なうちに)
- 重量物/大型物を事前に配置します(別のワークフロー)
- 時間動作分析を使用して調整します
課題:レイアウトの変更
問題:
- 倉庫のレイアウトが変更されます(スロッティングの更新)
- ルートが最適でなくなります
- ピッカーがロケーションの変更に戸惑います
解決策:
- スロッティングの変更後にルートを再計算します
- 段階的に展開します(ゾーンごと)
- WMS のロケーションマスターをすぐに更新します
- 新しいレイアウトについてピッカーをトレーニングします
- RF/音声で案内します(ロケーションに依存しない方式)
出力形式
ピッカールーティングレポート
ルート最適化分析 - オーダー #12345
ピックリストの概要:
- ピック合計:42 行
- 対象通路:8 通路(2、4、5、7、9、11、13、15)
- 倉庫ゾーン:A(18 ピック)、B(15 ピック)、C(9 ピック)
ルーティングの比較:
| 方法 | 距離(ft) | 推定時間(分) | 改善率 |
|---|---|---|---|
| リターンルーティング | 1,845 | 28.5 | 基準 |
| S 字ルーティング | 1,124 | 17.4 | 39% |
| 最大ギャップ法 | 892 | 13.8 | 52% |
| 最適 TSP | 834 | 12.9 | 55% |
推奨ルート(最大ギャップ法):
Sequence | Pick ID | SKU | Aisle | Position | Side | Qty |
---------|---------|-----|-------|----------|------|-----|
1 | P001 | SKU_A | 2 | 15.3 | L | 2 |
2 | P002 | SKU_B | 2 | 28.7 | R | 1 |
3 | P003 | SKU_C | 2 | 45.2 | L | 3 |
4 | [Cross to Aisle 4]
5 | P008 | SKU_H | 4 | 82.1 | R | 1 |
6 | P009 | SKU_I | 4 | 67.3 | L | 2 |
...
ルートの可視化:
Depot (Start)
↓
Aisle 2 [Enter Front → Pick 3 items → Exit Front]
↓
Cross-Aisle Travel (2 → 4)
↓
Aisle 4 [Enter Back → Pick 5 items → Exit Front]
↓
Cross-Aisle Travel (4 → 5)
↓
Aisle 5 [Enter Front → Pick 4 items → Exit Front]
...
↓
Return to Depot (End)
Total Distance: 892 ft
Estimated Time: 13.8 minutes (assuming 100 ft/min walk + 10 sec/pick)
パフォーマンス指標:
- ピックあたりの距離:21.2 ft/ピック
- 推定ピック数/時間:182(リターンルーティングでは 145)
- 生産性の向上:+26%
確認事項
詳細情報が必要な場合:
- 倉庫のレイアウトはどのようなものですか(グリッド型、横断通路ありなど)?
- 通路は一方通行ですか、双方向ですか?
- どのピッキング方式を使用していますか(個別、バッチ、ゾーン)?
- オーダー/バッチあたりの平均ピック数はいくつですか?
- 現在、どのルーティング方式を使用していますか?
- 現在のピック数/時間はいくつですか?
- ルーティング機能を備えた WMS を使用していますか?
- 混雑や通行妨害の問題はありますか?
関連スキル
- traveling-salesman-problem:TSP のアルゴリズムと理論について
- order-batching-optimization:ルーティング用の最適なバッチ作成について
- warehouse-slotting-optimization:ルートに影響する SKU の配置について
- wave-planning-optimization:ルーティングに影響するウェーブ設計について
- network-flow-optimization:倉庫内のフローのモデリングについて
- graph-algorithms:最短経路とルーティングについて
- metaheuristic-optimization:大規模なルーティング問題について
コメント
Markdown 対応