仓库货位优化器 warehouse-slotting-optimizer

仓库货位优化器是一个AI驱动的仓库管理技能,专门用于优化仓库布局和货位分配,通过算法分析商品周转率、优化拣货路径、设计黄金区域、计算前向拣货区规模,实现拣货效率最大化、行走距离最小化和空间利用率最优化。关键词:仓库优化、货位分配、拣货路径、周转率分析、空间利用率、物流效率、供应链管理、仓储管理、智能仓储、WMS优化。

仓储管理 0 次安装 0 次浏览 更新于 2/25/2026

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.js
  • inventory-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设置前向拣货区"
  ]
}

最佳实践

  1. 使用实际拣货数据 - 不仅仅是销售或预测数据
  2. 定期重新分配货位 - 周转率随时间变化
  3. 考虑人体工学 - 不仅仅是效率
  4. 品类分组 - 经常一起订购的商品
  5. 测量前后对比 - 验证改进效果
  6. 让拣货员参与 - 他们了解实际问题

约束条件

  • 需要历史拣货数据
  • 物理约束限制了优化空间
  • 重新分配货位有过渡成本
  • 需平衡优化与操作灵活性