==================================================================== 依頼 v11 の 5 章:ずれの切り分けの仕掛けを作った(回すのはエイイチ) 2026-10-25 18:04 ==================================================================== 結論 ・試し1〜3 を押せる形にしました。名札は **det_check** で、 段階Bの18回とも split_check とも混ざりません。組み立ては通っています(54 本・エラー 0)。 ・**私からは Play を押せません**(Unity が ServiceSim のプロジェクトを押さえています)。 4 章の手順で押してください。1回 11 分・2回ずつ3通りで 70 分ほどです。 ・試しの直しは **check-in していません**。保留は私が直した 6 本だけです。 ・★ ご指摘のとおりでした。3 章の「忙しいと pathPending が長くなる」は **私が確かめずに書いた見込み**です。1 章で訂正し、試し3 で確かめる形にしました。 ・ops.NowS の洗い出しは 2 章です。**(0) の直しは入れていません。** 会計・片付けの時間、60 秒であきらめる、600 秒で卓を空ける は NowS を使っておらず、 2 分待ちは NowS を使っていますが「引き算」なのでずれは消えます。 1 か所だけ、来客を止める時刻が絶対値で比べられていて、ずれが効きます。 ==================================================================== 1. 【訂正】pathfindingIterationsPerFrame は忙しさで変わらないはず ==================================================================== ご指摘のとおりです。Unity の説明では NavMesh.pathfindingIterationsPerFrame = 「1コマで処理する道探しの節の最大数」(既定 100) で、**実時間ではなくコマあたりの回数**です。1コマ 0.05 秒に固定してあり、 注文が溜まる順番はシミュレーションの中身だけで決まるので、 **この枠が原因なら Mac の忙しさでは変わらないはず**です。 前の報告の 3 章で「忙しいと pathPending が長くなる」と書いたのは、 私が「非同期だから実時間に引っ張られるだろう」と見込みで書いたもので、 確かめていませんでした。取り消します。すみません。 では、2回がずれた元は何か。いまの時点で言えるのは次までです。 確かなこと ・2回の回しで違っていた入力は、実時間の値だけ(plan_ms_max が1回めの往復から違う。 wall_seconds は 654.5 秒 / 601.1 秒) ・1546.25 秒までは、出力が小数4桁まで完全に一致していた ・最初のずれは「スタッフの譲りが4コマ遅れた」で、 その判断は _agent.pathPending が true の間は打ち切られる作りになっている まだ分かっていないこと ・pathPending の長さが、何によって2回で変わったのか。 枠がコマあたりなら、残る筋は「道探しや局所回避がワーカースレッドで解かれていて、 main スレッドが結果を読む時点で終わっているかどうかが、 スレッドの込み具合で変わる」あたりですが、**確かめていません**。 ・そもそも「忙しさ」ではなく、同じ Editor の中で2回続けて回したことによる 持ち越し(前の回の状態)かもしれません。 だから試し3 を「わざと重くして回す」に変えずに、**2つに分けました**(4 章)。 試し3a 同じ条件でもう2回(何も重くしない) → 毎回ずれるのか、たまたまだったのかが分かる 試し3b ほかに2本回しながら1本 → 忙しさで**結果が変わるか**。ご指摘どおりなら変わらないはず 試し3b で結果が変わったら、「何が変えたか」を次の順で詰めます。 ・run_info.json の wall_seconds と plan_ms(どれだけ重くなったか) ・det_frames.csv の path_pending 列(1コマごとに、道待ちの人が何人いたか)を 3通りで並べる。忙しいときに path_pending が増えていれば、 「枠はコマあたりでも、結果を読む時点で終わっていない」ことの裏づけになります ・増えていないのに結果が変わったら、道待ち以外(局所回避)が原因と分かります ついでに1つ消しました。 通行者の種類(NavMesh.CreateSettings)が Play ごとに増えて、 1回めと2回めで人用の種類の番号が違ったのでは、と疑いましたが、 NavMeshAreas.asset は cs:1 のときも今も **Humanoid 1種のまま**でした。 この筋は無しです。 ==================================================================== 2. ops.NowS を判断に使っている所((0) の直しは入れていません) ==================================================================== NowS を読んでいる所は 56 か所ありました。判断に効くものだけ並べます。 (a) 引き算なので、NowS がずれても **影響しない**もの ServiceOps.cs:1317 if (NowS - d.readyS < wait) → ★ 料理の「2分待ち」(robotFirstWaitSec = 120)。 readyS も NowS から入れているので、引き算でずれが消えます。安全 GroupManager.cs:312 float now = ops.NowS → Arrive / SeatWaiting / Advance → 来客の間隔(now < _nextArrivalS)、料理ができる時刻(now < g.cookAt[i])、 食べ終わり(now < g.eatEndS)、呼び出し、ドリンクバー・トイレ。 どれも「now + 決めた秒数」で入れた時刻と now を比べているので、引き算。安全 ServiceOps.cs:1057,1063 turnMin / seatWaitMin ServiceOps.cs:516 仕事が板で待った秒数(依頼 v10 の 2) → 記録。引き算なので安全 (b) **絶対値で比べていて、NowS のずれがそのまま効く**もの ExperimentRunner.cs:602 if (ops.NowS >= _arrivalsEndS && !groups.AnyGroupInStore) ExperimentRunner.cs:607 return ops.NowS >= _hardEndS; GroupManager.cs(Arrive) if (arrivalsEndS > 0f && now >= arrivalsEndS) return; → _arrivalsEndS = peak_min × 60 = **7200 秒**(定数)。 NowS は float で足し続けているので、本当の 7200 秒のときには NowS = 7190.68 秒(**−9.32 秒**)。 つまり **来客を 9.3 秒ぶん長く受け付けています**(本当の 7209.3 秒まで)。 打ち切り(_hardEndS = 7200 + 600×60)も同じだけずれます。 → ただし **2回の回しで同じようにずれる**ので、今回の「2回がずれる」の原因ではありません。 直すと来客が 9 秒ぶん減るので、**基準データ v2 の数字は少し動きます**。 ExperimentRunner.cs:560 if (ops.NowS >= _nextTimelineS) → timeline.csv の t_s(0・60・120…)。記録だけですが、 終わりごろには本当の時刻と 20 秒以上ずれた札になります (179 分の回で測ると −23.4 秒) (c) NowS を **使っていない**もの(ご指摘の項目はこちら) ★ 会計 90 秒・片付け 40+40 秒・案内 45 秒の dwell → StaffAgent の `_timer += Time.deltaTime` ★ 60 秒であきらめる(staffGiveUpSec) → StaffAgent の `_stepS += Time.deltaTime` ★ 600 秒で卓を空ける(tableStuckFreeSec・依頼 v10 の 1) → GroupManager の `g.stuckS += dt` ・ロボットが 60 秒で動けなくなる(stuckAfterStoppedSec)、 1往復の打ち切り(maxTripSec)、人どうしの詰まり(JamWatcher) → どれも dt の足し算 ・StoppedNowS は名前に NowS が付いていますが、中身は積み上げた秒数です まとめ ・ご心配の「会計・片付けの時間、60 秒であきらめる」は NowS と無関係でした。 ・「2 分待ち」は NowS を使いますが引き算なので安全です。 ・効くのは **来客を止める時刻と打ち切りの時刻**(9.3 秒ぶん長い)だけです。 ・なので (0) の直しは「記録をきれいにする」だけでなく、 **来客の長さが 9 秒変わる=基準データ v2 の数字が少し動く**直しになります。 前の報告で「(0) なら v2 はそのまま使える」と書いたのは不正確でした。 訂正します。(0) のうち ・plan_ms_max を比べる列から外す … 動き変わらない ・stops.csv の t_s を NowS にそろえる … 動き変わらない ・NowS をコマ数×0.05 にする … **来客が 9 秒短くなる。v2 が動く** の3つに分けて決めてください。 ==================================================================== 3. 試し2 で一致しなかったときの、残りの元の見分け方 ==================================================================== 残るのは (B) 局所回避(RVO)と (C) 物理の返る順番です。 見分けられるように、2つの道具を足しました。 (C) 物理の返る順番 — **先に白黒がつきます** FrontSensor は箱に入った当たりを 32 個の配列で受け、 超えたら切り捨てます(どれが落ちるかは返る順番で決まる)。 実際に超えているかを数えて run_info.json に出すようにしました。 "sensor_max_hits": その回でいちばん多かった当たりの数 "sensor_overflows": 32 を超えて切り捨てた回数 → sensor_overflows が 0 なら、**切り捨ては起きていない=(C) は原因から外せます**。 (残るのは「ぴったり同じ距離で並んだとき」だけですが、 小数の一致はまず起きないので、実質無しと見ます) → これは試し1 の出力でもう分かります。試し2 を待つ必要がありません。 (B) 局所回避(RVO) — ずれ方の形で分かります 1コマごとの指紋を det_frames.csv に書くようにしました(名札 det_check のときだけ)。 frame,t_s,people,robots,people_sum,robot_sum,path_pending people_sum / robot_sum 位置を 0.1 mm まで整数にして混ぜた数。 2回ぶんを並べると **どのコマで初めてずれたか** が1コマ単位で分かる path_pending そのコマで道待ちだった人の数 見分け方 ・**RVO なら**:ずれ始めが **早い**(数分以内)。人が2人以上近づいた最初の場面から people_sum が違い始め、path_pending は同じまま。 位置の差はごく小さく(1 mm 未満から)、だんだん大きくなる ・**道待ち(A)が残っているなら**:ずれ始めまで people_sum がぴったり一致し、 ずれた**そのコマで path_pending が違う**。差は「コマ何個ぶん」の形で出る (いまの 1546 秒・4コマのずれと同じ形) ・どちらでもないなら、試し4(局所回避も切る)を押します。 それで一致すれば RVO、しなければさらに別の元があると分かります 念のため:試し1・試し2 でも指紋を書くようにしてあります。 一致していても「最後まで people_sum が同じ」ことが確かめられます。 ==================================================================== 4. エイイチにお願いすること(押す順番) ==================================================================== Unity に切り替えてください(私が直した .cs が取り込まれます)。Console の赤い字を確認。 試し1 — 道探しの枠だけ大きくする 1. ServiceSim → Stage B → 切り分けの設定 → 試し1:道探しの枠を 5000 にする (Console に「道探しの枠 5000 回/コマ / 人の道を同期 切 / …」と出ます) 2. ServiceSim → Stage B → ずれの切り分け(③1台・種1 を2回) → {日付}_det_check_cs2_fam_p1_r1_s1 と _2 ができます 試し2 — 人の道を同期で引く 3. 切り分けの設定 → 試し2:人の道を同期で引く 4. ずれの切り分け(③1台・種1 を2回) 試し3a — 試し2 のまま、もう2回(たまたまかを見る) 5. 4 をもう一度押す(_3 と _4 になります) 試し3b — 忙しくして1回 6. ほかの重い処理を2つ走らせながら(または別の Unity を2つ回しながら)、 もう一度 4 を押す ※ 1回の実時間の上限は 40 分です。3本同時で超えそうなら、 先に ExperimentPlan の maxWallSecondsPerRun ではなく ServiceSimStageB の上限(40 分)に当たるので、 status が stopped になったら教えてください 終わったら 7. 切り分けの設定 → ぜんぶ切る(cs:2 と同じ動きに戻す) 8. 出力フォルダ名を教えてください。det_frames.csv と run_info.json を私が並べます ★ 試し1〜4 はどれも **ExperimentPlan.asset を書き換えます**(切り替えの保存)。 これも check-in しないでください。終わったら 7 を押して、 私が cm undochange で元に戻します。 ==================================================================== 5. 私が直したファイル(check-in していません) ==================================================================== いまの保留 6 件。ぜんぶ「試しの切り替えを入れた」分で、 切り替えを切れば cs:2 と同じ動きになります。 Experiment/ExperimentPlan.cs 切り替え4つを足した(初期値はぜんぶ切) detCheckPathIterations(0 = 触らない) detCheckSyncPeoplePaths detCheckNoAvoidance detCheckFrameFingerprint People/PeopleNav.cs Go に同期で引く道を足した(SyncPaths が true のとき)。 引けなければ今までどおり非同期に任せる Robot/FrontSensor.cs 当たりの最大数と切り捨て回数を数えた(MaxSeen / Overflows) Experiment/ExperimentRunner.cs 切り替えを入れる・戻す(ApplyDetCheck / RestoreDetCheck)、 det_frames.csv を書く、run_info に det_check を出す Experiment/TripLogger.cs run_info.json に det_check・sensor_max_hits・ sensor_overflows を足した Editor/ServiceSimStageB.cs 名札 det_check・メニュー 「ずれの切り分け(③1台・種1 を2回)」と 「切り分けの設定」5つ 変えていないもの ・店の回し方・ロボットの動き・人のよけ方の決まり(切り替えを切った状態で) ・ops.NowS((0) の直しは入れていません。2 章のとおり決めてから) ・台帳の列(増やしていません) ・段階Bの18回の中身と順番 増えたメニュー(ほかは cs:2 と同じ) ServiceSim/Stage B/ずれの切り分け(③1台・種1 を2回) ServiceSim/Stage B/切り分けの設定/試し1:道探しの枠を 5000 にする ServiceSim/Stage B/切り分けの設定/試し2:人の道を同期で引く ServiceSim/Stage B/切り分けの設定/試し4:局所回避も切る(要るときだけ) ServiceSim/Stage B/切り分けの設定/ぜんぶ切る(cs:2 と同じ動きに戻す) ServiceSim/Stage B/切り分けの設定/いまの設定を見る