mcpField
O
order-batching-optimization

order-batching-optimization

github.com/kishorkukrejaその他8446 回インストール3 週間前に更新
リポジトリ

order-batching-optimization について

倉庫の運用・計画担当者向けの、注文バッチ最適化スキルです。ピッキング方式や制約を評価し、類似度クラスタリング、ヒューリスティック、混合整数計画、動的調整を用いたバッチ戦略を検討します。

主な機能

  1. 01ピッキング方式と倉庫制約の評価
  2. 02注文バッチ戦略の比較
  3. 03SKU・位置の類似度によるクラスタリング
  4. 04ヒューリスティックと整数計画の適用
  5. 05動的バッチと仕分けコストの考慮

活用シーン

  1. 01注文バッチ間の歩行距離の削減
  2. 02注文規模と優先度に応じたバッチ設定
  3. 03バッチ容量と仕分け作業量の調整

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill order-batching-optimization

インストール後、Claude Code で/order-batching-optimizationを入力して読み込まれたか確認します。

詳細

注文バッチングの最適化

倉庫における注文のバッチングとピッキングの最適化に精通しています。ピッカーの移動距離を最小限に抑え、ピッキング効率を最大化し、注文のサイクルタイムを短縮して、倉庫全体の生産性を高める最適なバッチに注文をまとめることが目標です。

初期評価

注文のバッチングを最適化する前に、次の項目を把握します。

  1. ピッキング方式

    • ディスクリートピッキング(1回に1件の注文を処理)ですか?
    • バッチピッキング(複数の注文を処理)ですか?
    • ゾーンピッキング(次のゾーンへ引き渡す方式)ですか?
    • クラスターピッキング(カートへのピッキング)ですか?
    • ピック・トゥ・ライト方式または音声ピッキングですか?
  2. 注文の特性

    • 1件の注文あたりの平均明細数はどのくらいですか?
    • 注文サイズの分布(単一明細と複数明細)はどうなっていますか?
    • 注文間で SKU はどの程度重複していますか?
    • 注文の優先度にはどのような段階がありますか?
    • 1日の注文量はどのくらいですか?
  3. 倉庫の構成

    • 倉庫のレイアウト(グリッド型、斜め型、不規則型)はどのようになっていますか?
    • 通路とピッキング間口はいくつありますか?
    • ピッキングカートの容量(注文数とユニット数)はどのくらいですか?
    • バッチ処理後の仕分け方法(手作業、自動化)は何ですか?
    • 前方ピッキングロケーションと予備ロケーションはどうなっていますか?
  4. 現在のパフォーマンス

    • 現在の1時間あたりのピッキング数はどのくらいですか?
    • 注文1件あたりのピッカーの移動距離はどのくらいですか?
    • 使用しているバッチサイズはどのくらいですか?
    • バッチごとの仕分け時間はどのくらいですか?
    • ピッキングミスや仕分けミスの発生率はどのくらいですか?

注文バッチングのフレームワーク

バッチング戦略

1. ディスクリートピッキング(バッチングなし)

  • 1回の移動につき1件の注文を処理
  • メリット:シンプルで、仕分けが不要。エラー率が低い
  • デメリット:移動距離が長く、効率が低い
  • 用途:高額注文、複雑な注文、単品ピッキングのみの場合

2. バッチピッキング

  • 複数の注文を同時にピッキング
  • メリット:移動距離を削減(60〜80%削減)し、生産性を向上
  • デメリット:仕分けが必要で、より複雑
  • 用途:標準的な倉庫業務

3. ゾーンバッチピッキング

  • 注文をバッチ化する一方、各ピッカーは1つのゾーンを担当
  • トートやカートを次のゾーンに引き渡す
  • メリット:バッチが小さく、作業負荷のバランスを取りやすい
  • デメリット:受け渡し地点が必要で、調整が必要

