自動配車デモ 関東限定

複数の配送先を複数の車両に自動で割り当て、時間窓・シフト・連続運転 4 時間ごとの休憩(430 制約)を考慮した配車計画を作ります。 Cloudflare Workers 単体で動く軽量版です。

配車計画デモを開く →

できること

デモの制約

仕組み

ジオコーディング国土地理院の住所検索 API で住所→座標。郵便番号は 郵便番号検索 API で住所に引き直してから変換
距離行列OSRM 公開サーバーの table API。首都圏の実測に基づく補正係数(都心 ×2.2 など)を掛けて渋滞込みの所要時間にする
ソルバーSolomon 挿入法 + relocate / swap / 2-opt の局所探索。30 件 × 5 台を数 ms で解く
430 休憩最適化後に累積運転 4 時間で 30 分休憩を挿入し、時刻を再計算。時間窓を割れば制約違反として報告

本番構成(NestJS + VROOM + セルフホスト OSRM / Nominatim + PostgreSQL)は address-engine。本デモは同じ API 契約を Workers 上で再実装したものです。

API

メソッドパス内容
POST/api/v1/dispatch/plan配車計画の作成(address-engine と同じリクエスト/レスポンス)
GET/api/v1/geocode?q=住所住所の座標候補(国土地理院)
GET/api/v1/healthヘルスチェック

リクエスト例

curl -X POST "https://dispatch.tenajimi.com/api/v1/dispatch/plan" \
  -H "Content-Type: application/json" \
  -d '{
    "vehicles": [
      { "id": "1号車", "depot": { "type": "address", "address": "東京都大田区平和島6-1-1" },
        "shiftStart": "2026-09-21T08:00:00+09:00", "shiftEnd": "2026-09-21T17:00:00+09:00" }
    ],
    "jobs": [
      { "id": "渋谷", "location": { "type": "address", "address": "東京都渋谷区宇田川町1-1" },
        "serviceMinutes": 10, "timeWindow": { "earliest": "2026-09-21T10:00:00+09:00", "latest": "2026-09-21T12:00:00+09:00" } },
      { "id": "品川", "location": { "type": "coordinate", "latitude": 35.6092, "longitude": 139.7301 }, "serviceMinutes": 10 },
      { "id": "丸の内", "location": { "type": "postalCode", "postalCode": "1000005" }, "serviceMinutes": 10 }
    ],
    "scheduleTightness": "normal"
  }'

レスポンス(抜粋)

{
  "status": "OK",
  "routes": [{
    "vehicleId": "1号車",
    "steps": [
      { "type": "start", "arrival": "2026-09-21T08:00:00+09:00", "departure": "2026-09-21T08:00:00+09:00", "location": {...} },
      { "type": "job", "jobId": "品川", "arrival": "2026-09-21T08:31:00+09:00", "departure": "2026-09-21T08:41:00+09:00", "serviceMinutes": 10 },
      { "type": "job", "jobId": "渋谷", "arrival": "2026-09-21T09:12:00+09:00", "departure": "2026-09-21T10:10:00+09:00", "waitingMinutes": 48 },
      { "type": "end", "arrival": "2026-09-21T11:05:00+09:00" }
    ],
    "summary": { "totalDistanceMeters": 41200, "totalDurationSeconds": 11100, "drivingSeconds": 7300, "waitingSeconds": 2880, "breakSeconds": 0, "jobCount": 3 },
    "geometry": [[35.5787, 139.7476], ...]
  }],
  "unassigned": [],
  "summary": { "totalVehiclesUsed": 1, "totalJobs": 3, "assignedJobs": 3, "unassignedJobs": 0, ... },
  "constraintViolations": [],
  "warnings": ["郵便番号 1000005 は町域レベル(東京都千代田区丸の内)の座標で解決しました"],
  "decisionSummary": "3件の配送先を1台の車両に割り当てました。この結果は推奨解であり、最適解を保証するものではありません。",
  "meta": { "matrixProvider": "osrm-public", "geocodingProvider": "gsi", "solveMillis": 1 }
}

vehicles[].enable430Rule(既定 true)、jobs[].priority(0〜100、高いほど優先)、 scheduleTightness(very-tight / tight / normal / relaxed / very-relaxed)も指定できます。