| name | pick-routing |
| description | Implement routing strategies for warehouse order picking. Use this skill when determining optimal pick sequences, applying heuristic routing methods, calculating travel distances, or optimizing picker movements through storage areas. |
Pick Routing
Implement efficient routing strategies for warehouse order picking operations.
Installation
pip install numpy pandas networkx
Quick Start
import numpy as np
# Define pick locations
picks = [
{'sku': 'A100', 'zone': 'A', 'aisle': 1, 'bay': 5, 'level': 2},
{'sku': 'B200', 'zone': 'A', 'aisle': 3, 'bay': 12, 'level': 1},
{'sku': 'C300', 'zone': 'B', 'aisle': 2, 'bay': 8, 'level': 3}
]
# Generate optimal pick sequence
route = optimize_pick_route(picks)
Routing Strategies
Traversal Strategy
def traversal_routing(picks, aisles_in_zone):
"""
Traverse entire aisles that contain picks.
Best for high-density picking.
"""
# Group picks by aisle
picks_by_aisle = {}
for pick in picks:
key = (pick['zone'], pick['aisle'])
if key not in picks_by_aisle:
picks_by_aisle[key] = []
picks_by_aisle[key].append(pick)
route = []
direction = 1 # 1 = forward, -1 = backward
for zone_aisle in sorted(picks_by_aisle.keys()):
aisle_picks = picks_by_aisle[zone_aisle]
# Sort by bay position
aisle_picks.sort(key=lambda p: p['bay'], reverse=(direction == -1))
route.extend(aisle_picks)
direction *= -1 # Alternate direction
return route
Return Strategy
def return_routing(picks):
"""
Enter and exit each aisle from the same end.
Best for low-density picking.
"""
picks_by_aisle = {}
for pick in picks:
key = (pick['zone'], pick['aisle'])
if key not in picks_by_aisle:
picks_by_aisle[key] = []
picks_by_aisle[key].append(pick)
route = []
for zone_aisle in sorted(picks_by_aisle.keys()):
aisle_picks = sorted(picks_by_aisle[zone_aisle], key=lambda p: p['bay'])
route.extend(aisle_picks)
# Implicit return to aisle entrance
return route
Combined Strategy
def combined_routing(picks, aisle_length, threshold=0.5):
"""
Use traversal for aisles with many picks, return for sparse aisles.
"""
picks_by_aisle = {}
for pick in picks:
key = (pick['zone'], pick['aisle'])
if key not in picks_by_aisle:
picks_by_aisle[key] = []
picks_by_aisle[key].append(pick)
route = []
current_end = 'front'
for zone_aisle in sorted(picks_by_aisle.keys()):
aisle_picks = picks_by_aisle[zone_aisle]
positions = [p['bay'] for p in aisle_picks]
# Calculate coverage
coverage = (max(positions) - min(positions)) / aisle_length
if coverage > threshold:
# Traversal - go through entire aisle
if current_end == 'front':
aisle_picks.sort(key=lambda p: p['bay'])
current_end = 'back'
else:
aisle_picks.sort(key=lambda p: p['bay'], reverse=True)
current_end = 'front'
else:
# Return - enter and exit same side
if current_end == 'front':
aisle_picks.sort(key=lambda p: p['bay'])
else:
aisle_picks.sort(key=lambda p: p['bay'], reverse=True)
route.extend(aisle_picks)
return route
Pick Sequence Optimization
def optimize_sequence_nearest_neighbor(picks, start_location):
"""Sequence picks using nearest neighbor heuristic."""
def distance(p1, p2):
return (abs(p1['aisle'] - p2['aisle']) * 10 +
abs(p1['bay'] - p2['bay']) +
abs(p1['level'] - p2['level']) * 2)
remaining = picks.copy()
sequence = []
current = start_location
while remaining:
nearest = min(remaining, key=lambda p: distance(current, p))
sequence.append(nearest)
remaining.remove(nearest)
current = nearest
return sequence
def optimize_sequence_2opt(picks, distance_func):
"""Improve pick sequence using 2-opt."""
best = picks.copy()
improved = True
while improved:
improved = False
for i in range(1, len(best) - 1):
for j in range(i + 1, len(best)):
# Try reversing segment
new_route = best[:i] + best[i:j+1][::-1] + best[j+1:]
old_dist = sum(distance_func(best[k], best[k+1])
for k in range(len(best)-1))
new_dist = sum(distance_func(new_route[k], new_route[k+1])
for k in range(len(new_route)-1))
if new_dist < old_dist:
best = new_route
improved = True
break
if improved:
break
return best
Multi-Level Picking
def level_optimized_routing(picks):
"""
Optimize routing considering vertical travel between levels.
Group picks by level to minimize elevator/lift usage.
"""
picks_by_level = {}
for pick in picks:
level = pick['level']
if level not in picks_by_level:
picks_by_level[level] = []
picks_by_level[level].append(pick)
route = []
# Process levels in order (bottom to top or vice versa)
for level in sorted(picks_by_level.keys()):
level_picks = picks_by_level[level]
# Apply horizontal routing strategy within each level
level_route = traversal_routing(level_picks, aisles_in_zone=10)
route.extend(level_route)
return route
def calculate_vertical_travel(route, level_travel_time=5):
"""Calculate additional time for level changes."""
level_changes = 0
for i in range(1, len(route)):
if route[i]['level'] != route[i-1]['level']:
level_changes += abs(route[i]['level'] - route[i-1]['level'])
return level_changes * level_travel_time
Route Distance Calculation
class WarehouseDistanceCalculator:
def __init__(self, aisle_width, bay_depth, level_height):
self.aisle_width = aisle_width
self.bay_depth = bay_depth
self.level_height = level_height
def distance(self, loc1, loc2):
"""Calculate travel distance between two locations."""
horizontal = 0
if loc1['aisle'] == loc2['aisle']:
# Same aisle
horizontal = abs(loc1['bay'] - loc2['bay']) * self.bay_depth
else:
# Different aisles
aisle_diff = abs(loc1['aisle'] - loc2['aisle'])
horizontal = (aisle_diff * self.aisle_width +
abs(loc1['bay'] - loc2['bay']) * self.bay_depth)
vertical = abs(loc1['level'] - loc2['level']) * self.level_height
return horizontal + vertical
def route_distance(self, route, start=None, end=None):
"""Calculate total route distance."""
total = 0
if start:
total += self.distance(start, route[0])
for i in range(len(route) - 1):
total += self.distance(route[i], route[i+1])
if end:
total += self.distance(route[-1], end)
return total
Pick Time Estimation
def estimate_pick_time(route, walk_speed=1.2, pick_time=5, level_change_time=10):
"""
Estimate total time to complete pick route.
walk_speed: meters per second
pick_time: seconds per pick
"""
calc = WarehouseDistanceCalculator(
aisle_width=3.0, bay_depth=1.2, level_height=1.5
)
walk_distance = calc.route_distance(route)
walk_time = walk_distance / walk_speed
total_pick_time = len(route) * pick_time
level_changes = sum(
1 for i in range(1, len(route))
if route[i]['level'] != route[i-1]['level']
)
total_time = walk_time + total_pick_time + (level_changes * level_change_time)
return {
'walk_distance': walk_distance,
'walk_time': walk_time,
'pick_time': total_pick_time,
'level_change_time': level_changes * level_change_time,
'total_time': total_time
}