サービスロボット導入シミュレーション Log#24 | 配膳ロボットの試算が、混んだファミレスの実物に近づいた
この記事の素材(コード・ログ・データ 8ファイル・まとめてダウンロードあり)arrow_downward混んだファミレスでは、会計のレジを先に回すと食べ終えた卓の片付けが追いつかなくなり、42卓の店(1時間42組)に配膳ロボットを2台入れても、店を回すスタッフは3人から減らせませんでした。元ファミレス店員の実感に合わせて、配膳ロボットの試算(SimPy)を「会計のスタッフはレジに並んだ順に付く」形に直したところ、Unity の3Dのシミュレーションとの差は、普通の混み具合ではほぼなくなりました。
配膳ロボットを入れるか迷っている店や、ロボットを提案する会社が知りたいのは、「入れたら何人で回るのか」だと思います。Pixels to Motors では、その答えをサイトの試算(計算で出す待ち時間)と、Unity で建てた3Dの店(人とロボットが実際に通路を歩く)の両方で出しています。数字の正はサイトの試算で、Unity は通路で起きることを測る役です。ところが、混んだ日を Unity で一晩回すと、席は空いているのにお客さんを案内できない時間が、試算の3〜6倍になっていました。
原因は、スタッフの仕事の量ではありませんでした。調べると、試算と Unity で「会計のスタッフを呼ぶタイミング」が違っていました。試算では、レジの順番が来てからスタッフを呼んでいたので、会計が後回しになり、食べ終えたお客さんが卓に座ったまま会計を待っていました。卓が空かないので、片付けの仕事も入ってきません。Unity では、お客さんがレジに並んだら、すぐスタッフが付きます。会計がどんどん済むぶん片付けが追いつかず、汚れた卓がたまります。同じ詰まりが、試算では「会計待ち」、Unity では「片付け待ち」として見えていました。
どちらが実際の店に近いかは、ファミレスでフロントをしていた経験で決めました。
フロントスタッフやっていた頃、基本はAでレジや案内を優先に仕事していたね。
試算を v2.9 に直すと、LP の最初の画面の「店舗が回る人数 3人 → 2人」は「3人 → 3人」に変わりました。前の版で2人でも回っていたのは、お客さんが卓で24分会計を待っていたからでした。
| やったこと | なぜ | 結果 |
|---|---|---|
6店の混み具合を「空いている・混んでいる」に広げ、Unity で一晩360回回した | これまで Unity は「普通」しか回していなかった | 混んでいるとき、席は空いているのに待つ時間が試算の3〜6倍 |
スタッフの手がふさがった時間を、仕事ごとに分けて試算と比べた | 仕事の量の差か、取り方の差かを分けるため | 仕事の量は5〜10%しか違わない。客が帰ってから片付けにスタッフが付くまでが Unity 約10分・試算約20秒(③) |
Claude Code に Unity のスタッフの仕事の選び方を読んでもらった | 取り方のどこが違うかを知るため | 会計のスタッフを呼ぶタイミングが違っていた |
試算を v2.9 にし、全32,850条件を計算し直した | フロントの実感(レジと案内を優先)に合わせるため | 普通の混み具合で Unity とほぼ一致。「店が回らない」1,922 → 2,617条件、「レジが詰まる」5,372 → 2,626条件(レジ1つ) |
試算のページと LP を v2.9 にした | 人件費が浮かない結果も正直に見せるため | LP の最初の画面は 3人 → 3人、ひと月の差し引きは −14.4万円 |
配膳ロボットを自分の店に入れたら何人で回るかは、このシリーズの試算をもとにした別のページで試せます。
作業ログ ― 6店の混み具合を「空いている・混んでいる」に広げ、Unity で一晩360回回した
この記事のコード・ログ・データは、1ファイルずつ下に置いています。全文はリンク先にあります。
ファイルの一覧(8本)
01_sec01.md ― 依頼30「混み具合を広げて夜を回す」の全文・3,570字
02_sec01.md ― 依頼30 の結果(依頼箱の報告)・3,031字
03_sec01.txt ― Claude Code の報告 20261106_night_busy.txt の全文・40,508字
04_staff_breakdown.py.txt ― 仕事ごとの時間を比べた staff_breakdown.py・6,223字
05_sec03.txt ― Claude Code の報告 20261030_0823_staff_job_choice.txt の全文・32,636字
06_sec05.txt ― haizen_sim.py の v2.8 から v2.9 への差分・3,689字
07_sec05.py.txt ― 試算 v2.9 の haizen_sim.py の全文・33,820字
08_quick_compare_unity54.txt ― quick_compare_unity54.txt(直した後の v2.9)・3,109字
サイトの試算・Unity・依頼箱は、このシリーズで何をしているか
このシリーズでは、同じファミレスを2つの道具で回しています。
| 言葉 | 何か | このシリーズでの使い方 |
|---|---|---|
| サイトの試算 | Python の SimPy(出来事を時刻の順に並べて進める、待ち行列のシミュレーションの道具)で書いた計算( haizen_sim.py) | 店・スタッフの人数・ロボットの台数・混み具合の組み合わせ32,850通りを、それぞれ40回回した平均を 試算のページ に出している。数字の正 |
| Unity | ゲームなどを作る3Dの開発環境(Unity) | サイトの店の形どおりに店を建て、スタッフ・お客さん・ロボットを通路で歩かせて、止まる回数や待ち時間を測る |
| 依頼箱 | Claude が書いた依頼を、Mac の見張り係が Claude Code(コードを書いて動かす AI)に自動で渡す仕組み | Unity を動かす作業はすべてここから頼む。夜のあいだも続けて回せる |
| 種 | 乱数の出発点。同じ種なら同じお客さんの来方になる | Unity は1つの条件を種10個で回し、10回の中央値(真ん中の値)で見る |
| 店が回らない | 席は空いているのに、片付けや案内が追いつかず待った時間が平均10分以上 | サイトの試算の印。LP の「店舗が回る人数」は、この印が出ない最少の人数 |
Unity はこれまで、6店(③⑦④⑤⑥②)とも、混み具合は「普通」、スタッフは真ん中の人数だけを回していました。サイトの試算は「空いている・普通・混んでいる」を選べるので、Unity でも空いている日と混んでいる日を回すことにしました。
Unity が読む条件のファイルに、混み具合の36条件を足した
Unity は、サイトの public/sim/serving-robot-params.json を読んで条件を決めます(値を手で写さないため)。書き出しの export_params.py に、店ごとに「空いている 0・1・2台 → 混んでいる 0・1・2台」の6条件を後ろに足しました。前の18条件(普通)は、並びも中身も変えていません。条件は18件から54件になりました(p2m-site commit 29a5a62)。
--- /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_8/_前の版_gendffc100b269c/export_params.py 2026-10-29 08:00:12.448367603 +0000
+++ /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_8/export_params.py 2026-10-29 14:37:18.641975486 +0000
@@ -6,7 +6,7 @@
使い方: python3 export_params.py results.json <出力先> (出力先は p2m-site の public/sim/serving-robot-params.json)
- common:店に共通の値。role が "unity_measures" の値は Unity で測ってサイトに戻すための仮の値なので、Unity は読まない
- stores:Unity で回す店(fam_p1・fam_p5・fam_p2・fam_p3・fam_p4・fam_p0)の値。registers はレジの数(v2.8)
-- unity_conditions:Unity で回す条件(店ごとに3つ。③⑦④⑤⑥②で18条件)と、同じ条件の試算の結果(40回の平均)。Unity の店はレジの内側に店員が1人だけなので、レジは1つ
+- unity_conditions:Unity で回す条件(0〜17番は店ごとに混み具合が普通の3つ。18〜53番は2026-10-29に足した空いている・混んでいるの各3つ。計54条件)と、同じ条件の試算の結果(40回の平均)。Unity の店はレジの内側に店員が1人だけなので、レジは1つ
"""
import json
import sys
@@ -30,7 +30,7 @@
}
common = {k: {"value": v, "role": ROLE.get(k, "site_rule")} for k, v in h.COMMON.items()}
-UNITY_STORES = ["fam_p1", "fam_p5", "fam_p2", "fam_p3", "fam_p4", "fam_p0"] # ④は2026-10-27に追加(スタッフ2人・28組/時)。⑤は2026-10-28に追加(スタッフ4人・46組/時)。⑥も2026-10-28に追加(スタッフ3人・38組/時)。②も2026-10-29に追加(スタッフ2人・19組/時)
+UNITY_STORES = ["fam_p1", "fam_p5", "fam_p2", "fam_p3", "fam_p4", "fam_p0"] # ④は2026-10-27に追加(スタッフ2人・28組/時)。⑤は2026-10-28に追加(スタッフ4人・46組/時)。⑥も2026-10-28に追加(スタッフ3人・38組/時)。②も2026-10-29に追加(スタッフ2人・19組/時。同じ日に T06 を右へ1.0m 動かして②だけ計算し直した)
UNITY_REGISTERS = 1
stores = {}
for k in UNITY_STORES:
@@ -50,6 +50,18 @@
r = rows[h.key(c)]
conds.append({"condition": c, "site_result": {m: r[m] for m in r if m not in c}})
+# 2026-10-29 エイイチと決めた:混み具合を「空いている・混んでいる」にも広げて夜に回す。
+# 前の18条件(0〜17番)は並びも中身も変えず、後ろに足す(店ごとに 空いている0・1・2台 → 混んでいる0・1・2台)
+for k in UNITY_STORES:
+ s = h.STORES[k]
+ staff = s["staff"][1] # 真ん中の人数のまま
+ for arr in (s["arrivals"][0], s["arrivals"][2]): # 空いている・混んでいる
+ for robots in (0, 1, 2):
+ c = {"store": k, "staff": staff, "robots": robots, "arrivals_per_h": arr, "mode": "serve",
+ "trays": 3 if robots else 1, "fragile_by": "any", "registers": UNITY_REGISTERS}
+ r = rows[h.key(c)]
+ conds.append({"condition": c, "site_result": {m: r[m] for m in r if m not in c}})
+
D = {
"meta": {
"schema_version": "serving-robot-params/0.4",
足した条件の来客のペース(組/時)は、③ 30・54/⑦ 30・54/④ 20・36/⑤ 33・59/⑥ 27・49/② 14・24 です。台帳(Unity の回ごとの記録を1行ずつ足していく表)は、回す前に KIOXIA の archive/unity_ledger_v1_prenight_20261106_busy/ に控えました(672行・md5 79ded907583535ed5ac120e56595ac64)。
依頼30「混み具合を広げて夜を回す」の全文
---
作業フォルダ: /Users/shimazueiichi/Robotics_UX/ServiceSim
上限時間: 600
---
# 依頼:6店の混み具合を「空いている・混んでいる」に広げて、夜(360回)を回す(2026-10-29)
エイイチと決めたこと:今までの Unity は、6店とも混み具合が「普通」だけだった。サイトの試算は「空いている・普通・混んでいる」を選べるので、Unity でも空いている・混んでいるを回す。6店に共通の待機場所のまわりの詰まりが、混むとどれだけ増えるかも見る。
依頼30 の結果(依頼箱の報告)
23:40 に始まり、6:09 に終わりました(389分)。360回とも最後まで回り、失敗は0です。
# 結果:20261029_30_混み具合を広げて夜を回す
- 状態:完了
- 始めた時刻:2026-10-29 23:40
- 終わった時刻:2026-10-30 06:09(389分)
- 作業フォルダ:/Users/shimazueiichi/Robotics_UX/ServiceSim
- この続きを Claude Code で話すとき:作業フォルダで `claude --resume 093a5918-86a3-4c91-8716-ef6a03b33f1c`
Claude Code の報告 20261106_night_busy.txt の全文
====================================================================
6店の混み具合を「空いている・混んでいる」に広げた夜 360 回
依頼:20261029_30_混み具合を広げて夜を回す.md
夜の名前 20261106 記録 night/20261106_busy/ 回した日時 2026-10-29 23:45〜2026-10-30 06:04
build_id cs44_20261029_2343(ServiceSim/cs:44・保留 0) make_queue.py の追加は cs:45
====================================================================
結論混んでいるときは、席が空いているのに待つ時間が試算の3〜6倍だった
ロボット0台の入店待ち(店に来てから卓に着くまで)と、そのうち「席は空いているのに待った」時間(分。Unity は種10個の中央値、試算は40回の平均)。
| 店(混んでいる・0台) | 入店待ち Unity/試算 | 席は空いているのに待った Unity/試算 |
|---|---|---|
| ③ 効率重視・入口が角 | 19.02/13.03 | 8.94/1.55 |
| ⑤ ゆったり・入口が正面 | 24.79/19.38 | 12.22/2.07 |
| ⑥ ゆったり・入口が横 | 30.08/21.37 | 18.73/5.87 |
| ④ 効率重視・小さめの店 | 32.87/23.93 | 21.49/14.25 |
空いているときは、どの店も入店待ちは1〜2分で、試算とのずれは小さいままでした。満席で待った時間は、混んでいても試算とほぼ同じでした。ずれは「席は空いているのに待った」に集まっていました。
作業ログ ― スタッフの手がふさがった時間を、仕事ごとに分けて試算と比べた
仕事の量の差か、仕事の取り方の差か
席は空いているのに待つのは、片付けか案内のスタッフの手が空かないからです。Unity のスタッフが1組あたりに使う時間が長いのか(仕事の量)、それとも仕事を取る順番が違うのか(取り方)を分けたいと考えました。
Unity は60秒ごとに、仕事ごとのスタッフの人数を timeline.csv に書いています。試算のほうは、試算のコードを変えずに、スタッフの手がふさがった時間を足す所で、どの仕事かを見分けて別に足すようにしました。新しく Unity は回していません。
仕事ごとの時間を比べた staff_breakdown.py
"""混んでいるときの「席は空いているのに待った」のずれを、スタッフの時間の内訳で比べる(2026-10-30)
Unity:夜 20261106_busy の各回の timeline.csv(60秒ごとに、仕事ごとのスタッフの人数)と groups.csv、台帳の待ちの列
試算:haizen_sim.py(v2.8)を同じ条件で 40 回(試算と同じ種 0〜39)回し、スタッフの手がふさがった時間を仕事ごとに分けて数える
(試算のコードは変えない。手がふさがった時間を足す所で、どの仕事かを見分けて別に足すだけ)
使い方: python3 staff_breakdown.py <haizen_sim.py のフォルダ> <service_sim/unity のフォルダ> <出力フォルダ>
"""staff_breakdown.txt(混んでいるとき・1組あたりの分)
混んでいるとき:1組あたりのスタッフの手がふさがった時間(分、中央値。Unity 種10個/試算 40回)
店 台数 | 組数 U/S | 稼働率 U/S | 案内 U/S | 配膳 U/S | 呼び出し U/S | 会計 U/S | 片付け U/S | ロボット救出 U/S | 合計 U/S | 入店待ち U/S | 片付け待ち U/S
③ 効率重視・入口が角 0台 | 108/107 | 0.84/0.76 | 1.21/1.11 | 1.59/1.29 | 0.43/0.52 | 1.54/1.50 | 2.23/2.04 | 0.00/0.00 | 7.01/6.47 | 19.02/12.84 | 8.48/1.47
③ 効率重視・入口が角 1台 | 108/106 | 0.78/0.69 | 1.30/1.11 | 0.63/0.40 | 0.47/0.50 | 1.55/1.50 | 2.45/2.05 | 0.02/0.00 | 6.52/5.56 | 16.40/11.14 | 4.13/1.26
③ 効率重視・入口が角 2台 | 108/106 | 0.79/0.65 | 1.30/1.10 | 0.42/0.02 | 0.52/0.52 | 1.57/1.50 | 2.70/2.05 | 0.06/0.00 | 6.50/5.17 | 15.81/10.98 | 5.54/1.21
⑦ ③の通路を95cmに 0台 | 108/106 | 0.84/0.77 | 1.20/1.11 | 1.58/1.33 | 0.41/0.51 | 1.54/1.50 | 2.29/2.06 | 0.00/0.00 | 7.04/6.50 | 19.35/12.17 | 8.86/1.45
⑦ ③の通路を95cmに 1台 | 108/112 | 0.79/0.69 | 1.24/1.12 | 0.68/0.41 | 0.47/0.51 | 1.56/1.50 | 2.52/2.07 | 0.02/0.00 | 6.50/5.59 | 17.68/14.05 | 4.74/1.30
⑦ ③の通路を95cmに 2台 | 108/106 | 0.78/0.65 | 1.29/1.11 | 0.30/0.02 | 0.49/0.52 | 1.55/1.50 | 2.67/2.06 | 0.07/0.00 | 6.41/5.21 | 16.57/11.25 | 5.02/1.20
④ 効率重視・小さめの店 0台 | 72/70 | 0.87/0.89 | 1.12/1.03 | 1.37/1.14 | 0.38/0.47 | 1.58/1.50 | 2.04/1.89 | 0.00/0.00 | 6.47/6.01 | 32.87/24.06 | 20.78/14.32
④ 効率重視・小さめの店 1台 | 72/74 | 0.85/0.82 | 1.36/1.03 | 0.38/0.06 | 0.49/0.48 | 1.63/1.50 | 2.39/1.90 | 0.12/0.00 | 6.32/4.97 | 31.57/17.32 | 20.10/4.83
④ 効率重視・小さめの店 2台 | 72/70 | 0.87/0.82 | 1.58/1.03 | 0.42/0.00 | 0.51/0.47 | 1.67/1.50 | 2.45/1.91 | 0.48/0.00 | 7.17/4.91 | 45.92/13.04 | 23.61/4.54
⑤ ゆったり・入口が正面 0台 | 119/116 | 0.86/0.81 | 1.13/1.17 | 1.70/1.45 | 0.43/0.55 | 1.53/1.50 | 2.33/2.17 | 0.00/0.00 | 7.15/6.84 | 24.79/19.85 | 11.72/1.80
⑤ ゆったり・入口が正面 1台 | 119/120 | 0.84/0.73 | 1.25/1.17 | 0.78/0.47 | 0.52/0.56 | 1.53/1.50 | 2.74/2.17 | 0.01/0.00 | 6.86/5.86 | 23.70/18.24 | 8.51/1.39
⑤ ゆったり・入口が正面 2台 | 119/113 | 0.85/0.69 | 1.36/1.17 | 0.34/0.04 | 0.59/0.54 | 1.54/1.50 | 3.09/2.18 | 0.02/0.00 | 6.99/5.44 | 24.37/12.38 | 7.67/1.28
⑥ ゆったり・入口が横 0台 | 100/96 | 0.89/0.87 | 1.18/1.11 | 1.53/1.33 | 0.42/0.52 | 1.53/1.50 | 2.19/2.06 | 0.00/0.00 | 6.83/6.52 | 30.08/20.14 | 18.14/5.13
⑥ ゆったり・入口が横 1台 | 100/96 | 0.84/0.80 | 1.22/1.11 | 0.32/0.29 | 0.43/0.52 | 1.55/1.50 | 2.35/2.05 | 0.00/0.00 | 5.87/5.47 | 21.62/13.30 | 11.05/1.52
⑥ ゆったり・入口が横 2台 | 100/99 | 0.83/0.79 | 1.23/1.11 | 0.10/0.00 | 0.47/0.52 | 1.54/1.50 | 2.37/2.06 | 0.04/0.00 | 5.78/5.17 | 19.76/11.15 | 10.43/1.39
② 定番の郊外型 0台 | 47/46 | 0.76/0.75 | 1.07/1.04 | 1.30/1.14 | 0.40/0.47 | 1.55/1.50 | 2.05/1.91 | 0.00/0.00 | 6.33/6.02 | 11.17/8.76 | 5.81/3.32
② 定番の郊外型 1台 | 47/48 | 0.66/0.63 | 1.15/1.04 | 0.24/0.02 | 0.39/0.45 | 1.54/1.50 | 2.18/1.92 | 0.09/0.00 | 5.58/4.94 | 11.37/6.01 | 4.90/1.41
② 定番の郊外型 2台 | 47/48 | 0.65/0.65 | 1.17/1.03 | 0.10/0.00 | 0.44/0.50 | 1.56/1.50 | 2.09/1.91 | 0.17/0.00 | 5.61/4.93 | 10.69/6.12 | 3.57/1.32
ロボット0台では、1組あたりのスタッフの仕事は Unity のほうが5〜10%長いだけでした(③ 7.01/6.47分)。量の差では、3〜6倍のずれは出ません。
ロボットがいると差が広がります。④の2台では 7.17/4.91分です。案内と片付けが長くなり、ロボットを動かしに行く時間(試算は0)も入ります。
客が帰ってから片付けにスタッフが付くまでを比べた clear_wait_check.py
"""片付け待ちのずれは仕事の量か、仕事の取り方か(2026-10-30)
客が帰ってから片付けのスタッフが付くまでの待ち・汚れた卓の平均の数を、Unity と試算で比べる(混んでいる・0台)
使い方: python3 clear_wait_check.py <haizen_sim.py のフォルダ> <service_sim/unity のフォルダ>
"""
import csv, glob, os, statistics, sys
SIM_DIR, UNITY_DIR = sys.argv[1], sys.argv[2]
sys.path.insert(0, SIM_DIR)
import haizen_sim as h
class S(h.Store):
def __init__(self, *a, **k):
super().__init__(*a, **k)
self.cw = [] # 片付けの依頼からスタッフが付くまで(秒)
self.dirty_area = 0.0; self._lt = 0.0; self._ld = 0
def __setattr__(self, n, v):
if n == "dirty" and hasattr(self, "_lt"):
now = self.env.now; self.dirty_area += self._ld * (now - self._lt); self._lt = now; self._ld = v
object.__setattr__(self, n, v)
def staff_job(self, priority, walk_m, work_s, waited=None):
if priority == 2:
t0 = self.env.now; w = []
yield from super().staff_job(priority, walk_m, work_s, w)
self.cw.append(w[0])
else:
yield from super().staff_job(priority, walk_m, work_s, waited)
def ledger():
return {r["run_folder"]: r for r in csv.DictReader(open(os.path.join(UNITY_DIR, "unity_ledger_v1.csv"), encoding="utf-8"))}
L = ledger()
print("店 | 片付けに付くまで 秒 U/S | 汚れた卓の平均数 U(未着手+片付け中)/S | 満席で待った 分 U/S | 片付け待ち 分 U/S")
for st in ["fam_p1", "fam_p5", "fam_p2", "fam_p3", "fam_p4", "fam_p0"]:
s = h.STORES[st]; arr = s["arrivals"][2]; staff = s["staff"][1]
cond = {"store": st, "staff": staff, "robots": 0, "arrivals_per_h": arr, "mode": "serve", "trays": 1, "fragile_by": "any", "registers": 1}
sc, sd, sf, sb = [], [], [], []
for seed in range(h.REPS):
p = {**h.COMMON, **s, **cond}
env = h.simpy.Environment(); x = S(env, h.random.Random(seed), p); env.process(x.arrivals()); env.run()
sc.append(statistics.mean(x.cw)); sd.append(x.dirty_area / env.now)
sb.append(statistics.mean(x.blocked_waits))
sf.append(statistics.mean(x.entry_waits) - statistics.mean(x.blocked_waits) - statistics.mean(x.guide_waits))
uc, ud, uf, ub = [], [], [], []
for f in glob.glob(os.path.join(UNITY_DIR, f"20261106_night_a{arr}_cs44_{st}_r0_s*_passon_sbret")):
r = L[os.path.basename(f)]
uc.append(float(r["clear_wait_mean_s"])); ub.append(float(r["blocked_wait_mean"]))
uf.append(float(r["entry_wait_mean"]) - float(r["staff_wait_mean"]))
tl = list(csv.DictReader(open(os.path.join(f, "timeline.csv"), encoding="utf-8")))
ud.append(statistics.mean(float(t["tables_dirty_untaken"]) + float(t["tables_clearing"]) for t in tl))
m = statistics.median
print(f"{s['label']} | {m(uc):.0f}/{m(sc):.0f} | {m(ud):.2f}/{m(sd):.2f} | {m(uf):.2f}/{m(sf):.2f} | {m(ub):.2f}/{m(sb):.2f}")
clear_wait_check.txt
店 | 片付けに付くまで 秒 U/S | 汚れた卓の平均数 U(未着手+片付け中)/S | 満席で待った 分 U/S | 片付け待ち 分 U/S
③ 効率重視・入口が角 | 617/20 | 5.78/1.14 | 10.18/11.31 | 8.48/1.47
⑦ ③の通路を95cmに | 629/20 | 5.86/1.15 | 10.07/10.40 | 8.86/1.45
④ 効率重視・小さめの店 | 1539/1124 | 7.45/6.03 | 11.38/9.61 | 20.78/14.32
⑤ ゆったり・入口が正面 | 915/33 | 8.50/1.29 | 12.63/17.84 | 11.72/1.80
⑥ ゆったり・入口が横 | 1352/290 | 9.54/2.79 | 11.04/13.41 | 18.14/5.13
② 定番の郊外型 | 358/192 | 1.84/1.27 | 5.21/4.74 | 5.81/3.32
③では、客が帰ってから片付けにスタッフが付くまで、Unity は617秒(約10分)、試算は20秒でした。汚れた卓の平均の数も 5.78卓と1.14卓です。仕事の量はほぼ同じなのに、Unity では片付けが長く後回しになっています。取り方のどこかが違う、と見ました。
作業ログ ― Claude Code に Unity のスタッフの仕事の選び方を読んでもらった(依頼31)
依頼31「スタッフの仕事の選び方を読んで調べる」の全文
Unity も記録も変えず、新しく回さない、読むだけの依頼にしました。試算の決まり(優先0:ロボットを動かす、優先1:案内・配膳・呼び出し・会計、優先2:片付け。同じ優先なら先に頼まれた順)を書き、Unity の選び方と比べてもらいました。
---
作業フォルダ: /Users/shimazueiichi/Robotics_UX/ServiceSim
上限時間: 90
---
# 依頼:混むと片付けが詰まる理由を、Unity のスタッフの仕事の選び方から読んで調べる(読むだけ・2026-10-30)
**この依頼では何も変えない。** Assets・tools・設定ファイル・台帳・今までの出力は変えない。Unity エディタは開かない。新しく回さない。check-in しない。コードと今までの記録を読み、集計のスクリプトを書いて走らせるのはよい(スクリプトと出力は下の報告の場所にだけ置く)。
## なぜ調べるか
夜 20261106_busy(混み具合を広げた360回)で、混んでいるときの「席は空いているのに待った」が、どの店でも試算の3〜6倍になった。Claude が集計したところ、スタッフの仕事の量の差は小さく、客が帰ってから片付けにスタッフが付くまでの時間が大きく違った(集計:`/Volumes/KIOXIA/r2r2r-data/service_sim/unity/night/20261106_busy/staff_breakdown/` の staff_breakdown.txt・clear_wait_check.txt とスクリプト)。
| 混んでいる・0台 | 1組あたりのスタッフの仕事(分) Unity/試算 | 片付けにスタッフが付くまで(秒) Unity/試算 | 汚れた卓の平均の数 Unity/試算 |
|---|---|---|---|
| ③ | 7.0/6.5 | 617/20 | 5.8/1.1 |
| ⑤ | 7.2/6.8 | 915/33 | 8.5/1.3 |
| ⑥ | 6.8/6.5 | 1352/290 | 9.5/2.8 |
| ② | 6.3/6.0 | 358/192 | 1.8/1.3 |
ロボットを入れると、Unity では1組あたりの案内と片付けの時間が増える(④2台で 7.2 分/試算 4.9 分)。
回し方の決まりはサイトが正。Unity の仕事の選び方が試算と違うなら、どこが違うかを知りたい。直すかどうかはエイイチが決めるので、この依頼では直す案は並べるだけにする。
## 試算(haizen_sim.py v2.8)の決まり(比べる相手)
- スタッフは場所を持たない。手が空いた人が、待っている仕事を優先順で取る。同じ優先なら先に頼まれた順
- 優先0:ロボットを動かしに行く
- 優先1:案内・配膳・呼び出し・会計
- 優先2:片付け
- 片付け(客が会計を終えて帰ったら頼まれる):卓まで歩く+皿をまとめる40秒+卓を拭く40秒+皿を持って戻る。往復は2回(bus_trips)。洗い場までの追加の距離は0。洗い場で待つことはない
- 案内:卓まで歩く → 席に着く → 説明45秒 → 戻る。会計:レジで90秒(レジ1つにつき1組ずつ)
- 「席は空いているのに待った」=片付け待ち(汚れた卓の数までの列の前の組)+案内のスタッフの手が空くのを待った時間
## 調べること
1. **スタッフの仕事の選び方:** 仕事の種類ごとの優先順、同じ優先の中での順番(先に頼まれた順か、近い順か)、スタッフがどこで待ち、どこから仕事を取るか(待つ点・持ち場・決まった人だけが取る仕事があるか)、仕事の途中で別の仕事に割り込まれるか
2. **片付けの中身:** だれが・いつ頼むか(客が帰った時か、会計の後か)、歩く道(卓 → 洗い場 → 卓など)、往復の回数、皿をまとめる・拭くの時間(params の clear_s・wipe_s・bus_trips を使っているか)、洗い場で待つ・置ける量に上限があるか
3. **数え方:** clear_wait_mean_s・blocked_wait_mean・staff_wait_mean・timeline の tables_dirty_untaken/tables_clearing を、どの時刻からどの時刻までで数えているか。params の meta.metrics の定義と同じか
4. **記録で追う:** ③混んでいる0台の種0(`20261106_night_a54_cs44_fam_p1_r0_s0_passon_sbret`)で、片付けを待った時間が長かった卓を5つ選び、客が帰った時刻 → スタッフが片付けを取った時刻 → 終わった時刻と、その間スタッフ4人がそれぞれ何をしていたかを並べる(記録から分かる範囲で。分からなければ分からないと書く)
5. **ロボットがいるとスタッフの仕事が増える理由:** ④混んでいる2台で、案内・片付けが長くなる分が何から来ているか(ロボットをよける・道を空ける・ロボットを動かしに行く・2分待って代わりに運ぶ・遠回りなど)を、記録から分かる範囲で分ける
## 報告
依頼箱の結果に加えて、claude_code_reports に書く(場所:~/Library/Mobile Documents/com~apple~CloudDocs/デスクトップ/ADAPT DESIGN/じぶんの/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/unity/claude_code_reports/)。
**20261030_HHMM_staff_job_choice.txt**
- 1〜5 の答え。読んだファイルと行の番号を添える
- 試算との違いの表:項目/試算/Unity/片付け待ちのずれにどのくらい効いていそうか(大・中・小・分からない)
- 4 の5つの卓の時刻の表
- 直すとしたら考えられる形を、並べるだけ(どれを選ぶかは書かない)。サイトの試算の側に足すもの(たとえば洗い場の受け入れ)と、Unity を試算の決まりに合わせるもの、の両方
- 書いた集計のスクリプトの全文(置いた場所も)
- 変えたファイルがないこと(ServiceSim の保留 0・台帳の行数 1032 と md5 が始めと同じ)
Mac が止まり、依頼31は途中で終わった
7:38 に始めた依頼31は、Mac が止まって再起動したため途中で終わりました。見張り係は 8:13 に自動で立ち上がり、途中だった依頼を「失敗」に回していました。
# 結果:20261030_31_スタッフの仕事の選び方を読んで調べる
- 状態:失敗
- 始めた時刻:2026-10-30 08:13
- 終わった時刻:2026-10-30 08:13(0分)
- 作業フォルダ:不明
## Claude Code の報告
見張り係が止まった(Mac の再起動など)ため、この依頼は途中で終わっています。途中まで変わったファイルがあるかもしれません。やり直すかどうかは、確かめてから決めます。
読むだけの依頼なので、変わったのは Claude Code が書きかけた集計のスクリプト1つと、中身が空の結果ファイルだけでした。台帳は1032行のままで、最後の行は夜の最後の回、夜の前の672行は控えと同じ md5(79ded907583535ed5ac120e56595ac64)でした。そこで、書きかけのファイルは使えるなら続きから使ってよいことと、始める前に Unity の保留が0で前の作業で変わったファイルがないことを確かめることを、依頼の頭に足して出し直しました(8:16 に開始)。
---
作業フォルダ: /Users/shimazueiichi/Robotics_UX/ServiceSim
上限時間: 90
---
# 依頼:混むと片付けが詰まる理由を、Unity のスタッフの仕事の選び方から読んで調べる(読むだけ・2026-10-30)
**やり直し(2026-10-30 08:20):** 07:38 に始めた同じ依頼が、Mac の再起動で途中で止まった。途中のファイル(claude_code_reports/20261030_0744_staff_job_choice_files/ の compare_timeline.py・空の compare_timeline.txt)は消さずに残っている。使えるなら続きから使ってよい。始める前に、ServiceSim の保留が 0 で、前の作業で変わったファイルがないことを確かめ、報告に書く(変わっていたら手を止めて報告)。
**この依頼では何も変えない。** Assets・tools・設定ファイル・台帳・今までの出力は変えない。Unity エディタは開かない。新しく回さない。check-in しない。コードと今までの記録を読み、集計のスクリプトを書いて走らせるのはよい(スクリプトと出力は下の報告の場所にだけ置く)。
## なぜ調べるか
夜 20261106_busy(混み具合を広げた360回)で、混んでいるときの「席は空いているのに待った」が、どの店でも試算の3〜6倍になった。Claude が集計したところ、スタッフの仕事の量の差は小さく、客が帰ってから片付けにスタッフが付くまでの時間が大きく違った(集計:`/Volumes/KIOXIA/r2r2r-data/service_sim/unity/night/20261106_busy/staff_breakdown/` の staff_breakdown.txt・clear_wait_check.txt とスクリプト)。
| 混んでいる・0台 | 1組あたりのスタッフの仕事(分) Unity/試算 | 片付けにスタッフが付くまで(秒) Unity/試算 | 汚れた卓の平均の数 Unity/試算 |
|---|---|---|---|
| ③ | 7.0/6.5 | 617/20 | 5.8/1.1 |
| ⑤ | 7.2/6.8 | 915/33 | 8.5/1.3 |
| ⑥ | 6.8/6.5 | 1352/290 | 9.5/2.8 |
| ② | 6.3/6.0 | 358/192 | 1.8/1.3 |
ロボットを入れると、Unity では1組あたりの案内と片付けの時間が増える(④2台で 7.2 分/試算 4.9 分)。
回し方の決まりはサイトが正。Unity の仕事の選び方が試算と違うなら、どこが違うかを知りたい。直すかどうかはエイイチが決めるので、この依頼では直す案は並べるだけにする。
## 試算(haizen_sim.py v2.8)の決まり(比べる相手)
- スタッフは場所を持たない。手が空いた人が、待っている仕事を優先順で取る。同じ優先なら先に頼まれた順
- 優先0:ロボットを動かしに行く
- 優先1:案内・配膳・呼び出し・会計
- 優先2:片付け
- 片付け(客が会計を終えて帰ったら頼まれる):卓まで歩く+皿をまとめる40秒+卓を拭く40秒+皿を持って戻る。往復は2回(bus_trips)。洗い場までの追加の距離は0。洗い場で待つことはない
- 案内:卓まで歩く → 席に着く → 説明45秒 → 戻る。会計:レジで90秒(レジ1つにつき1組ずつ)
- 「席は空いているのに待った」=片付け待ち(汚れた卓の数までの列の前の組)+案内のスタッフの手が空くのを待った時間
## 調べること
1. **スタッフの仕事の選び方:** 仕事の種類ごとの優先順、同じ優先の中での順番(先に頼まれた順か、近い順か)、スタッフがどこで待ち、どこから仕事を取るか(待つ点・持ち場・決まった人だけが取る仕事があるか)、仕事の途中で別の仕事に割り込まれるか
2. **片付けの中身:** だれが・いつ頼むか(客が帰った時か、会計の後か)、歩く道(卓 → 洗い場 → 卓など)、往復の回数、皿をまとめる・拭くの時間(params の clear_s・wipe_s・bus_trips を使っているか)、洗い場で待つ・置ける量に上限があるか
3. **数え方:** clear_wait_mean_s・blocked_wait_mean・staff_wait_mean・timeline の tables_dirty_untaken/tables_clearing を、どの時刻からどの時刻までで数えているか。params の meta.metrics の定義と同じか
4. **記録で追う:** ③混んでいる0台の種0(`20261106_night_a54_cs44_fam_p1_r0_s0_passon_sbret`)で、片付けを待った時間が長かった卓を5つ選び、客が帰った時刻 → スタッフが片付けを取った時刻 → 終わった時刻と、その間スタッフ4人がそれぞれ何をしていたかを並べる(記録から分かる範囲で。分からなければ分からないと書く)
5. **ロボットがいるとスタッフの仕事が増える理由:** ④混んでいる2台で、案内・片付けが長くなる分が何から来ているか(ロボットをよける・道を空ける・ロボットを動かしに行く・2分待って代わりに運ぶ・遠回りなど)を、記録から分かる範囲で分ける
## 報告
依頼箱の結果に加えて、claude_code_reports に書く(場所:~/Library/Mobile Documents/com~apple~CloudDocs/デスクトップ/ADAPT DESIGN/じぶんの/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/unity/claude_code_reports/)。
**20261030_HHMM_staff_job_choice.txt**
- 1〜5 の答え。読んだファイルと行の番号を添える
- 試算との違いの表:項目/試算/Unity/片付け待ちのずれにどのくらい効いていそうか(大・中・小・分からない)
- 4 の5つの卓の時刻の表
- 直すとしたら考えられる形を、並べるだけ(どれを選ぶかは書かない)。サイトの試算の側に足すもの(たとえば洗い場の受け入れ)と、Unity を試算の決まりに合わせるもの、の両方
- 書いた集計のスクリプトの全文(置いた場所も)
- 変えたファイルがないこと(ServiceSim の保留 0・台帳の行数 1032 と md5 が始めと同じ)
依頼31 のやり直しの結果(依頼箱の報告)
# 結果:20261030_31_スタッフの仕事の選び方を読んで調べる_やり直し
- 状態:完了
- 始めた時刻:2026-10-30 08:16
- 終わった時刻:2026-10-30 08:58(43分)
- 作業フォルダ:/Users/shimazueiichi/Robotics_UX/ServiceSim
- この続きを Claude Code で話すとき:作業フォルダで `claude --resume 714640fd-feee-4e3e-a54a-fddf8d649b41`
## Claude Code の報告
**やったこと**
- 始める前に確かめました。ServiceSim の保留は 0(cs:45 head)、台帳は 1032 行で md5 は 52e433713d4f7d41dfa8ccfb889492cd、Assets・tools に前の作業で変わったファイルはありませんでした。
- Unity のコード(StaffAgent.cs・ServiceOps.cs・GroupManager.cs・TimelineLogger.cs・LedgerRow.cs)と試算 haizen_sim.py v2.8 を読み、1〜5 に行の番号つきで答えました。
- 前の作業の compare_timeline.py が終わらなかった理由がわかりました。試算の window() を走らせていないため「全員が帰った」の合図が来ず、1分ごとに数える処理が止まりません。前のファイルは残し、window() を足した写しを今回のフォルダに置いて走らせました。
- 集計のスクリプトを3つ書いて走らせました。pay_flow_check(会計と片付けの流れ)、trace_clear(4 の卓を追う)、robot_staff_extra(5 のロボットありの分け方)です。
**結果**
- 仕事の選び方の決まりは、Unity と試算で同じでした。優先は 0/1/2 で、同じ優先なら先に頼まれた順です。近い順ではなく、持ち場も割り込みもありません。
- 片付け待ちのずれは、主に次の二つから来ています。
- **会計の頼みを出す時刻が違う(効き:大)。** 試算はレジが空いてからスタッフを頼むので、ほかの仕事の後ろに並び、そのあいだレジが止まります(レジを持っていた時間の 12%)。Unity は客がレジの並ぶ点に着いた時に頼むので、前の会計が終わるとすぐ取られ、ほぼいつも1人がレジにいます。
- そのため ③ の 60〜160分では、会計が済む組が Unity 1時間に 38.7、試算 34.8 で、片付けの仕事も Unity のほうが速く入ってきます。
- **片付け1回が Unity のほうが長い(効き:中)。** ③で 135 秒、試算は 123 秒です。Unity は前の仕事が終わった所から歩き出し、卓で終わるためです。片付けが済む卓は1時間に Unity 32.4、試算 35.1 です。
- 二つを合わせると、入ってくる数と済む数の差は ③ で Unity 1時間に +6.3 卓、試算 −0.3 卓です。100分で約10卓たまり、記録の汚れた卓(12前後)と合います。② では差が小さく(0.3/0.6)、ずれが小さいことと合います。
- 試算では、たまる分は「卓で会計を待つ」(15分)として出ます。Unity では「汚れた卓」として出ます。
- **数え方:** clear_wait・blocked_wait・staff_wait の数え方は試算とほぼ同じで、数え方の違いで 3〜6 倍は出ません。
- **4(③0台・種0の5つの卓):** 客が帰ってからスタッフが取るまで 18.5〜21 分、取ってから終わるまで 1〜3 分でした。
- 割り出した待ちの平均は 555 秒で、記録の 548 秒とほぼ合います。T36 は次の組が来なかったので、終わった時刻は分かりません。
- スタッフ4人それぞれの動きは記録にないので分かりません。人数で見ると、待機は 0、レジに約1人、ほかの仕事に約2人、片付けに約1人でした。
- **5(④2台):** 1回あたり、案内が +24.7 秒、片付けが +25.6 秒長くなりました。
- どちらもおよそ、あきらめるまでの60秒が約12秒、ロボットをよけて止まった分が 4〜5 秒、残りが約9秒です。よけた分はどの仕事のときか記録にないので、歩いた時間の割合で分けた見積りです。
- ほかに、ロボットを動かしに行く時間が 1組あたり 30.6 秒増えました(試算は 0)。
- **変えたものがないこと:** 終わりも保留 0、台帳は 1032 行で md5 も始めと同じでした。Unity エディタは開かず、新しく回さず、check-in もしていません。
- 例外が二つあります。一つは simulation/v2_8/__pycache__/haizen_sim.cpython-312.pyc です。1回目の走らせで python が自動で書いたキャッシュで、haizen_sim.py そのものは変わっていません。
- もう一つは、試算を走らせる simpy がこの Mac に入っていなかったため、uv の一時の環境で使ったことです。入るのは uv のキャッシュの中だけで、プロジェクトと設定は変えていません。
**変えたファイル**
- 新しく作ったもの(claude_code_reports/ の中だけ):
- 20261030_0823_staff_job_choice.txt(報告。スクリプトの全文入り)
- 20261030_0823_staff_job_choice_files/ の中の compare_timeline.py/.txt、pay_flow_check.py/.txt、trace_clear.py/.txt、robot_staff_extra.py/.txt/_p1.txt
- 前の作業の 20261030_0744_staff_job_choice_files/compare_timeline.txt は、今回の1回目の走らせで空のまま書き直されました(前も空でした)。
**決めてほしいこと**
- 直すかどうかと、どの形にするか。案は報告にサイト側・Unity 側の両方を並べました。大きく効いているのは「会計の頼みを出す時刻」で、試算の頼み方に合わせるか、Unity の頼み方を試算に入れるかで方向が逆になります。
- 試算のフォルダにできたキャッシュ haizen_sim.cpython-312.pyc を消すかどうか。前からあったか分からないので、消さずに残しています。
Claude Code の報告 20261030_0823_staff_job_choice.txt の全文
報告の中に、集計のスクリプト(pay_flow_check・trace_clear・robot_staff_extra)の全文も入っています。
混むと片付けが詰まる理由を、Unity のスタッフの仕事の選び方から読んで調べる(読むだけ・2026-10-30 08:23〜08:56・やり直し)
■ まとめ
・仕事を選ぶ決まり(優先0/1/2、同じ優先なら先に頼まれた順、持ち場なし、途中で割り込まれない)は、Unity と試算で同じ。
・ずれは主に二つから来ている。どちらも「片付けの仕事が入ってくる速さ」と「片付けが済む速さ」の差になる。
(ア) 会計の頼みを出す時刻が違う(大)。試算はレジが空いてからスタッフを頼むので、ほかの優先1の仕事の後ろに並び、
そのあいだレジが止まる(レジを持っていた時間のうち、会計していたのは ③ 88%)。Unity は会計の人がレジの並ぶ点に
着いた時に板に出すので、板の優先1の中で古くなり、前の会計が終わるとすぐ取られる(会計のスタッフ 0.97 人=ほぼいつも1人)。違いは「会計のスタッフを呼ぶタイミング」だった
| 項目 | 試算 v2.8 | Unity |
|---|---|---|
| 仕事の優先順 | 0 ロボットを動かす → 1 案内・配膳・呼び出し・会計 → 2 片付け。同じ優先なら先に頼まれた順 | 同じ |
| 会計のスタッフを呼ぶとき | レジの順番が来てから。ほかの優先1の後ろに並び、そのあいだレジが止まる(③でレジを持っていた時間の12%) | お客さんがレジの並ぶ点に着いたとき。前の会計が終わるとすぐ取られ、会計のスタッフがほぼいつも1人レジにいる |
会計が済む組(③ 60〜160分) | 1時間に34.8組 | 1時間に38.7組 |
| 片付け1回 | 123秒 | 135秒(前の仕事が終わった所から歩き出すため) |
| たまる所 | 卓で会計を待つ(約15分)。卓は客が座ったまま埋まる | 汚れた卓(③で1時間に約6卓ずつたまる) |
同じ詰まりが、試算では「会計待ち」、Unity では「片付け待ち」として出ていました。前に記録だけにしていた「詰まる所が試算はレジ、Unity は片付け」というずれの正体は、これでした。
作業ログ ― フロントの実感で、試算を Unity の会計の頼み方に合わせると決めた
回し方の決まりはサイトの試算が正なので、どちらにそろえるかで直す方向が逆になります。
| 案 | 中身 | 影響 |
|---|---|---|
| A | 試算を Unity の呼び方に変える(客がレジに並んだら、すぐスタッフが行く) | サイトの試算の数字が変わる |
| B | Unity を試算の呼び方に合わせる | サイトの数字は変わらない |
| C | 記録だけにする | 何も変えない |
どちらにするかは、お客さんがレジに来たとき、片付けの途中でも会計に行っていたかどうか、フロントをしていたころの動き方で決めることにしました。
フロントスタッフやっていた頃、基本はAでレジや案内を優先に仕事していたね。
A に決めました。
作業ログ ― 試算 v2.9:会計のスタッフをレジに並んだ順に付ける
1回目の直しでは、会計がほとんど速くならなかった
最初は、会計のスタッフの頼みを「レジに並んだ時刻」にさかのぼらせるだけにしました。SimPy の PriorityResource(優先順と頼んだ時刻の順に、手の空いた人を割り当てる仕組み)は、頼みを(優先、頼んだ時刻)の順に並べます。頼んだ時刻を古くすれば、同じ優先の案内や配膳より先に会計が取られるはず、という考えです。
ところが、Unity の54条件で比べると、効きは小さいままでした。③の普通・0台で、会計待ちは 12.3分 → 9.9分(Unity は4.5分)。1時間に済む会計も 35.0組 → 35.9組で、Unity の 38.7組に届きません。
会計が済む速さと、レジの使われ方を測った throughput_check.py・register_idle_check.py
import sys, statistics, importlib
sys.path.insert(0, sys.argv[1]); import haizen_sim as h
class S(h.Store):
def __setattr__(s, n, v):
if n == "dirty" and hasattr(s, "dirty") and v > s.dirty: s.__dict__.setdefault("paid", []).append(s.env.now)
object.__setattr__(s, n, v)
for arr in (42, 54):
c = {'store':'fam_p1','staff':4,'robots':0,'arrivals_per_h':arr,'mode':'serve','trays':1,'fragile_by':'any','registers':1}
thr, pw = [], []
for seed in range(40):
p = {**h.COMMON, **h.STORES['fam_p1'], **c}; env = h.simpy.Environment(); s = S(env, h.random.Random(seed), p); env.process(s.arrivals()); env.run()
thr.append(sum(3600 <= t < 9600 for t in s.paid) / (6000/3600)); pw.append(statistics.mean(s.pay_waits))
print(sys.argv[1], arr, "会計/時(60-160分)", round(statistics.mean(thr),1), "会計待ち", round(statistics.mean(pw),1))
import sys, statistics
sys.path.insert(0, sys.argv[1]); import haizen_sim as h
c = {'store':'fam_p1','staff':4,'robots':0,'arrivals_per_h':42,'mode':'serve','trays':1,'fragile_by':'any','registers':1}
p = {**h.COMMON, **h.STORES['fam_p1'], **c}
env = h.simpy.Environment(); s = h.Store(env, h.random.Random(0), p)
reg = s.register; log = []
def mon():
while True:
log.append((env.now, len(reg.users), len(reg.queue), len(s.staff.users), len(s.staff.queue), [r.priority for r in s.staff.queue]))
yield env.timeout(5)
env.process(s.arrivals()); env.process(mon()); env.run(until=9600)
w = [x for x in log if 3600 <= x[0] < 9600]
print("レジが使われている割合", round(sum(x[1] for x in w)/len(w),2), "レジの列の平均", round(statistics.mean(x[2] for x in w),1), "スタッフ稼働", round(statistics.mean(x[3] for x in w),2))
1回目の直しのあとで、レジを取ってから会計のスタッフが付くまでを数えると、すぐ(0秒で)付いたのは33%だけで、平均9.8秒、最長44秒待っていました。頼みは確かに列の先頭に並んでいるのに、会計を終えたスタッフが次の会計を取っていません。
SimPy の出来事の順番で、次の組の頼みより先にスタッフの手を放していた
SimPy では、同じ時刻の出来事は、予定に入った順に処理されます。1回目の直しでは、会計が終わると、レジを空ける → スタッフの手を放す、の順に書いていました。ところが、レジを空けたことで次の組がレジに入るのは、ひとつ後の出来事になります。そのため、次の組の会計の頼みが並ぶ前にスタッフの手が放され、そのスタッフは別の仕事を取っていました。
レジを空けたことが処理されるのを待ち(yield self.register.release(rq))、さらに1つ待って(yield env.timeout(0))次の組の頼みが並んでから、スタッフの手を放すように直しました。会計を終えたスタッフが、そのまま次の会計を取ります。Unity の「会計の係がほぼいつも1人レジにいる」と同じ形になりました。
- ねらい:お客さんがレジに並んだら、片付けの途中でもスタッフが会計に行く、というフロントの動き方を試算に入れる
- 変わったこと:レジを取ってから会計のスタッフが付くまでが 0秒だった割合が 33% → 98%(平均 9.8秒 → 0.5秒)。③の普通・0台で、1時間に済む会計は 35.0組 → 38.1組(Unity 38.7組)、会計待ちは 12.3分 → 4.8分(Unity 4.5分)
- 見込める効果:混んだ日に詰まる場所が、試算でも「片付け」として出るようになる。どの店で何人いれば回るかを、店の動き方に近い形で答えられる(下の54条件の表で確かめた)
haizen_sim.py の v2.8 から v2.9 への差分
--- /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_8/haizen_sim.py 2026-10-24 23:58:07.432138539 +0000
+++ /sessions/rcw-01levexvnksg51fwlydnyzff/mnt/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/simulation/v2_9/haizen_sim.py 2026-10-30 00:13:52.903726056 +0000
@@ -1,5 +1,16 @@
"""
-サービスロボット導入シミュレーション #1 配膳ロボット(試作 v2.4)
+サービスロボット導入シミュレーション #1 配膳ロボット(試作 v2.9)
+
+v2.9 で直したこと(2026-10-30。エイイチと決めた:フロントの実感では、レジと案内を優先して仕事をしていた)試算 v2.9 の haizen_sim.py の全文
"""
サービスロボット導入シミュレーション #1 配膳ロボット(試作 v2.9)
v2.9 で直したこと(2026-10-30。エイイチと決めた:フロントの実感では、レジと案内を優先して仕事をしていた)
会計のスタッフを頼む時刻を「客が食べ終えてレジに並んだとき」にした。v2.8 までは、レジの順番が来てから
スタッフを頼んだので、ほかの優先1の仕事(案内・料理・呼び出し)の後ろに並び、そのあいだレジが止まっていた。
v2.9 では、会計の頼みはレジに並んだ時刻の古さで並ぶ(優先は1のまま)。スタッフが会計に付くのは、
これまでどおりレジが空いてから(レジ1つにつき1組ずつ・pay_s 秒)。会計が終わると先にレジを空け、Unity の54条件で、v2.8・v2.9・Unity を並べた quick_compare_unity54.py
"""v2.9(会計の頼みをレジに並んだ時刻で並べる)を、Unity で回している54条件だけ先に回し、v2.8 と Unity の夜と比べる(2026-10-30)
使い方: python3 quick_compare_unity54.py <service_sim/unity のフォルダ> → quick_compare_unity54.json / .txt"""
import csv, glob, json, os, statistics, sys
from multiprocessing import Pool
import haizen_sim as h
U = sys.argv[1]
P = json.load(open("serving-robot-params.json", encoding="utf-8"))
conds = [c["condition"] for c in P["unity_conditions"]]
with Pool() as pool:
new = pool.map(h.run_condition, conds)
L = list(csv.DictReader(open(os.path.join(U, "unity_ledger_v1.csv"), encoding="utf-8")))
NIGHT = {"fam_p1": "20261102", "fam_p5": "20261102", "fam_p2": "20261030", "fam_p3": "20261103", "fam_p4": "20261104", "fam_p0": "20261105"}
def unity(c):
s = h.STORES[c["store"]]
if c["arrivals_per_h"] == s["arrivals"][1]:
pre = NIGHT[c["store"]] + "_night_cs"
else:
pre = f"20261106_night_a{c['arrivals_per_h']}_cs"
rows = [r for r in L if r["run_folder"].startswith(pre) and r["store"] == c["store"] and r["robots"] == str(c["robots"]) and r["status"] == "completed"]
m = lambda k: statistics.median(float(r[k]) for r in rows) if rows else float("nan")
return {"n": len(rows), "entry": m("entry_wait_mean"), "staff_wait": m("staff_wait_mean"), "pay_wait": m("pay_wait_mean")}
out = []
lines = ["店 混み 台 | 入店待ち v2.8/v2.9/Unity | 席は空いているのに待った v2.8/v2.9/Unity | 会計待ち v2.8/v2.9/Unity | 店が回らない v2.8→v2.9"]
for c, old, n in zip(conds, P["unity_conditions"], new):
o = old["site_result"]; u = unity(c)
lab = h.STORES[c["store"]]["label"].split()[0]
crowd = ["空", "普", "混"][h.STORES[c["store"]]["arrivals"].index(c["arrivals_per_h"])]
ng = lambda r: "×" if r["staff_wait_mean"] >= h.NG_MIN else "○"
lines.append(f"{lab} {crowd} {c['robots']} | {o['entry_wait_mean']:.1f}/{n['entry_wait_mean']:.1f}/{u['entry']:.1f} | "
f"{o['staff_wait_mean']:.1f}/{n['staff_wait_mean']:.1f}/{u['staff_wait']:.1f} | {o['pay_wait_mean']:.1f}/{n['pay_wait_mean']:.1f}/{u['pay_wait']:.1f} | {ng(o)}→{ng(n)}")
out.append({"condition": c, "v28": o, "v29": {k: v for k, v in n.items() if k not in c}, "unity_median": u})
json.dump(out, open("quick_compare_unity54.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
open("quick_compare_unity54.txt", "w", encoding="utf-8").write("\n".join(lines) + "\n")
print("\n".join(lines))
quick_compare_unity54.txt(直した後の v2.9)
店 混み 台 | 入店待ち v2.8/v2.9/Unity | 席は空いているのに待った v2.8/v2.9/Unity | 会計待ち v2.8/v2.9/Unity | 店が回らない v2.8→v2.9
③ 普 0 | 3.3/4.1/4.1 | 1.0/2.7/2.9 | 12.3/4.8/4.5 | ○→○
③ 普 1 | 2.6/1.7/5.0 | 0.6/0.5/3.3 | 8.0/5.6/6.1 | ○→○
③ 普 2 | 1.2/1.1/4.0 | 0.4/0.4/1.7 | 8.0/6.6/7.1 | ○→○
⑦ 普 0 | 2.8/3.1/4.5 | 0.9/2.1/3.3 | 11.7/4.9/4.4 | ○→○
⑦ 普 1 | 1.8/1.6/3.2 | 0.5/0.5/1.5 | 8.1/5.8/5.1 | ○→○
⑦ 普 2 | 2.0/1.6/3.6 | 0.4/0.5/1.7 | 8.0/6.6/7.2 | ○→○
④ 普 0 | 10.4/10.9/13.0 | 8.4/9.4/11.5 | 4.2/1.7/1.9 | ○→○普通の混み具合のロボット0台では、入店待ち・席は空いているのに待った・会計待ちが、ほぼ Unity と同じになりました(③ 4.1/2.7/4.8分、Unity 4.1/2.9/4.5分。⑤ 7.5/5.5/6.1分、Unity 8.1/6.5/5.7分)。混んでいるときは、まだ Unity のほうが長くなります(③ 12.9/5.2/6.5分、Unity 19.0/8.9/5.7分)。残りは、片付け1回の長さの差(Unity は前の仕事の場所から歩き出す)と、ロボットがいるときにスタッフの仕事が増える分と見ています。
作業ログ ― 全32,850条件を Mac とクラウドで分けて計算し直した
分けて回す run_shard.py と、まとめる merge_shards_v29.py
条件の並びの番号を n で割った余りが k のものだけを回す run_shard.py で分けました。Mac につないだ shell は1回の呼び出しが3分までで、裏で動かしたままにすると呼び出しの終わりに止まるので、約170秒ずつ区切って回しました(途中経過は100条件ごとに書かれ、次の呼び出しで続きから回ります)。並行して、Claude のクラウドの作業環境でも別の番号を回しました。Mac は1秒に約4〜7条件、クラウドは約2〜3条件だったので、途中で分け方を6つに細かくし、遅い側の残りを Mac が後ろから回しました(run_timed_rev.py。v2.8 のときと同じ道具)。
"""v2.8 の全条件を、番号で分けて回す(2026-10-25。計算機を2台に分けて速くするため)。
使い方: python3 run_shard.py <k> <n> <出力.json> [使い回す結果.json ...]
- 条件の並び(haizen_sim.conditions())の番号を n で割った余りが k のものだけ回す
- 使い回す結果に同じ条件(同じ前提)があれば回さない。100条件ごとに出力へ途中経過を書く"""
import json, os, sys
from multiprocessing import Pool
import haizen_sim as h
k, n, out = int(sys.argv[1]), int(sys.argv[2]), sys.argv[3]
common = json.loads(json.dumps(h.COMMON))
done = {}
for path in sys.argv[4:] + [out]:
try:
old = json.load(open(path, encoding="utf-8"))
if old.get("common") == common and old.get("reps") == h.REPS:
done.update({h.key(r): r for r in old["rows"]})
except FileNotFoundError:
pass
conds = [c for i, c in enumerate(h.conditions()) if i % n == k]
todo = [c for c in conds if h.key(c) not in done]
print(f"shard {k}/{n}: {len(conds)} conditions, {len(todo)} to run", flush=True)
def save():
rows = [done[h.key(c)] for c in conds if h.key(c) in done]
with open(out + ".tmp", "w", encoding="utf-8") as f:
json.dump({"common": h.COMMON, "reps": h.REPS, "shard": [k, n], "rows": rows}, f, ensure_ascii=False)
os.replace(out + ".tmp", out)
with Pool() as pool:
for i, r in enumerate(pool.imap_unordered(h.run_condition, todo, chunksize=2), 1):
done[h.key(r)] = r
if i % 100 == 0:
save(); print(f" {i}/{len(todo)}", flush=True)
save(); print("done", flush=True)
"""v2.9 の分けて回した結果(shards/*.json)を1つの results.json にまとめる(2026-10-30)
使い方: python3 merge_shards_v29.py (v2_9 のフォルダで)→ results.json
- 同じ前提(common・reps)の行だけを使う。条件の並びは haizen_sim.conditions() の順
- 同じ条件が2つの計算機で回っていたら、数字が同じかを確かめる"""
import glob, json
import haizen_sim as h
common = json.loads(json.dumps(h.COMMON))
rows, dup, diff = {}, 0, 0
for f in sorted(glob.glob("shards/*.json")):
d = json.load(open(f, encoding="utf-8"))
if d.get("common") != common or d.get("reps") != h.REPS:
print("前提が違うので使わない:", f); continue
for r in d["rows"]:
k = h.key(r)
if k in rows:
dup += 1
if any(abs(rows[k][m] - r[m]) > 1e-9 for m in r if isinstance(r[m], (int, float))):
diff += 1
else:
rows[k] = r
conds = list(h.conditions())
missing = [c for c in conds if h.key(c) not in rows]
print("条件", len(conds), "揃った", len(conds) - len(missing), "足りない", len(missing), "重なり", dup, "うち数字が違う", diff)
if not missing:
json.dump({"common": h.COMMON, "reps": h.REPS, "rows": [rows[h.key(c)] for c in conds]},
open("results.json", "w", encoding="utf-8"), ensure_ascii=False)
print("results.json を書いた")
まとめた結果です。
条件 32850 揃った 32850 足りない 0 重なり 8000 うち数字が違う 0
results.json を書いた
Mac とクラウドの両方で回った8,000条件は、数字がすべて一致しました(simpy 4.1.2)。
v2.8 と v2.9 の違い compare_v28_v29.txt
試算 v2.8 → v2.9 の違い(2026-10-30。全32,850条件・各40回。v2.9 は Mac とクラウドで分けて回し、重なった8,000条件の数字はすべて一致)
変えたこと:会計のスタッフの頼みを、食べ終えてレジに並んだ時刻の古さで並べる。会計が終わると先にレジを空け、次の組の頼みが並んでからスタッフの手を放す(会計の係がそのまま次を取る)。Unity と同じ頼み方(依頼31)。エイイチの実感(フロントはレジと案内を優先していた)で決めた
印の数(レジ1つ/レジ2つ、各16,425条件)
- 店が回らない(席は空いているのに10分以上):1,922 → 2,617 / 2,880 → 2,929
- レジが詰まる(会計待ち10分以上):5,372 → 2,626 / 490 → 45
平均の差(v2.9 − v2.8):入店待ち +0.35分・席は空いているのに待った +1.07分・会計待ち −2.18分・料理の待ち +0.04分
Unity と比べた(ロボット0台の例。入店待ち/席は空いているのに待った/会計待ち、分)
- ③普通:v2.8 3.3/1.0/12.3 → v2.9 4.1/2.7/4.8 Unity 4.1/2.9/4.5
- ⑤普通:v2.8 6.2/1.5/18.8 → v2.9 7.5/5.5/6.1 Unity 8.1/6.5/5.7
- ③混んでいる:v2.8 13.0/1.5/15.4 → v2.9 12.9/5.2/6.5 Unity 19.0/8.9/5.7
(54条件の表は quick_compare_unity54.txt)
LP(/serving-robot-one-less-staff)で変わる数字
- 最初の画面(③・42組/時・ロボット0台→2台):店舗が回る人数 3→2人 が 3→3人。入店待ち 3.3→1.2分 が 4.1→1.1分
③ スタッフ2人・2台:席は空いているのに待った 8.3 → 18.6分(v2.8 は会計待ちが24.2分で、卓に座ったまま待つ客が片付けの仕事を遅らせていた)
- 2台入れても人が減らない:99条件のうち45 → 38条件
- 1人が歩く距離:約40% → 約41%減
- 図面のある店の入店待ち(0台→2台):③ 3.3→1.2 が 4.1→1.1/④ 10.4→3.5 が 10.9→5.6/⑤ 6.2→3.7 が 7.5→2.4/⑥ 6.9→1.9 が 7.9→2.1/⑦ 2.8→2.0 が 3.1→1.6/② 2.6→1.2 が 2.6→1.3
ページの確かめ:全37,800画面を描いて欠け・undefined・NaN なし(test_page_v28.js)
レジ1つの店では、「店が回らない」が 1,922 → 2,617条件に増え、「レジが詰まる」が 5,372 → 2,626条件に減りました。会計で隠れていた詰まりが、片付けと案内の詰まりとして出てきた形です。
作業ログ ― 試算のページと LP を v2.9 にして公開した
試算のページに v2.9 の結果を入れる build_page_v29.py
結果の行だけを入れ替え、見出しの版を v2.9 に、前提の「会計とレジ」に v2.9 の決まりを書きました。全37,800画面を描いて、欠け・undefined・NaN がないことを確かめました(test_page_v28.js)。
"""試算 v2.9(会計のスタッフの頼みを、レジに並んだ時刻で並べる版)の結果を、公開中の試算のページに入れる(2026-10-30)
使い方: python3 build_page_v29.py <公開中のページ> <v2.9 の results.json> <出力するページ>
- const D:結果の行(r)だけを v2.9 に入れ替える。条件の並び・列・店の情報・平面図は公開中のまま
- 画面:見出しの版を v2.9 にする。前提の「会計とレジ」に v2.9 の決まりを書く
- 費用の欄・LP 用の色・使われ方の記録は公開中のまま"""
import json, sys
import haizen_sim as h
live, res, out = sys.argv[1:4]
s = open(live, encoding="utf-8").read()
N = json.load(open(res, encoding="utf-8"))
assert N["common"] == json.loads(json.dumps(h.COMMON)), "結果が今の haizen_sim.py の前提と違う"
assert N["common"]["model"] == "v2.9"
lines = s.split("\n")
idx = [i for i, l in enumerate(lines) if l.startswith("const D = ")]
assert len(idx) == 1
D = json.loads(lines[idx[0]][len("const D = "):].rstrip(";"))
K = D["k"]
cond = K[:8]
mets = K[8:-1]
assert cond == ["store", "staff", "robots", "arrivals_per_h", "mode", "trays", "fragile_by", "registers"] and K[-1] == "ng"
conds = list(h.conditions())
rows_by_key = {h.key(r): r for r in N["rows"]}
assert len(rows_by_key) == len(conds) == len(D["r"]), (len(rows_by_key), len(conds), len(D["r"]))
new = []
for old in D["r"]:
c = dict(zip(cond, old[:8]))
r = rows_by_key[h.key(c)]
new.append(old[:8] + [round(r[k], 3) for k in mets] + [1 if r["staff_wait_mean"] >= h.NG_MIN else 0])
D["r"] = new
assert {k: v for k, v in D["common"].items() if k != "model"} == {k: v for k, v in N["common"].items() if k != "model"}
D["common"]["model"] = "v2.9"
lines[idx[0]] = "const D = " + json.dumps(D, ensure_ascii=False, separators=(",", ":")) + ";"
s = "\n".join(lines)
def rep(a, b):
global s
assert s.count(a) == 1, (a, s.count(a))
s = s.replace(a, b)
rep("数字はすべて仮置き(試作 v2.8)", "数字はすべて仮置き(試作 v2.9)")
rep("会計はレジ1つにつき1組ずつ。食べ終えた組は卓に座ったままレジの順番を待ち、順番が来たらスタッフの手が空くのを待って会計する。",
"会計はレジ1つにつき1組ずつ。食べ終えた組は卓に座ったままレジの順番を待つ。会計のスタッフは、食べ終えてレジに並んだ順に付き、同じ優先の配膳・案内・呼び出しより先になる。会計が終わると、同じスタッフがそのまま次の組の会計に付く(v2.9 から。それまでは、レジの順番が来てからスタッフを頼んだので、ほかの仕事の後ろに並び、そのあいだレジが止まっていた)。")
open(out, "w", encoding="utf-8").write(s)
print(out, len(new), "rows")
rendered 37800 / レジが詰まるが出た画面 3365 / 回らないが出た画面 6822 / 5台以上の注意 10800
LP の最初の画面は「3人 → 2人」から「3人 → 3人」に変わる
LP(/serving-robot-one-less-staff)の数字は、ビルドのときに試算のページのデータから読みます。v2.9 で作り直すと、最初の画面(③に2台)の「店舗が回る人数」が 3人 → 2人 から 3人 → 3人 になりました。③のスタッフ2人・2台では、席は空いているのに待つ時間が 8.3分 → 18.6分です。v2.8 では会計待ちが24.2分あり、お客さんが卓に座ったまま待つあいだ片付けの仕事が入ってこなかったので、2人でも回っているように見えていました。
| LP の数字 | v2.8 | v2.9 |
|---|---|---|
最初の画面:店舗が回る人数(③・0台 → 2台) | 3人 → 2人 | 3人 → 3人 |
| 最初の画面:入店待ち | 3.3分 → 1.2分 | 4.1分 → 1.1分 |
| 2台入れても人が減らない条件 | 99条件のうち45 | 99条件のうち38 |
人数そのままで、1人が歩く距離 | 約40%減 | 約41%減 |
| ④の入店待ち(0台 → 2台) | 10.4 → 3.5分 | 10.9 → 5.6分 |
| ⑤の入店待ち(0台 → 2台) | 6.2 → 3.7分 | 7.5 → 2.4分 |
公開前に、そのまま出すか、最初の画面の例を差し替えるかを相談し、そのまま公開すると決めました。ロボットを入れても人件費が浮かない結果も正直に見せる、という前に決めた方針のままです。試算のページと、Unity が読む条件のファイル(条件は54件のまま、試算の値だけ v2.9)を一緒に公開しました(p2m-site commit c05dc77)。
最初の画面の「ひと月の差し引き」を、減った人数で計算する形に直した
最初の画面の「ひと月の差し引き −0.3万円」は、「1人減らせた」として、1人分の人件費からロボット2台の月額を引いた数字でした。人数が 3人 → 3人 の隣に置くと、人は減らないのに −0.3万円で済む、と読めてしまいます。減った人数分の人件費 − ロボット2台の月額、で出すように直しました。人数が減らないので、月額の14.4万円がそのままかかります(−14.4万円)。下の費用の節(1人減らせたときの計算)はそのまま残しました(p2m-site commit 0f6074b)。
commit 0f6074b4f7d4c07802718e149b7bbd89cb139085
Author: PixelstoMotors <studio@adaptdesign.jp>
Date: Fri Oct 30 10:36:24 2026 +0900
LP:最初の画面の差し引きを、試算で減った人数分の人件費 − ロボット2台の月額にする(人数が減らなければ月額がそのままかかる)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LevEXVNKSg51fWLydNyZFf
diff --git a/src/pages/serving-robot-one-less-staff.astro b/src/pages/serving-robot-one-less-staff.astro
index 0158afa..0e02d53 100644
--- a/src/pages/serving-robot-one-less-staff.astro
+++ b/src/pages/serving-robot-one-less-staff.astro
@@ -51,6 +51,8 @@ const planMax = Math.max(...plans.map((p) => p.from));
const p1 = plans.find((p) => p.key === 'fam_p1')!;
const p1Store = STORES.fam_p1, p1Arr = p1Store.arrivals[1];
const p1Staff0 = minStaff('fam_p1', 0, p1Arr)!, p1Staff2 = minStaff('fam_p1', 2, p1Arr)!;
+// 最初の画面の差し引き:試算で減った人数分の人件費 − ロボット2台の月額。人数が減らなければ月額がそのままかかる(2026-10-30。試算 v2.9 で ③ が 3人 → 3人 になったため)
+const p1Net = savedPeak * Math.max(p1Staff0 - p1Staff2, 0) - cost2;
const MENU = [
{ n: '01', id: 'trouble', t: '導入後のつまずき' }, { n: '02', id: 'know', t: '導入前に分かること' }, { n: '03', id: 'shape', t: 'なぜ自分の店舗でやるのか' },
{ n: '04', id: 'sim', t: 'まず、近い店舗で試す' }, { n: '05', id: 'flow', t: '頼むときの流れ' }, { n: '06', id: 'roles', t: '立場ごとの使い方' }, { n: '07', id: 'limits', t: 'この試算で言えないこと' },
@@ -71,7 +73,7 @@ const MENU = [
<dl>
<div><dt>店舗が回る人数</dt><dd><b>{p1Staff0}</b><small>人</small><i>→</i><em>{p1Staff2}</em><small>人</small></dd></div>
<div><dt>入店待ち</dt><dd><b>{min1(p1.from)}</b><small>分</small><i>→</i><em>{min1(p1.to)}</em><small>分</small></dd></div>
- <div><dt>ひと月の差し引き</dt><dd><em>{man(savedPeak - cost2)}</em><small>万円</small></dd></div>
+ <div><dt>ひと月の差し引き</dt><dd><em>{man(p1Net)}</em><small>万円</small></dd></div>
</dl>
<p class="ft-note">効率重視の店舗・1時間{p1Arr}組・ロボット0台 → 2台(月額)</p>
</figure>
- ねらい:最初の画面の3つの数字(人数・入店待ち・お金)を、同じ条件の同じ結果から出す
- 変わったこと:ひと月の差し引き −0.3万円 → −14.4万円(人数 3人 → 3人 のとき)
- 見込める効果:ロボットを入れると「人は減らないが、入店待ちは 4.1分 → 1.1分に縮む」ことが、お金の数字と食い違わずに伝わる。読む人がどう受け取ったかは、LP からの相談の数で見ていく
simulation/v2_9/README.md
# 配膳ロボットの試算 v2.9(2026-10-30 回す・公開)
会計のスタッフを、客が食べ終えてレジに並んだ順に付ける版。
## なぜ直したか
混み具合を広げた Unity の夜(20261106_busy・360回)で、混んでいるときの「席は空いているのに待った」が試算の3〜6倍になった。依頼31(Claude Code が Unity のコードと記録を読んだ調べ)で、仕事の優先順は試算と Unity で同じだが、会計のスタッフを頼む時刻が違うと分かった。
- v2.8 の試算:レジの順番が来てからスタッフを頼む。ほかの優先1(案内・配膳・呼び出し)の後ろに並ぶので、そのあいだレジが止まる(③で、レジを持っていた時間の12%)。食べ終えた客が卓に座ったまま会計を待ち、片付けの仕事が入ってこない
- Unity:客がレジの並ぶ点に着いたときに頼む。前の会計が終わるとすぐ取られ、会計のスタッフがほぼいつも1人レジにいる
エイイチの実感(フロントをしていたころ、基本はレジと案内を優先して仕事をしていた)で、Unity の頼み方を試算に入れると決めた(2026-10-30)。調べと集計は KIOXIA の service_sim/unity/night/20261106_busy/staff_breakdown/ と、unity/claude_code_reports/20261030_0823_staff_job_choice.txt。
## 決めたこと
- 会計のスタッフの頼みは、食べ終えてレジに並んだ時刻の古さで並ぶ(優先は1のまま)。同じ優先の案内・配膳・呼び出しより先になる
- スタッフが会計に付くのは、これまでどおりレジが空いてから(レジ1つにつき1組ずつ・90秒)
- 会計が終わると先にレジを空け、次の組の頼みが並んでからスタッフの手を放す(会計の係がそのまま次の会計を取る)
- 「店が回らない」「レジが詰まる」の決め方、LP の「店舗が回る人数」の決め方は変えていない
- LP の最初の画面(③に2台)は「3人 → 2人」から「3人 → 3人」に変わる。人件費が浮かない結果も正直に見せる方針のまま、そのまま公開すると決めた(エイイチ)
## 確かめたこと
- Unity で回している54条件で先に比べた(quick_compare_unity54.txt)。普通の混み具合のロボット0台では、入店待ち・席は空いているのに待った・会計待ちがほぼ Unity と同じになった(③ 4.1/2.7/4.8 分、Unity 4.1/2.9/4.5 分)
- 全32,850条件を Mac とクラウドで分けて回し、重なった8,000条件の数字はすべて一致した(simpy 4.1.2)
- 全37,800画面を描いて、欠け・undefined・NaN がない(v2_8 の test_page_v28.js)
- v2.8 との違いは compare_v28_v29.txt
## ファイル
- haizen_sim.py:試算 v2.9(layouts/ は v2_8 と同じ店の形 08db8a0d067c)
- results.json:全32,850条件(40回の平均)。shards/ と merge_shards_v29.py で分けて回したものをまとめた
- build_page_v29.py → serving-robot.v29.html(p2m-site の public/sim/serving-robot.html)
- export_params.py → serving-robot-params.json(p2m-site の public/sim/serving-robot-params.json。条件は54件のまま、試算の値だけ v2.9)
- quick_compare_unity54.py:Unity の54条件で v2.8・v2.9・Unity を並べる
- run_shard.py・run_timed_rev.py:分けて回す道具
## 作り方
```
python3 run_shard.py <k> <n> shards/<名前>.json # 分けて回す(Mac 側は1回3分までなので区切る)
python3 merge_shards_v29.py # → results.json
python3 build_page_v29.py <公開中のページ> results.json serving-robot.v29.html
python3 export_params.py results.json serving-robot-params.json
node ../v2_8/test_page_v28.js serving-robot.v29.html
```
- 公開済みの記事の数字は前の版のまま
作業ログ ― ここで区切り、残りのずれ2つは次に回す
段階4(サイトとのずれを調べ、取得項目や店の形を見直す)の区切りを「ずれ3つそれぞれについて、サイトの試算を直すか記録だけにするかを決めたら区切る」と決めていました。そのうち1つ(混むと片付けが詰まる)は、試算を v2.9 に直して決着しました。
| ずれ | 今の扱い |
|---|---|
| 混むと片付けが詰まる | 試算を v2.9 に直した(会計のスタッフをレジに並んだ順に付ける) |
待機場所のまわりの詰まり(6店すべて) | 次に、待機場所の置き方を店の形を作る決まりとして考える |
ロボットがいるとスタッフの仕事が増える(④2台で1組あたり約46%多い) | 次に、ロボットを動かしに行く時間・待ってあきらめる時間・よける時間を、試算に入れるかを考える |
混んでいるとき、v2.9 でも Unity のほうが入店待ちが長い | 片付け1回の長さの差(135秒・123秒)として記録 |
次に Unity へ夜の依頼を出すときは、v2.9 の条件のファイルを先に写してもらいます。スタッフの人数を広げる夜も、段階4の中で1回回します。