欢迎光临
我们一直在努力

AI在智能仓储中的应用:从库存预测到拣货路径优化的技术复盘

AI在智能仓储中的应用:从库存预测到拣货路径优化的技术复盘

仓储智能化不是堆算法,而是把每个决策点的几秒钟优化加起来——拣货员每天走3万步降到1.8万步,这就是AI的价值。

一、仓储的四个AI决策点

2023年我们参与了一个日订单量5万单的电商仓储改造项目。仓库面积2万平米,SKU超过15万个,拣货员120人,日均出库8万件。核心痛点:库存周转慢、拣货路径长、爆仓与断货交替出现。

我们将仓储AI化拆解为四个独立又关联的决策问题:

  • 库存预测:每个SKU未来N天的需求量是多少?
  • 安全库存:每个SKU应该备多少安全库存?
  • 拣货路径:一张拣货单上的20个SKU,最优行走路径是什么?
  • AGV调度:50台AGV如何协同不碰撞且效率最高?
  • 二、库存需求的时序预测

    我们选用了两种模型做对比:

    2.1 Prophet vs DeepAR

    import pandas as pd
    from prophet import Prophet
    from gluonts.model.deepar import DeepAREstimator
    from gluonts.mx import Trainer

    class InventoryForecastService:
    """
    库存需求预测
    Prophet: 适合有明显季节性和趋势的SKU
    DeepAR: 适合SKU数量多且需要联合建模的场景
    """

    def forecast_with_prophet(self, sku_id: str, history: pd.DataFrame,
    periods: int = 30) -> pd.DataFrame:
    """Prophet单SKU预测,适合TOP 3000高频SKU"""
    df = history[['ds', 'y']].copy()

    model = Prophet(
    growth='linear',
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    changepoint_prior_scale=0.05,
    seasonality_prior_scale=10.0,
    holidays=self._build_holidays_df(), # 618/双11/年货节
    interval_width=0.90 # 90%置信区间
    )

    # 添加促销事件作为额外回归器
    df['is_promotion'] = history['is_promotion'].values
    model.add_regressor('is_promotion', mode='additive')

    model.fit(df)

    future = model.make_future_dataframe(periods=periods, freq='D')
    future['is_promotion'] = self._get_future_promotions(periods)

    forecast = model.predict(future)

    # 返回预测分位数(用于安全库存计算)
    return forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(periods)

    def forecast_with_deepar(self, sku_list: List[str],
    history_dict: Dict[str, pd.DataFrame],
    periods: int = 30) -> Dict[str, pd.DataFrame]:
    """DeepAR多SKU联合预测,适合长尾SKU"""
    # 构建GluonTS数据集
    from gluonts.dataset.common import ListDataset

    train_data = []
    for sku_id in sku_list:
    series = history_dict[sku_id]
    train_data.append({
    'start': series['ds'].min(),
    'target': series['y'].values,
    'feat_static_cat': [self._get_category_id(sku_id)],
    'item_id': sku_id
    })

    estimator = DeepAREstimator(
    freq='D',
    prediction_length=periods,
    context_length=min(90, len(series)),
    num_layers=3,
    num_cells=64,
    cell_type='lstm',
    dropout_rate=0.1,
    trainer=Trainer(epochs=100, learning_rate=1e-3)
    )

    predictor = estimator.train(ListDataset(train_data, freq='D'))

    results = {}
    for forecast in predictor.predict(ListDataset(train_data, freq='D')):
    sku_id = forecast.item_id
    samples = forecast.samples # shape: (num_samples, prediction_length)
    results[sku_id] = pd.DataFrame({
    'ds': pd.date_range(start=forecast.start_date, periods=periods, freq='D'),
    'yhat': samples.mean(axis=0),
    'yhat_lower': np.percentile(samples, 5, axis=0),
    'yhat_upper': np.percentile(samples, 95, axis=0),
    })

    return results

    2.2 ABC分类与安全库存

    def calculate_safety_stock(sku_id: str, forecast: pd.DataFrame,
    service_level: float = 0.95) -> dict:
    """
    安全库存 = Z_score × σ_demand × √(lead_time)

    – A类(前20% SKU,占80%销售额):service_level=0.99
    – B类(中30% SKU,占15%销售额):service_level=0.95
    – C类(后50% SKU,占5%销售额):service_level=0.85
    """
    abc_class = get_abc_class(sku_id)

    service_level_map = {'A': 0.99, 'B': 0.95, 'C': 0.85}
    sl = service_level_map.get(abc_class, 0.90)

    # Z-score from standard normal distribution
    from scipy.stats import norm
    z_score = norm.ppf(sl)

    # 需求标准差(从90%预测区间反推)
    sigma_demand = (forecast['yhat_upper'] – forecast['yhat_lower']).mean() / (2 * 1.645)

    # 补货提前期(天)
    lead_time = get_lead_time(sku_id)

    safety_stock = z_score * sigma_demand * np.sqrt(lead_time)

    return {
    'sku_id': sku_id,
    'abc_class': abc_class,
    'service_level': sl,
    'avg_daily_demand': forecast['yhat'].mean(),
    'safety_stock': int(np.ceil(safety_stock)),
    'reorder_point': int(np.ceil(forecast['yhat'].mean() * lead_time + safety_stock)),
    'suggested_order_qty': int(np.ceil(max(
    forecast['yhat'].sum() – get_current_stock(sku_id) + safety_stock, 0
    )))
    }

    三、拣货路径的TSP优化

    一张拣货单通常包含15-30个SKU,拣货员需要走过所有这些库位。这就是经典的TSP(旅行商问题)。

    @Service
    public class PickingPathOptimizer {

    /**
    * 使用Lin-Kernighan启发式算法(LKH)求解TSP
    * 对于20个拣货点,LKH能在100ms内找到最优解
    */
    public PickingRoute optimize(List<PickLocation> locations, Location startPoint) {
    int n = locations.size();

    // 构建距离矩阵(实际行走距离,非欧氏距离)
    double[][] distanceMatrix = buildDistanceMatrix(locations);

    // LKH求解
    LKHSolver solver = new LKHSolver(distanceMatrix);
    int[] tour = solver.solve();

    // 转换为实际路径
    List<PickLocation> optimalPath = new ArrayList<>();
    optimalPath.add(startPoint); // 起点:拣货台
    for (int idx : tour) {
    optimalPath.add(locations.get(idx));
    }
    optimalPath.add(startPoint); // 回到拣货台

    double totalDistance = calculateTotalDistance(optimalPath, distanceMatrix);

    return new PickingRoute(optimalPath, totalDistance,
    estimatePickingTime(totalDistance, locations.size()));
    }

    /**
    * 构建仓库实际行走距离矩阵
    * 关键:库位之间的实际距离 ≠ 欧氏距离
    * 需要考虑通道走向、单向通道、楼梯/货梯
    */
    private double[][] buildDistanceMatrix(List<PickLocation> locations) {
    int n = locations.size();
    double[][] matrix = new double[n][n];

    for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
    PickLocation a = locations.get(i);
    PickLocation b = locations.get(j);

    // 同通道内:直接距离
    if (a.getAisleId().equals(b.getAisleId())) {
    matrix[i][j] = Math.abs(a.getShelfPosition() – b.getShelfPosition());
    } else {
    // 跨通道:走到通道口 + 横向移动 + 走到目标位置
    matrix[i][j] = crossAisleDistance(a, b);
    }
    matrix[j][i] = matrix[i][j];
    }
    }
    return matrix;
    }
    }

    3.1 优化效果

    在真实仓库数据上的测试:

    指标按库位顺序贪心最近邻LKH优化
    单次拣货行走距离 850m 620m 480m
    拣货时间 28min 21min 16min
    每人日行走步数 32000 24000 18500
    每人日完成拣货单 16张 22张 29张

    LKH优化后每人日均拣货效率提升81%,人力成本节省约40%。

    四、AGV调度的多智能体协调

    50台AGV在仓库中运行,核心挑战是死锁避免和路径冲突解决。

    @Service
    public class AGVScheduler {

    // 仓库地图:将仓库建模为网格图
    private final GridMap warehouseMap;
    // 每台AGV的预留路径(时间维度的路径占用)
    private final Map<String, ReservedPath> reservedPaths = new ConcurrentHashMap<>();

    /**
    * 基于时空A*的AGV路径规划
    * 关键:路径不仅包含空间坐标,还包含时间维度
    * 这样不同AGV可以在不同时间使用同一空间位置
    */
    public AGVPath planPath(String agvId, Location from, Location to) {
    // 时空A*:状态 = (x, y, t)
    PriorityQueue<STNode> openSet = new PriorityQueue<>(
    Comparator.comparingDouble(n -> n.gCost + n.hCost)
    );

    Map<String, STNode> closedSet = new HashMap<>();

    STNode start = new STNode(from.x, from.y, currentTime(), 0, heuristic(from, to));
    openSet.add(start);

    while (!openSet.isEmpty()) {
    STNode current = openSet.poll();
    String key = current.key();

    if (closedSet.containsKey(key)) continue;
    closedSet.put(key, current);

    // 到达目标位置
    if (current.x == to.x && current.y == to.y) {
    return reconstructPath(current);
    }

    // 扩展邻居(5个动作:上下左右 + 等待)
    for (Action action : ACTIONS) {
    int nx = current.x + action.dx;
    int ny = current.y + action.dy;
    long nt = current.time + action.duration;

    // 边界检查
    if (!warehouseMap.isValid(nx, ny)) continue;

    // 冲突检查:该位置在该时间是否已被其他AGV预留
    if (isReserved(nx, ny, nt, agvId)) continue;

    double ng = current.gCost + action.cost;
    STNode neighbor = new STNode(nx, ny, nt, ng, heuristic(nx, ny, to));
    openSet.add(neighbor);
    }
    }

    throw new NoPathFoundException(from, to);
    }

    /**
    * 死锁检测与恢复
    */
    @Scheduled(fixedDelay = 1000)
    public void detectDeadlock() {
    // 构建等待图:AGV A等待AGV B释放资源
    WaitGraph graph = new WaitGraph();
    for (AGV agv : agvRegistry.getAllActive()) {
    if (agv.isWaiting()) {
    List<String> blockingAgvs = findBlockingAgvs(agv);
    for (String blocker : blockingAgvs) {
    graph.addEdge(agv.getId(), blocker);
    }
    }
    }

    // 检测环 → 死锁
    List<List<String>> cycles = graph.findCycles();
    for (List<String> cycle : cycles) {
    // 死锁恢复:让优先级最低的AGV重新规划路径
    String victim = selectVictim(cycle);
    agvRegistry.get(victim).replanPath();
    log.warn("死锁检测到,牺牲AGV: {}, 死锁环: {}", victim, cycle);
    }
    }
    }

    五、总结

    智能仓储的AI落地有三个核心认知:

  • 预测模型的选择要看SKU规模,不是算法前沿程度。TOP 3000的高频SKU用Prophet就够了,可解释性强、调参简单,业务方看得懂;15万SKU的长尾才需要DeepAR联合建模,用相似SKU的信息补偿数据稀疏性。

  • 拣货路径优化的ROI是所有仓储AI项目中最高的。LKH求解器100ms就能给出近优路径,实施成本几乎为零(纯软件),直接效果是拣货员日行走距离减少40%——人力成本是仓储最大的成本项。

  • AGV调度的复杂度不在单机路径规划,而在多机协同。时空A*解决了单AGV的路径规划,但50台AGV的全局最优涉及死锁避免、冲突消解、任务分配的多维博弈。我们的经验是:宁可牺牲单AGV的路径最优性,也要保证全局无死锁。

  • 最终效果:库存周转天数从45天降到28天,拣货效率提升81%,AGV利用率从62%提升到88%。仓储AI不是炫技,是每平方米、每分钟、每一步的优化累积。

    赞(0)
    未经允许不得转载:171主机测评 » AI在智能仓储中的应用:从库存预测到拣货路径优化的技术复盘
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址