4. クラスターピッキング(カートへのピッキング)

  • 複数の仕切りがあるカート(例:4〜8件の注文分)
  • 注文コンテナに直接ピッキング
  • メリット:仕分けが不要で、効率は中程度
  • デメリット:カートの容量と注文サイズのばらつきによる制約がある

5. ウェーブレスバッチング

  • 注文が届くたびに継続的にバッチ化
  • 固定されたウェーブ時間なし
  • メリット:サイクルタイムを短縮し、状況に応じて対応可能
  • デメリット:高度な WMS が必要
バッチングの目的
Primary Goals:
1. Minimize total picker travel distance
2. Maximize picks per hour
3. Balance batch sizes (avoid very small/large)
4. Minimize sort time and errors
5. Meet order cutoff times

Constraints:
- Cart capacity (units and orders)
- Sorting capacity
- Time windows (priority orders)
- Zone limitations

数学的定式化

クラスタリング問題としてのバッチング

決定変数:

  • x[o,b] = 注文 o がバッチ b に割り当てられている場合は 1、それ以外は 0
  • y[b] = バッチ b を使用する場合は 1、それ以外は 0

パラメーター:

  • S[o,o'] = 注文 o と o' の類似度スコア(SKU の重複度、近接性)
  • D[o] = 注文 o を単独で処理した場合の総移動距離
  • L[o] = 注文 o の明細数
  • C_max = バッチあたりの最大容量(注文数または明細数)

目的関数:

Minimize:
  Total Travel Distance + Penalty for unbalanced batches

Formally:
  Σ Σ (travel_distance[b] × y[b])
  + α × Σ (deviation from ideal batch size)
  + β × (number of batches)

where travel_distance[b] is calculated from batched order locations

制約条件:

# 1. Each order in exactly one batch
for o in orders:
    Σ x[o,b] = 1  for all b

# 2. Batch capacity (orders)
for b in batches:
    Σ x[o,b] ≤ C_max_orders  for all o

# 3. Batch capacity (lines)
for b in batches:
    Σ (L[o] × x[o,b]) ≤ C_max_lines  for all o

# 4. Link batch usage to order assignment
for b in batches:
    for o in orders:
        x[o,b] ≤ y[b]

# 5. Time window constraints (priority orders)
for o in priority_orders:
    for b in batches:
        if x[o,b] = 1:
            completion_time[b] ≤ deadline[o]
類似度に基づくクラスタリング

一緒にバッチ化する注文は類似度が高くなります。

Similarity(order_i, order_j) =
  α × (SKU overlap / total unique SKUs)
  + β × (1 - distance between order centroids / max_distance)
  + γ × (time window compatibility)

Higher similarity → Better batch candidates

バッチングアルゴリズム

貪欲法によるシードベースのバッチング
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform

def seed_based_batching(orders, locations, max_batch_size=6):
    """
    Greedy batching using seed orders

    Algorithm:
    1. Select "seed" order (e.g., most lines, earliest deadline)
    2. Add most similar orders until capacity reached
    3. Repeat for remaining orders

    Parameters:
    -----------
    orders : DataFrame
        Columns: order_id, sku_list, priority, deadline
    locations : dict
        {sku: (x, y) location}
    max_batch_size : int
        Maximum orders per batch

    Returns:
    --------
    Batch assignments
    """

    # Calculate order similarity matrix
    similarity_matrix = calculate_order_similarity(orders, locations)

    remaining_orders = set(orders['order_id'])
    batches = []
    batch_id = 1

    while remaining_orders:
        # Select seed order (highest priority remaining)
        seed_candidates = orders[orders['order_id'].isin(remaining_orders)]
        seed_order = seed_candidates.sort_values(
            ['priority', 'deadline'],
            ascending=[False, True]
        ).iloc[0]['order_id']

        # Initialize batch with seed
        batch = [seed_order]
        remaining_orders.remove(seed_order)

        # Add similar orders to batch
        while len(batch) < max_batch_size and remaining_orders:
            # Find most similar remaining order
            best_order = None
            best_similarity = -1

            for candidate in remaining_orders:
                # Average similarity to all orders in batch
                avg_similarity = np.mean([
                    similarity_matrix.loc[candidate, b_order]
                    for b_order in batch
                ])

                if avg_similarity > best_similarity:
                    best_similarity = avg_similarity
                    best_order = candidate

            if best_order and best_similarity > 0.3:  # Threshold
                batch.append(best_order)
                remaining_orders.remove(best_order)
            else:
                break  # No good candidates

        batches.append({
            'batch_id': batch_id,
            'orders': batch,
            'num_orders': len(batch),
            'seed_order': seed_order
        })
        batch_id += 1

    return pd.DataFrame(batches)


def calculate_order_similarity(orders, locations):
    """
    Calculate pairwise similarity between orders

    Similarity based on:
    - SKU overlap (Jaccard similarity)
    - Spatial proximity of pick locations
    """

    order_ids = orders['order_id'].tolist()
    n = len(order_ids)
    similarity = np.zeros((n, n))

    for i, order_i_id in enumerate(order_ids):
        for j, order_j_id in enumerate(order_ids):
            if i == j:
                similarity[i, j] = 1.0
                continue

            order_i = orders[orders['order_id'] == order_i_id].iloc[0]
            order_j = orders[orders['order_id'] == order_j_id].iloc[0]

            # SKU overlap (Jaccard)
            skus_i = set(order_i['sku_list'])
            skus_j = set(order_j['sku_list'])

            intersection = len(skus_i & skus_j)
            union = len(skus_i | skus_j)
            jaccard = intersection / union if union > 0 else 0

            # Spatial proximity (simplified: centroid distance)
            centroid_i = calculate_order_centroid(order_i['sku_list'], locations)
            centroid_j = calculate_order_centroid(order_j['sku_list'], locations)

            distance = np.linalg.norm(np.array(centroid_i) - np.array(centroid_j))
            max_distance = 500  # warehouse size
            proximity = 1 - min(distance / max_distance, 1)

            # Combined similarity
            similarity[i, j] = 0.6 * jaccard + 0.4 * proximity

    return pd.DataFrame(similarity, index=order_ids, columns=order_ids)


def calculate_order_centroid(sku_list, locations):
    """Calculate centroid of pick locations for an order"""
    coords = [locations.get(sku, (0, 0)) for sku in sku_list if sku in locations]
    if not coords:
        return (0, 0)
    return (np.mean([c[0] for c in coords]), np.mean([c[1] for c in coords]))


# Example usage
orders = pd.DataFrame({
    'order_id': [f'ORD{i:03d}' for i in range(1, 21)],
    'sku_list': [
        np.random.choice(['SKU_A', 'SKU_B', 'SKU_C', 'SKU_D', 'SKU_E',
                         'SKU_F', 'SKU_G', 'SKU_H', 'SKU_I', 'SKU_J'],
                        size=np.random.randint(1, 8), replace=False).tolist()
        for _ in range(20)
    ],
    'priority': np.random.choice([1, 2, 3], 20),
    'deadline': pd.date_range('2024-01-01 16:00', periods=20, freq='30T')
})

locations = {
    'SKU_A': (10, 20), 'SKU_B': (15, 25), 'SKU_C': (50, 30),
    'SKU_D': (55, 35), 'SKU_E': (80, 40), 'SKU_F': (85, 45),
    'SKU_G': (20, 60), 'SKU_H': (25, 65), 'SKU_I': (60, 70),
    'SKU_J': (65, 75)
}

batches = seed_based_batching(orders, locations, max_batch_size=6)

print("Order Batching Results:")
print(f"Total Batches: {len(batches)}")
for _, batch in batches.iterrows():
    print(f"Batch {batch['batch_id']}: {batch['num_orders']} orders")
    print(f"  Orders: {', '.join(batch['orders'])}")
バッチングのための K-means クラスタリング
from sklearn.cluster import KMeans

def kmeans_batching(orders, locations, num_batches):
    """
    Use K-Means clustering to batch orders

    Cluster based on spatial and temporal features

    Parameters:
    -----------
    orders : DataFrame
        Order data with sku_list, priority, deadline
    locations : dict
        SKU locations
    num_batches : int
        Target number of batches

    Returns:
    --------
    Batch assignments
    """

    # Feature engineering
    features = []

    for idx, order in orders.iterrows():
        # Spatial feature: order centroid
        centroid = calculate_order_centroid(order['sku_list'], locations)

        # Temporal feature: deadline urgency (hours until deadline)
        hours_until_deadline = (
            (order['deadline'] - pd.Timestamp.now()).total_seconds() / 3600
        )

        # Size feature: number of lines
        num_lines = len(order['sku_list'])

        # Combine features (normalized)
        features.append([
            centroid[0] / 100,  # Normalize by warehouse size
            centroid[1] / 100,
            hours_until_deadline / 24,  # Normalize to days
            num_lines / 10  # Normalize by typical order size
        ])

    features = np.array(features)

    # K-Means clustering
    kmeans = KMeans(n_clusters=num_batches, random_state=42)
    cluster_labels = kmeans.fit_predict(features)

    orders['batch_id'] = cluster_labels + 1

    # Group into batches
    batches = []
    for batch_id in range(1, num_batches + 1):
        batch_orders = orders[orders['batch_id'] == batch_id]['order_id'].tolist()

        if batch_orders:
            batches.append({
                'batch_id': batch_id,
                'orders': batch_orders,
                'num_orders': len(batch_orders),
                'centroid': kmeans.cluster_centers_[batch_id - 1]
            })

    return pd.DataFrame(batches)


# Example
batches_kmeans = kmeans_batching(orders, locations, num_batches=4)

print("\nK-Means Batching Results:")
for _, batch in batches_kmeans.iterrows():
    print(f"Batch {batch['batch_id']}: {batch['num_orders']} orders")
最適化モデル:MIP に基づくバッチング
from pulp import *

def optimize_order_batching(orders, travel_distances, max_orders_per_batch=8,
                           max_lines_per_batch=100):
    """
    Optimal order batching using Mixed-Integer Programming

    Parameters:
    -----------
    orders : list
        Order identifiers
    travel_distances : dict
        {batch_composition: total_distance}
        Precomputed for all possible batch combinations
    max_orders_per_batch : int
        Cart capacity (orders)
    max_lines_per_batch : int
        Cart capacity (lines)

    Returns:
    --------
    Optimal batching
    """

    # For tractability, use a simplified model
    # In practice, would use heuristics to generate candidate batches
    # then solve assignment problem

    prob = LpProblem("Order_Batching", LpMinimize)

    # Generate candidate batches (simplified: all pairs and triples)
    candidate_batches = []
    batch_id = 0

    # Single-order batches
    for o in orders:
        candidate_batches.append({
            'batch_id': batch_id,
            'orders': [o],
            'distance': travel_distances.get((o,), 100)
        })
        batch_id += 1

    # Pair batches
    for i, o1 in enumerate(orders):
        for o2 in orders[i+1:]:
            candidate_batches.append({
                'batch_id': batch_id,
                'orders': [o1, o2],
                'distance': travel_distances.get((o1, o2), 150)
            })
            batch_id += 1

    batches = range(len(candidate_batches))

    # Decision variables
    # y[b] = 1 if batch b is used
    y = LpVariable.dicts("use_batch", batches, cat='Binary')

    # Objective: minimize total travel distance
    prob += lpSum([
        candidate_batches[b]['distance'] * y[b]
        for b in batches
    ]), "Total_Distance"

    # Constraints

    # Each order in exactly one batch
    for o in orders:
        prob += lpSum([
            y[b] for b in batches
            if o in candidate_batches[b]['orders']
        ]) == 1, f"Order_{o}"

    # Solve
    prob.solve(PULP_CBC_CMD(msg=0))

    # Extract solution
    selected_batches = []
    for b in batches:
        if y[b].varValue > 0.5:
            selected_batches.append(candidate_batches[b])

    return {
        'status': LpStatus[prob.status],
        'total_distance': value(prob.objective),
        'batches': selected_batches,
        'num_batches': len(selected_batches)
    }


# Example (simplified with mock distances)
orders_list = [f'ORD{i:03d}' for i in range(1, 11)]

# Mock travel distances
travel_distances = {
    (o,): np.random.randint(80, 120) for o in orders_list
}
for i, o1 in enumerate(orders_list):
    for o2 in orders_list[i+1:]:
        # Batched distance is less than sum of individual
        travel_distances[(o1, o2)] = int(
            travel_distances[(o1,)] + travel_distances[(o2,)] * 0.6
        )

result = optimize_order_batching(orders_list, travel_distances)

print(f"\nOptimization Status: {result['status']}")
print(f"Total Distance: {result['total_distance']:.0f}")
print(f"Number of Batches: {result['num_batches']}")
print("\nBatches:")
for batch in result['batches']:
    print(f"  Batch {batch['batch_id']}: {batch['orders']} "
          f"(distance: {batch['distance']})")

高度なバッチング手法

リアルタイム更新による動的バッチング
class DynamicBatcher:
    """
    Dynamic order batching system with real-time updates
    """

    def __init__(self, max_batch_size=6, max_wait_time=15):
        """
        Parameters:
        -----------
        max_batch_size : int
            Maximum orders per batch
        max_wait_time : int
            Maximum minutes to wait for batch to fill
        """
        self.max_batch_size = max_batch_size
        self.max_wait_time = max_wait_time
        self.pending_orders = []
        self.completed_batches = []
        self.current_batch_id = 1

    def add_order(self, order):
        """Add new order to pending queue"""
        order['received_time'] = datetime.now()
        self.pending_orders.append(order)

    def should_release_batch(self):
        """
        Determine if a batch should be released

        Release if:
        1. Batch size reached
        2. Oldest order exceeds max wait time
        3. High-priority order needs immediate processing
        """

        if len(self.pending_orders) == 0:
            return False

        # Check size threshold
        if len(self.pending_orders) >= self.max_batch_size:
            return True

        # Check wait time
        oldest_order = min(self.pending_orders,
                          key=lambda x: x['received_time'])
        wait_time = (datetime.now() - oldest_order['received_time']).total_seconds() / 60

        if wait_time >= self.max_wait_time:
            return True

        # Check for high-priority urgent orders
        priority_orders = [o for o in self.pending_orders if o.get('priority', 3) == 1]
        if priority_orders:
            # If priority order waiting > 5 min, release
            for po in priority_orders:
                wait = (datetime.now() - po['received_time']).total_seconds() / 60
                if wait >= 5:
                    return True

        return False

    def create_batch(self):
        """
        Create batch from pending orders using similarity-based grouping
        """

        if not self.pending_orders:
            return None

        # Sort by priority and received time
        sorted_orders = sorted(
            self.pending_orders,
            key=lambda x: (x.get('priority', 3), x['received_time'])
        )

        # Take up to max_batch_size orders
        batch_orders = sorted_orders[:self.max_batch_size]

        # Remove from pending
        for order in batch_orders:
            self.pending_orders.remove(order)

        batch = {
            'batch_id': self.current_batch_id,
            'orders': batch_orders,
            'created_time': datetime.now(),
            'num_orders': len(batch_orders)
        }

        self.completed_batches.append(batch)
        self.current_batch_id += 1

        return batch

    def optimize_pending_batches(self, locations):
        """
        Re-optimize pending orders into best batches

        Called periodically or when significant orders accumulated
        """

        if len(self.pending_orders) < 2:
            return

        # Create DataFrame from pending
        pending_df = pd.DataFrame(self.pending_orders)

        # Use seed-based batching
        batches_df = seed_based_batching(
            pending_df,
            locations,
            max_batch_size=self.max_batch_size
        )

        # Update pending with batch assignments
        for idx, row in pending_df.iterrows():
            order_id = row['order_id']
            # Find batch assignment
            for _, batch in batches_df.iterrows():
                if order_id in batch['orders']:
                    # Update order with batch hint
                    for pending_order in self.pending_orders:
                        if pending_order['order_id'] == order_id:
                            pending_order['suggested_batch'] = batch['batch_id']
                    break


# Example usage
batcher = DynamicBatcher(max_batch_size=6, max_wait_time=15)

# Simulate order arrivals
for i in range(15):
    order = {
        'order_id': f'ORD{i:03d}',
        'sku_list': np.random.choice(['SKU_A', 'SKU_B', 'SKU_C'],
                                    size=np.random.randint(1, 5),
                                    replace=False).tolist(),
        'priority': np.random.choice([1, 2, 3]),
        'deadline': datetime.now() + timedelta(hours=4)
    }
    batcher.add_order(order)

# Check if should release
if batcher.should_release_batch():
    batch = batcher.create_batch()
    print(f"Released Batch {batch['batch_id']}:")
    print(f"  Orders: {len(batch['orders'])}")
    for order in batch['orders']:
        print(f"    {order['order_id']}: {order['sku_list']}")

print(f"\nPending Orders: {len(batcher.pending_orders)}")
バッチングと仕分けの最適化
def optimize_batch_and_sort(batch_orders, sort_stations=4):
    """
    Optimize batch picking with downstream sorting

    Minimize: Pick time + Sort time

    Parameters:
    -----------
    batch_orders : list
        Orders in the batch
    sort_stations : int
        Number of parallel sort stations

    Returns:
    --------
    Optimized pick sequence and sort assignments
    """

    # Calculate pick route (TSP-style)
    # Simplified: assume pre-calculated

    # Calculate sort time based on item distribution
    total_items = sum(len(o['sku_list']) for o in batch_orders)

    # Sort time depends on:
    # 1. Number of unique SKUs (more touchpoints)
    # 2. Number of orders (more destinations)
    # 3. Sort method (manual, automated)

    # Manual sort time estimation (seconds per item)
    items_per_order = total_items / len(batch_orders)

    if items_per_order <= 2:
        sort_time_per_item = 3  # Simple, few items per order
    elif items_per_order <= 5:
        sort_time_per_item = 5  # Moderate complexity
    else:
        sort_time_per_item = 8  # Complex, many items per order

    total_sort_time = total_items * sort_time_per_item / sort_stations

    # Pick time estimation (assuming 100 picks/hour = 36 sec/pick)
    pick_time_per_line = 36
    total_pick_time = total_items * pick_time_per_line

    # Total time
    total_time = total_pick_time + total_sort_time

    return {
        'pick_time': total_pick_time,
        'sort_time': total_sort_time,
        'total_time': total_time,
        'efficiency': total_items / (total_time / 3600)  # items per hour
    }


# Example
batch = [
    {'order_id': 'ORD001', 'sku_list': ['A', 'B', 'C']},
    {'order_id': 'ORD002', 'sku_list': ['A', 'D']},
    {'order_id': 'ORD003', 'sku_list': ['B', 'C', 'D', 'E']},
]

result = optimize_batch_and_sort(batch, sort_stations=2)
print("\nBatch-and-Sort Analysis:")
print(f"Pick Time: {result['pick_time']:.0f} seconds")
print(f"Sort Time: {result['sort_time']:.0f} seconds")
print(f"Total Time: {result['total_time']:.0f} seconds")
print(f"Efficiency: {result['efficiency']:.1f} items/hour")

ツールとライブラリ

注文バッチングソフトウェア

バッチング機能を備えた倉庫管理システム:

  • Manhattan WMS:高度なバッチングアルゴリズム
  • Blue Yonder (JDA) WMS:AI による最適化バッチング
  • SAP EWM:バッチ決定ルール
  • HighJump WMS:複数注文のピッキング戦略
  • Körber WMS:動的なバッチ作成

専門的な最適化:

  • Optislot:スロッティングとバッチングの最適化
  • Wise Systems:ラストマイル向けの動的なルーティングとバッチング
  • Lucas Systems:音声指示によるバッチピッキングの最適化
Python ライブラリ
# Optimization
from pulp import *
from scipy.optimize import linear_sum_assignment
from ortools.constraint_solver import pywrapcp

# Clustering
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage

# Distance Calculations
from scipy.spatial.distance import pdist, squareform, jaccard
import networkx as nx  # For TSP routing within batch

# Analysis
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

よくある課題と解決策

課題:仕分け時間が長い

問題:

  • 仕分けにピッキングより時間がかかります
  • 仕分けミスや誤ピッキングが発生します
  • 仕分けステーションがボトルネックになっています

解決策:

  • バッチサイズを小さくします(仕分けの複雑さを軽減)
  • 代わりにクラスターピッキングを使用します(カートへのピッキングで、仕分けが不要です)
  • 仕分けを自動化します(プット・トゥ・ライト、自動仕分け機)
  • ピッキング中に事前仕分けします(注文ごとに番号付きトートを使用)
  • 仕分け時に混在しないよう、注文ごとにピッキング順を調整します
  • 仕分けステーションまたはレーンを増やします
課題:注文サイズにばらつきがある

問題:

  • 1行の注文と20行の注文が混在しています
  • 小口注文ではバッチ容量が無駄になります
  • 大口注文は他の注文とバッチ化できません

解決策:

  • 注文サイズごとにバッチ化戦略を分けます
    • 小口注文(1~3行):大きなバッチ(10~12件)
    • 中口注文(4~10行):標準的なバッチ(4~6件)
    • 大口注文(10行超):個別ピッキングまたは小さなバッチ
  • 注文単位ではなく、行数ベースでバッチ化します
  • 注文の構成に応じてバッチサイズを動的に調整します
課題:SKUの重複率が低い

問題:

  • 注文ごとに異なるSKUが含まれており、重複率が低いです
  • バッチ化しても移動距離があまり短縮されません
  • 個別ピッキングに近い効率です

解決策:

  • SKUの重複より、空間的なクラスタリング(ゾーンの近接性)を重視します
  • 空間的な適合性を高め、小さなバッチ(2~3件)を使用します
  • 代わりにゾーンピッキングを導入します(各ゾーンが注文の一部を担当)
  • GTPシステムを検討します(いずれにせよ移動によるメリットがありません)
  • スロッティングを改善し、回転の速い商品を集約します
課題:優先注文との競合

問題:

  • 優先度の高い注文がバッチ作業を中断します
  • 緊急のため、バッチが揃うまで待てません
  • 急ぎの注文と通常注文が混在しています

解決策:

  • 優先注文用のレーンを分けます(サイズを1または2とするエクスプレスバッチ)
  • 優先注文専任のピッカーを確保します
  • バッチのリリースを動的に行います(優先注文があれば、バッチが揃うまで待ちません)
  • 優先度に応じて異なるピッキング方式を使用します(通常注文はゾーン、優先注文は個別)
  • 急ぎの注文が事前に分かっている場合、在庫を事前に割り当てます
課題:カート容量の制限

問題:

  • 物理カートにはトートを6個しか載せられません
  • 設備の制約により、それ以上の注文をバッチ化できません
  • 重量や容積の制限があります

解決策:

  • 複数種類のカートを用意します(トート4個、8個、12個用)
  • 使用可能なカートの種類に応じてバッチを組みます
  • 2回に分けてピッキングします(大きなバッチを複数回に分けます)
  • 電動カートまたはAGVを使用します(容量を増やせます)
  • コンベヤーを使ったゾーンピッキングを導入します(容量に制限がありません)

出力形式

注文バッチ化レポート

バッチ最適化の概要 - 2024年1月15日

パフォーマンス比較:

指標個別ピッキング現在のバッチ化最適化後のバッチ化
バッチあたりの平均注文数1.04.25.8
注文あたりの平均移動距離(ft)425185142
移動距離の削減率0%56%67%
1時間あたりのピッキング数65145175
注文あたりの仕分け時間(分)02.52.1
サイクルタイム合計(分)241412

バッチの詳細:

Batch 001:
  Orders: 6
  Total Lines: 42
  SKU Overlap: 68%
  Estimated Travel: 620 ft
  Estimated Pick Time: 24 min
  Estimated Sort Time: 12 min
  Priority Orders: 1 (ORD0045)

  Order Breakdown:
    ORD0042: 8 lines, Zone A (4), Zone B (3), Zone C (1)
    ORD0043: 6 lines, Zone A (3), Zone B (2), Zone C (1)
    ORD0045: 9 lines [PRIORITY], Zone A (5), Zone B (3), Zone C (1)
    ORD0047: 5 lines, Zone A (2), Zone B (2), Zone C (1)
    ORD0048: 7 lines, Zone A (4), Zone B (2), Zone C (1)
    ORD0050: 7 lines, Zone A (3), Zone B (3), Zone C (1)

  Shared SKUs: SKU_A (6 orders), SKU_B (5 orders), SKU_D (4 orders)

バッチ化の統計:

  • 注文総数:120
  • バッチ総数:21
  • 平均バッチサイズ:5.7件
  • バッチサイズの分布:
    • 3件:2バッチ
    • 4件:4バッチ
    • 5件:6バッチ
    • 6件:7バッチ
    • 7件:2バッチ

移動距離の削減:

  • 総移動距離(個別):51,000 ft
  • 総移動距離(バッチ):16,850 ft
  • 削減量:34,150 ft(67%削減)

生産性への影響:

  • ピッキング作業時間(個別):30.8時間
  • ピッキング作業時間(バッチ):17.6時間
  • 労働時間の削減:13.2時間(43%削減)

推奨事項:

  1. 最適化されたバッチ化戦略を導入します
  2. ピーク時の処理能力を確保するため、仕分けステーションを2つ追加します
  3. 優先注文をエクスプレスバッチに分けます
  4. より大きなバッチ向けに、トートを8個載せられるカートを検討します

確認事項

さらに詳しい情報が必要な場合は、以下をご確認ください。

  1. 現在、どのピッキング方式を使用していますか?
  2. 注文の平均サイズ(行数)はどれくらいですか?
  3. ピッキング後に仕分けできる設備はありますか?
  4. カートの容量(注文数および商品数)はどれくらいですか?
  5. 1日の注文量はどれくらいですか?
  6. 優先注文や急ぎの注文はありますか?
  7. 倉庫のレイアウト(ゾーン、通路)はどのようになっていますか?
  8. 現在、1時間あたり何点をピッキングしていますか?

関連スキル

  • picker-routing-optimization:バッチ内のピッキング経路の最適化に使用します
  • wave-planning-optimization:ウェーブの設計とリリース戦略に使用します
  • warehouse-slotting-optimization:バッチ処理に影響する SKU の配置に使用します
  • traveling-salesman-problem:バッチ内の TSP ベースの経路計画に使用します
  • clustering-algorithms:注文の類似性の判定とグループ化に使用します
  • task-assignment-problem:ピッカーのバッチへの割り当てに使用します
  • order-fulfillment:フルフィルメントプロセス全体の設計に使用します

コメント