name: warehouse-slotting-optimizer description: 仓库货位与布局优化技能,用于拣货路径最小化和空间利用率最大化 allowed-tools: Bash(*) Read Write Edit Glob Grep WebFetch metadata: author: babysitter-sdk version: “1.0.0” category: supply-chain backlog-id: SK-IE-026
仓库货位优化器
您是 warehouse-slotting-optimizer - 一个专门用于优化仓库货位分配和布局的技能,旨在最小化拣货路径并最大化空间利用率。
概述
本技能支持AI驱动的仓库优化,包括:
- 产品周转率分析(按拣货频次进行ABC分类)
- 体积移动分析
- 拣货路径优化
- 区域设计与分配
- 前向拣货区规模确定
- 货位分配算法
- 黄金区域优化
- 货位分配性能指标
功能
1. 周转率分析
import pandas as pd
import numpy as np
def velocity_analysis(order_data: pd.DataFrame):
"""
按拣货频次分析SKU周转率
"""
# 按SKU聚合拣货数据
sku_picks = order_data.groupby('sku').agg({
'quantity': 'sum',
'order_id': 'count'
}).rename(columns={'order_id': 'pick_count'})
# 按拣货次数降序排序
sku_picks = sku_picks.sort_values('pick_count', ascending=False)
# 计算累积百分比
total_picks = sku_picks['pick_count'].sum()
sku_picks['cum_picks'] = sku_picks['pick_count'].cumsum()
sku_picks['cum_pct'] = sku_picks['cum_picks'] / total_picks * 100
# 分配周转率类别
def assign_velocity(pct):
if pct <= 80:
return '快' # A类商品 - 拣货量的前80%
elif pct <= 95:
return '中' # B类商品
else:
return '慢' # C类商品
sku_picks['velocity_class'] = sku_picks['cum_pct'].apply(assign_velocity)
return {
"sku_velocity": sku_picks,
"summary": {
"total_skus": len(sku_picks),
"fast_movers": len(sku_picks[sku_picks['velocity_class'] == '快']),
"medium_movers": len(sku_picks[sku_picks['velocity_class'] == '中']),
"slow_movers": len(sku_picks[sku_picks['velocity_class'] == '慢'])
}
}
2. 黄金区域优化
def optimize_golden_zone(skus: pd.DataFrame, warehouse_config: dict):
"""
优化黄金区域(符合人体工学的首要拣货区域)的货位分配
黄金区域:腰部到肩部高度,伸手可及的范围
"""
golden_zone = warehouse_config.get('golden_zone', {
'height_min': 24, # 距离地面的英寸数
'height_max': 54,
'reach_max': 24 # 距离通道的英寸数
})
# 计算黄金区域容量
rack_config = warehouse_config.get('rack', {
'bays': 100,
'levels': 5,
'positions_per_bay': 3
})
# 黄金区域层数(通常为5层中的第2-3层)
golden_levels = [2, 3] # 假设有5层
golden_positions = (rack_config['bays'] *
len(golden_levels) *
rack_config['positions_per_bay'])
# 按拣货频次对SKU排序
fast_skus = skus[skus['velocity_class'] == '快'].copy()
fast_skus = fast_skus.sort_values('pick_count', ascending=False)
# 分配到黄金区域
assignments = []
position_count = 0
for idx, row in fast_skus.iterrows():
if position_count < golden_positions:
assignments.append({
'sku': idx,
'zone': 'golden',
'level': golden_levels[position_count % len(golden_levels)],
'priority': position_count + 1
})
position_count += 1
else:
assignments.append({
'sku': idx,
'zone': 'standard',
'level': None,
'priority': position_count + 1
})
return {
"golden_zone_capacity": golden_positions,
"skus_in_golden": position_count,
"assignments": assignments,
"golden_zone_pick_coverage": fast_skus.head(golden_positions)['pick_count'].sum() /
skus['pick_count'].sum() * 100
}
3. 拣货路径优化
def optimize_pick_path(picks: list, warehouse_layout: dict):
"""
优化仓库内的拣货路径
使用旅行商问题启发式算法
"""
from scipy.spatial.distance import cdist
import itertools
# 获取拣货点的位置坐标
locations = []
for pick in picks:
loc = warehouse_layout['locations'].get(pick['location'])
if loc:
locations.append((pick['location'], loc['x'], loc['y']))
# 计算距离矩阵
coords = np.array([(l[1], l[2]) for l in locations])
dist_matrix = cdist(coords, coords)
# 最近邻启发式算法
n = len(locations)
visited = [False] * n
path = [0] # 从第一个位置开始
visited[0] = True
for _ in range(n - 1):
current = path[-1]
nearest = None
nearest_dist = float('inf')
for j in range(n):
if not visited[j] and dist_matrix[current][j] < nearest_dist:
nearest = j
nearest_dist = dist_matrix[current][j]
if nearest is not None:
path.append(nearest)
visited[nearest] = True
# 计算总距离
total_distance = sum(dist_matrix[path[i]][path[i+1]]
for i in range(len(path)-1))
# 返回优化后的序列
optimized_picks = [picks[i] for i in path]
return {
"optimized_sequence": optimized_picks,
"total_distance": total_distance,
"locations_count": n,
"estimated_time_minutes": total_distance / warehouse_layout.get('walk_speed', 100) * 60
}
4. 前向拣货区规模确定
def size_forward_pick_area(sku_data: pd.DataFrame, replenishment_cost: float,
space_cost_per_unit: float):
"""
确定最优的前向拣货区规模
平衡补货成本与空间成本
"""
# 按周转率排序
sku_data = sku_data.sort_values('picks_per_day', ascending=False)
results = []
cumulative_picks = 0
total_picks = sku_data['picks_per_day'].sum()
for i, (idx, row) in enumerate(sku_data.iterrows()):
cumulative_picks += row['picks_per_day']
pick_coverage = cumulative_picks / total_picks
# 估算成本
forward_skus = i + 1
space_cost = forward_skus * row.get('cube', 1) * space_cost_per_unit
replen_trips = sku_data.head(forward_skus)['picks_per_day'].sum() / \
sku_data.head(forward_skus)['case_qty'].mean()
replen_cost = replen_trips * replenishment_cost
total_cost = space_cost + replen_cost
results.append({
'forward_skus': forward_skus,
'pick_coverage': pick_coverage,
'space_cost': space_cost,
'replen_cost': replen_cost,
'total_cost': total_cost
})
if pick_coverage >= 0.95:
break
# 找到最优值
optimal = min(results, key=lambda x: x['total_cost'])
return {
"analysis": results,
"optimal_forward_skus": optimal['forward_skus'],
"pick_coverage": optimal['pick_coverage'],
"total_cost": optimal['total_cost']
}
5. 货位分配算法
def slot_assignment(skus: pd.DataFrame, locations: pd.DataFrame,
constraints: dict = None):
"""
将SKU分配到仓库位置
考虑因素:
- 周转率(快周转商品分配到最佳位置)
- 体积(尺寸兼容性)
- 重量(重物放在底层)
- 品类分组(相关商品放在一起)
"""
constraints = constraints or {}
# 为每个位置评分
def score_location(loc):
score = 0
# 距离发货区的距离(越小越好)
score -= loc.get('distance_to_ship', 0) * 0.01
# 符合人体工学区域加分
if 24 <= loc.get('height', 0) <= 54:
score += 10
# 底层适合重物
if loc.get('level', 0) == 1:
score += 5
return score
locations['score'] = locations.apply(score_location, axis=1)
locations = locations.sort_values('score', ascending=False)
# 按分配优先级对SKU排序
skus['priority'] = skus['picks_per_day'] * 100 - skus.get('cube', 1)
skus = skus.sort_values('priority', ascending=False)
assignments = []
used_locations = set()
for sku_idx, sku in skus.iterrows():
for loc_idx, loc in locations.iterrows():
if loc_idx in used_locations:
continue
# 检查约束条件
if sku.get('cube', 1) > loc.get('capacity', float('inf')):
continue
if sku.get('weight', 0) > loc.get('weight_limit', float('inf')):
continue
# 分配
assignments.append({
'sku': sku_idx,
'location': loc_idx,
'picks_per_day': sku['picks_per_day'],
'location_score': loc['score']
})
used_locations.add(loc_idx)
break
return {
"assignments": assignments,
"assigned_count": len(assignments),
"unassigned_skus": len(skus) - len(assignments)
}
6. 货位分配性能指标
def calculate_slotting_metrics(current_slotting: pd.DataFrame,
order_history: pd.DataFrame,
warehouse_config: dict):
"""
计算货位分配性能指标
"""
metrics = {}
# 拣货密度(每英尺行走距离的拣货次数)
total_picks = len(order_history)
# 基于当前货位分配估算行走距离
travel_estimate = estimate_total_travel(current_slotting, order_history, warehouse_config)
metrics['pick_density'] = total_picks / travel_estimate if travel_estimate > 0 else 0
# 黄金区域利用率
golden_picks = order_history.merge(current_slotting, on='sku')
golden_picks = golden_picks[golden_picks['zone'] == 'golden']
metrics['golden_zone_pick_pct'] = len(golden_picks) / total_picks * 100
# 货位利用率
total_slots = len(current_slotting)
active_slots = current_slotting[current_slotting['picks_per_day'] > 0]
metrics['slot_utilization'] = len(active_slots) / total_slots * 100
# 周转率对齐分数(快周转商品是否在最佳位置?)
current_slotting = current_slotting.sort_values('picks_per_day', ascending=False)
current_slotting['ideal_rank'] = range(1, len(current_slotting) + 1)
current_slotting['actual_rank'] = current_slotting['location_score'].rank(ascending=False)
correlation = current_slotting['ideal_rank'].corr(current_slotting['actual_rank'])
metrics['velocity_alignment'] = correlation
return metrics
def estimate_total_travel(slotting, orders, config):
# 简化的行走距离估算
avg_picks_per_order = len(orders) / orders['order_id'].nunique()
avg_travel_per_pick = config.get('avg_aisle_length', 100) / 2
return len(orders) * avg_travel_per_pick
流程集成
本技能与以下流程集成:
warehouse-layout-slotting-optimization.jsinventory-optimization-analysis.js
输出格式
{
"velocity_analysis": {
"fast_movers": 150,
"medium_movers": 450,
"slow_movers": 2400
},
"golden_zone": {
"capacity": 300,
"pick_coverage": 72.5
},
"slotting_metrics": {
"pick_density": 2.3,
"velocity_alignment": 0.85
},
"recommendations": [
"将前50个SKU移至黄金区域",
"考虑为200个SKU设置前向拣货区"
]
}
最佳实践
- 使用实际拣货数据 - 不仅仅是销售或预测数据
- 定期重新分配货位 - 周转率随时间变化
- 考虑人体工学 - 不仅仅是效率
- 品类分组 - 经常一起订购的商品
- 测量前后对比 - 验证改进效果
- 让拣货员参与 - 他们了解实际问题
约束条件
- 需要历史拣货数据
- 物理约束限制了优化空间
- 重新分配货位有过渡成本
- 需平衡优化与操作灵活性