Skip to content

feat: simulator-cli にibisバイナリプロトコルを移植 - #2

Open
HansRobo wants to merge 30 commits into
masterfrom
ibis
Open

HansRobo wants to merge 30 commits into
masterfrom
ibis

Conversation

@HansRobo

@HansRobo HansRobo commented Apr 7, 2026

Copy link
Copy Markdown
Member

No description provided.

HansRobo added 30 commits April 7, 2026 21:25
grSimフォーク(ibis-ssl/grSim)で実装されていた独自機能をER-Force
simulator-cliに移植し、ibisブランチではデフォルトで有効化する。

## 追加した機能

### IbisCommandAdaptor
- UDP:12345で715バイトのibisバイナリパケットを受信
- SSLビジョンデータをキャッシュしてロボット位置からチームを自動判別
- P-gainによる角度制御、加速度制限、極座標→ロボットローカル速度変換を実装
- sslsim::RobotControl (MoveLocalVelocity) に変換してシミュレータへ送信

### IbisFeedbackAdaptor
- SSLビジョンデータ(位置・向き)とRadioResponse(速度・ボール検出)を統合
- 128バイトのibisバイナリフィードバックパケットを構築
- UDP:50100+robotIdで制御系に非同期送信

### RefereeTeamDetector
- Game Controller(UDP:10003)のマルチキャストを受信
- チーム名照合でblue/yellowを自動判別し、解決後は受信を停止

### PacketSenderThread
- QThread+QMutex/QWaitConditionによるUDPバッチ送信スレッド
- 送信レイテンシを物理ループから切り離す

### ibis_protocol.h
- ibisバイナリプロトコルの定数・構造体・デシリアライズ関数・パケットビルダーを集約

## CLIオプション(デフォルト値付き)
- --ibis-port (12345)
- --ibis-feedback-addr (127.0.0.1)
- --ibis-feedback-port-base (50100)
- --ibis-feedback-team-name (ibis)
- --ibis-use-referee
- --ibis-acc-speedup/brake (4.0/6.0 m/s^2)
grSimのdocker-publish.yamlを参考に、ibis-ssl/framework-simulatorcliイメージを
ghcr.io (GitHub Container Registry) へpublishするワークフローを追加。

- イメージ名: ghcr.io/ibis-ssl/framework-simulatorcli
- Dockerfile: data/docker/Dockerfile.simulatorcli
- プラットフォーム: linux/amd64, linux/arm64
- トリガー: masterへのpush / v*.*.*タグ / PR(ビルドのみ) / workflow_dispatch
- GitHub Actions cache、SBOM、attestation対応
masterではなくibisブランチを中心に運用するため、
ワークフローのトリガーブランチをibisに更新。
arm64クロスコンパイル(QEMUエミュレーション)を廃止し、
amd64のみのビルドに変更することでCI実行時間を削減する。
QEMUによるarm64ビルドは実ビルドの5〜10倍以上の時間を要していた。

Dockerfile.simulatorcliも合わせて最適化:
- ビルドステージ: 不要なQt5パッケージを削除(simulator-cliはQt6のみ使用)
- ランタイムステージ: devパッケージをランタイムライブラリのみに置換
  - qt6-base-dev, libqt6opengl6-dev → libqt6core6t64, libqt6gui6t64等
  - libprotobuf-dev → libprotobuf32t64
ロボットレスポンス受信とフィードバック送信の結合を分離し、
QTimerを使った定期送信に変更。これにより送信タイミングが
コマンドレスポンスの受信タイミングに依存しなくなる。

変更内容:
- IbisFeedbackAdaptorにfeedbackHzパラメータを追加(デフォルト125Hz)
- QTimer(PreciseTimer)を使って指定レートでフィードバックを定期送信
- RobotCacheを追加し、ロボットレスポンス受信時に速度・ボール検知をキャッシュ
- タイマー発火時にキャッシュデータを参照して送信する方式に変更
- --ibis-feedback-hz CLIオプションを追加(最小値1Hz)
- ログ出力にフィードバック送信レートのHz表示を追加
## 変更内容

### sendGroundTruth シグナルの追加(Simulator → SimProxy)
- `Simulator::sendGroundTruth(QByteArray)` シグナルを新設
- `Simulator::process()`(200Hz)内で `stepSimulation` 直後にノイズなし位置データを emit
- `world::SimulatorState` 形式でシリアライズして伝送
- `SimProxy` でフォワードするシグナルと接続を追加

### IbisFeedbackAdaptor の位置更新ソース変更
- `handleVisionData`(SSL Visionパース、66.67Hz)を廃止
- `handleGroundTruth`(world::SimulatorState パース、200Hz)に置き換え
- Bullet座標系 → SSL座標系の変換: x_mm = p_y×1000, y_mm = -p_x×1000
- クォータニオン回転行列第1列から yaw 角を算出

### ibis-referee-port CLIオプションの追加
- `RefereeTeamDetector` コンストラクタにポート引数を追加
- `--ibis-referee-port` オプションで Game Controller ポートを変更可能に

## 背景
フィードバック送信レート(125Hz)はVisionパケット更新レート(66.67Hz)を上回っており、
位置キャッシュが古いデータのまま複数回送信される問題があった。
シミュレータの物理ステップ(200Hz)から直接取得することで完全に解消する。
ランタイムイメージにbuildディレクトリ全体をコピーしていた問題を解消。
cmake installを使ってバイナリとconfigファイルのみを抽出し、
必要最小限のファイルのみランタイムステージへコピーするよう変更。

- RELATIVE_DATA_DIRS=ONでビルドし、実行時のconfig参照を./config/に固定
- cmake --install でbin/とconfig/のみをインストールプレフィックスへ配置
- ランタイムステージはinstall済みのbin/simulator-cliとconfig/のみをコピー
- 不要になったsimulator-cli_entrypoint.bashを削除
- ENTRYPOINTをtiniに、CMDをsimulator-cliに整理
- CMakeLists.txtにinstallターゲットを追加(バイナリ・configファイル)
ibisフィードバック送信レート(125Hz = 8ms)の整数倍(×2)に揃えることで、
毎回のfeedback送信時に必ず2ステップ分の新鮮な位置データが得られる状態にする。

## 変更内容
- SUB_TIMESTEP: 1/200.f(5ms)→ 1/250.f(4ms)
- process() トリガー間隔: 5ms → 4ms(scaling=1.0 時)

## 周期の関係
- sendGroundTruth: 250Hz(4ms)
- feedbackTimer: 125Hz(8ms)= 2ステップに1回
- 位置データの最大鮮度: 4ms以内

## 副作用
- Vision パケット送信レートが 66.67Hz → 62.5Hz に変化
  (閾値 12.5ms に対して4msステップが当たるのが4ステップ目=16ms)
  ibis用途では影響なし
ヒンジモーター経由の間接摩擦方式ではトルクがdribble_powerに比例して
低下するため、ドリブラーが非常に効きにくい問題があった。
btPoint2PointConstraintでボールを剛体拘束する既存のperfectDribblerモードを
simulator-cliのデフォルトとして有効化することで修正。

- config/simulator-realism/Ibis.txtを新規作成(simulate_dribbling: false)
- simulator-cliの--realismデフォルト値をRealistic→Ibisに変更
- grSimのcheckDribbleFeedbackを参考に反力 > 0.5 Nで吸着解除する機構を追加
- 解除後100msのクールダウンで即再吸着を防ぐ
- 従来挙動は --realism Realistic で引き続き選択可能
crane が位置指令を送り、ロボット側の CM4 が位置制御ループを閉じて G474 へ
速度指令を渡す構成へ移行するための、simulator-cli 側の対応。
不安定で遅延が乗る無線経路を位置制御ループの外側へ出すのが狙いで、
simulator-cli は G474(STM32 メイン基板)とロボット物理を担当する。

## control_mode を見ない復号の修正

ibisDeserialize() は CONTROL_MODE(byte 23) を無視し、CONTROL_MODE_ARGS を
常に polar velocity として復号していた。ARGS は mode により意味が変わる
union のため、POSITION_TARGET(mode 4) のパケットでは terminal_velocity_x/y が
polar r/theta として読まれ、無言で誤った速度指令になっていた。

CONTROL_MODE に応じて ARGS を復号するよう修正し、固定フィールドの
target_global_pos / terminal_velocity と、vision_global_theta /
is_vision_available も復号対象へ追加した。

## mode 3 以外の明示的な拒否

IbisCommandAdaptor は G474 を模擬しており、G474 が実装する制御モードは
POLAR_VELOCITY_TARGET(3) のみである。位置制御は CM4 側の責務なので、
mode 3 以外を受け取った場合はロボットを停止し、1秒/台のレート制限付きで
理由をログへ出す。mode 4 が直接届くのは経路に CM4 相当が入っていない
設定ミスであり、黙って誤解釈するより停止するほうが安全で切り分けも早い。

## その他

- 送信元が制御しないスロットのゼロ埋めを ibisSlotIsEmpty() で明示的にスキップ。
  従来はチーム照合の距離判定に偶然引っかかって除外されていた。
- docs/robot-side-position-control.md に crane / Orion_CM4 / G474 との
  統合仕様(パケット契約・ポート割当・タイミング契約)を追加。
- data/scripts/ibis-chain-smoketest.py で実プロセスに対する回帰テストを追加。
crane-side セッションからの指摘を実測で検証し、誤りを訂正した。

## 訂正: feedback ポートは「crane 側の変更不要」ではない

crane_robot_receiver は sim_mode が真だと購読先を 127.0.0.1 に切り替える
(robot_receiver_node.cpp:351-352)。crane_comm/unicast.hpp:81 は is_multicast()
で分岐するため 127.0.0.1 は素の bind に落ちてグループ参加もせず、さらに :78-79 で
SO_REUSEADDR と SO_REUSEPORT の両方を設定している。

つまり sim:=true の crane は、cm4_sim が feedback を受けるのに必要な
127.0.0.1:50100+id を同じ形で先に押さえる。SO_REUSEPORT 付き2ソケットの実測では
単一送信元フローが片方に全量入り(0 / 200)、均等分割にはならない。feedback は
位置制御ループ内で唯一の位置信号なので、これは制御が完全に死ぬか動くかの二択に
なり、しかも4-tupleハッシュ次第で実行ごとに変わる。

対処として CM4-in-the-loop 構成では crane に feedback_sim_mode:=false を渡す。
multicast 受けと unicast 受けが同居する場合は完全分離されることも実測確認済み。

## mode 4 の終端速度フィールドの意味を定義

terminal_velocity_x/y(ARGS 24..27) は到達時の速度ベクトル(フィードフォワード)、
byte 36..37 のスカラは その大きさに対する上限 であり、冗長ではない。
クランプは受信側の責務で送信側に整合義務は無く、スカラ 0 は「上限なし」であって
「停止」ではない。参照実装は calculateSimGlobalVelocity()。

position_tolerance は ibis パケットに載っていないため CM4 は到達判定を再現できず、
固定定数を使う。ARGS 28..31 が空いているので拡張は可能。

## A/B の劣化注入点を対称化

cm4_sim が受信 mode で分岐し(3=passthrough, 4=位置制御)、劣化注入は mode に
よらず入力側で常に適用する。これで独立変数が「位置ループをどこで閉じるか」だけに
なる。mode 分岐は実機 CM4 にも必要な後方互換経路なのでシミュレータ専用の仕掛けでもない。
ibis指令のcontrol_modeを解釈しロボット側位置制御構成に対応
cm4-side セッションからの指摘を実機ソースと突き合わせて確認し、4箇所の
食い違いを修正した。正本は G474_Orion_main/Core/Src/ai_comm.c sendRobotInfo()。

## byte 4..7 ヨー角: ラジアン -> 度

実機は imu->yaw_deg を書いている。シミュレータだけラジアンだったため、
この値を使う消費側の挙動が実機と 180/pi 倍ずれていた。crane_latency_estimator が
vision との突き合わせに fb.yaw_angle を使っているので、実害のある差分である。

## byte 2: ロボットID -> 定数 10

実機は "CRC, 10:dummy" として定数 10 を書いており、実チェックサムは計算しない
(ホスト側 is_checksum_valid は実機パケットでも False になる)。シミュレータだけ
ロボットIDを入れていた。ロボットIDはポート番号から決まるので情報は失われない。

## byte 14: ボールセンサ複製 -> 送信サイクルカウンタ

実機は tx_cycle_count を置いている。3つ目のボールセンサではない。
消費側が ball_sensor を判定しているのは byte 12 のみ
(crane robot_receiver_node.cpp:416) なので、挙動への影響はない。

## byte 60: バージョンマーカ 0x01 -> 0

実機は camera_pos_x_div2。シミュレータ独自の「0x01 = シミュレータ」マーカを
置いていたため、消費側では camera_pos_x = 2 として復号されていた。
ローカルカメラを持たないので、カメラ未接続の実機と同じ 0 にする。
シミュレータ識別用の空きバイトは存在しない。

## 検証

Orion_CM4 の host/lib/feedback/packet.py(実機用デコーダの正本)で
シミュレータ出力を復号し、全フィールドが期待どおりになることを確認した。

  checksum=10 / imu_yaw_deg=-89.85 / camera=(0,0,0,0) / byte14 が単調増加

模擬 crane -> cm4_sim -> simulator-cli の3者連結も再確認済み
(目標まで 1.803 m -> 0.010 m、multicast 再配信 752 パケット、警告ゼロ)。

## 残る忠実度ギャップ

byte 3 は実機では AI 指令の check_counter エコーだが、IbisFeedbackAdaptor は
指令ストリームを見ないため自走カウンタを送る。陳腐化検出には使えるが、
特定の指令とは対応しない。コメントに明記した。
…rdware

ibis feedbackのレイアウトを実機STM32へ整合+パケットtapツール追加
crane-side セッションの実測で、シナリオテスト環境では --ibis-use-referee が
feedback を完全に停止させることが分かった。チーム色が解決するまで
IbisFeedbackAdaptor::handleGroundTruth が early return するため、
rcst / autoref-tigers が送る referee にチーム名 ibis が載っていない環境では
feedback が 1 パケットも出ない(実測 0 対 2480)。

新構成では feedback が位置制御ループ内で唯一の位置信号なので、これは
ロボット側の位置制御が永久に閉じないことを意味する。

## より危険な既定値の問題

--ibis-use-referee を外すと feedback は流れるが、m_ibisIsBlue の既定が true
のため無条件に BLUE として扱われる。crane が team:=Yellow で動いている場合、
feedback は敵チームの ground truth を運ぶ。パケットは正常に流れ続けるので
失敗が完全に silent になる。

## 対応

--ibis-team-color blue|yellow を追加し、referee に依存せず色を確定できるようにした。

起動時のログで3つの状態を区別する。
- 明示指定: どちらに設定されたかを出す
- referee 検出: 解決するまで feedback が出ないことを明記する
- どちらも無し: BLUE と仮定する旨を WARNING で出す(従来は無言だった)

解決待ちのログも「NO FEEDBACK IS BEING SENT」と結果を明示する文面に変えた。
従来は待っていることだけを伝えており、feedback が止まっている事実は
読み取れなかった。

## 検証

  --ibis-team-color blue   robot0: (+4.300, -2.800)
  --ibis-team-color yellow robot0: (-4.300, -2.800)   ← 反対サイド
  (指定なし)               robot0: (+4.300, -2.800)   ← blue と一致

指定なしが blue と一致することで、既定値の罠も裏付けた。
ibisチーム色を明示指定する --ibis-team-color を追加(PR #4 の取りこぼし分)
cm4-side セッションからの指摘を実機ソースと突き合わせて確認した。
simulator-cli は is_vision_available をデシリアライズするだけで使っておらず、
実機の STM32 メイン基板が車輪を止める条件を再現できていなかった。

実機 G474_Orion_main/Core/Src/state_func.c:314 の停止条件:

  sys->stop_flag
  || ai_cmd->stop_emergency
  || !ai_cmd->is_vision_available
  || ai_cmd->elapsed_time_ms_since_last_vision > 500

simulator-cli が見ていたのは stop_emergency だけで、残り2つを無視していた
(elapsed_time_ms_since_last_vision に至ってはデシリアライズもしていなかった)。

## なぜ重要か

cm4_sim を挟む新構成では CM4 側でも同条件で止めるようになったが、
A/B 比較の基準側(crane から simulator-cli への直送、mode 3)には CM4 が
経路に存在しない。そこでシミュレータが止めなければ、実機なら停車する条件で
シミュレータのロボットだけが走り続ける。

とくに無線劣化注入(パケットロス)を入れると is_vision_available と
elapsed_time_ms_since_last_vision はまさに発火するフィールドなので、
A/B の数値そのものが意味を失う。

## 変更内容

- ibisDeserialize() が latency_time_ms と elapsed_time_ms_since_last_vision を
  復号するようにした(byte 18..21。これらは +/-range の float 符号化ではなく
  素の uint16 なのでゼロ埋めは 0 として復号される)
- 停止判定を ibisShouldStop() に切り出し、実機と同じ3条件にした
  (sys->stop_flag はシミュレータに対応物が無い)

## 検証

実機の境界値を含めて確認した。

  [OK] vision あり・新鮮   flags=0x01 elapsed=  0ms -> moved 0.422 m
  [OK] vision なし         flags=0x00 elapsed=  0ms -> moved 0.001 m
  [OK] vision 古い(600ms)  flags=0x01 elapsed=600ms -> moved 0.001 m
  [OK] vision 境界(500ms)  flags=0x01 elapsed=500ms -> moved 0.423 m
  [OK] STOP_EMERGENCY      flags=0x09 elapsed=  0ms -> moved 0.001 m

500ms で動き 600ms で止まることで、実機の `> 500` という境界も一致している。

ibis-chain-smoketest と cm4_sim を挟んだ3者連結も通過
(目標まで 1.803 m -> 0.010 m、警告ゼロ)。
実機G474と同じvision喪失時の停止条件を再現
simulator-cli は指令の vision_global_pos をフィールド上のロボット位置と
突き合わせてチームと機体を判定しており、0.5m 以上ずれると指令を破棄していた。
この破棄が完全に無音だったため、パケットは届き続け check_counter も進み警告も
出ないのにロボットだけが動かない、という状態になっていた。「その場で静止せよ」
と指令されている状態と区別が付かない。

1秒/台 のレート制限付きで、最も近い候補までの距離を添えて警告するようにした。
距離を併記することで「推定が少し古いだけ」なのか「まったく別の場所を指して
いる」のかを切り分けられる。該当IDのロボットがまだフィールドに居ない場合は
別の文言を出す(vision 未受信と推定ずれは原因も対処も違うため)。

A/B比較にとってこの破棄は非対称な罠である。cm4_sim は --vision-echo-feedback
で feedback 由来の実位置を vision_global_pos に上書きしてから下流へ流すので
新構成側は免疫があるが、基準側(crane → simulator-cli 直送)は crane の
world model 推定をそのまま送るため無防備になる。推定が 0.5m ずれると基準側
だけが指令を失って動かず、A/B の差が制御方式の優劣ではなく破棄の有無で
決まってしまう。

実測(-g 2020B --realism None、mode 3 で3秒指令):
  ずれ 0.00m -> 移動 1.47m / 警告 0行
  ずれ 0.40m -> 移動 1.61m / 警告 0行
  ずれ 0.60m -> 移動 0.00m / 警告 3行(1秒/台のレート制限どおり)
  ずれ 3.00m -> 移動 0.00m / 警告 3行

スモークテストに3項目を追加(位置一致時に偽陽性が出ないことの確認を含む)。
docs/robot-side-position-control.md に「指令の位置照合」節を追加した。
feat(simulator): 位置照合の失敗で指令を破棄したことを警告する
stdout がパイプやファイルのときブロックバッファリングされ、log() は flush
しないため、SIGTERM で書きかけのログが丸ごと失われていた。Docker・systemd・
テストハーネスはいずれも stdout がパイプなので、実運用の経路すべてで起きる。

異常時こそログが消えるのが問題で、実際に公開イメージの動作確認で
「破棄警告 0 行」という誤った結論を一度出している(実際には破棄されていた)。
ログファイルは 0 バイトだった。

main() の先頭で setvbuf(stdout, nullptr, _IOLBF, 0) を呼び、呼び出し側が
stdbuf -oL を付けることに依存しない形にした。公開イメージの entrypoint は
tini なので、呼び出し側に任せると compose の書き方次第で消える。

実測(同一条件、setvbuf の有無だけが違う。stdbuf 無しで起動し SIGTERM):
  修正前: 0 バイト / 0 行
  修正後: 1049 バイト / 6 行(破棄警告 3 行を含む)

スモークテストの stdbuf は古いバイナリでも動くよう残し、コメントを実態に
合わせた。docs/robot-side-position-control.md に節を追加。

Orion_CM4 も同じ理由で cm4_sim / ai_cmd_v2 に setvbuf を入れている(d61a94d)。
fix(simulator): ログを自前で行バッファリングし異常時に消えないようにする
PR #8 で stdout と一緒に stderr にも setvbuf(_IOLBF) を入れたが、これは
目的に対して逆向きだった。glibc は stderr を既定で「バッファ無し」にしており、
_IOLBF はそれより弱い設定になる。改行で終わらない出力を抱え込むようになるため、
「異常終了時にログを残す」という PR #8 の目的に反する。

実測(改行なしで stderr へ出力してから SIGTERM。残ったバイト数):
  既定(glibc, バッファ無し): 27
  setvbuf(_IOLBF)          :  0

stdout の行バッファリングは PR #8 のまま維持する。stdout はパイプ・ファイル時に
ブロックバッファリングされるので _IOLBF が必要で、stderr は既定で十分、という
非対称が正しい。

Orion_CM4 も同じ理由で stderr の setvbuf を外している(8f2c646)。
fix(simulator): stderrへのsetvbufをやめ既定のバッファ無しに戻す
位置照合で指令が破棄されたとき、警告は "the robot will not move until they
agree" と出していたが、実測ではロボットは 1.5 m/s から 0.573 m 進んでから
停止する。「動かない」を信じて原因を探すと誤った場所を見ることになるため、
"coasts to a stop and stays there" に改める。

あわせて、実測で判明した 2 つの忠実度ギャップを docs に追記する。

1. 指令が途切れた間の停止は摩擦による惰走であり能動制動ではない。
   SimRobot::begin() は最後の指令から 0.1 s で standby に入り、車輪 PID の
   手前で return するため駆動力が一切かからない。同じ 1.5 m/s からの停止でも
   mode 3 の r=0(能動制動)なら 0.126 m、指令途絶なら 0.573 m と 4.5 倍違う。
   位置照合による破棄も経路劣化による欠落も同じ経路を通るので、シミュレータの
   停止距離をそのまま実機の挙動として読んではいけない。

2. feedback の速度フィールドは指令が届いたときしか更新されない。速度は
   RadioResponse 由来のキャッシュで、RadioResponse は指令が届いたときだけ
   生成される。一方で位置は毎周期 vision から詰め直されるため、指令が破棄
   されている間は同じパケットの中で位置だけが新鮮で速度は凍る。実測でも
   ロボットが約 1.0 s で停止した後も速度は 1.461 m/s を返し続けた。cm4_sim は
   位置だけでループを閉じるので制御には影響しないが、速度を見る診断は騙される。
ibisShouldStop()(stop_emergency / vision 断 / vision タイムアウト)が
ゼロ速度の move_command を出していたため、シミュレータでは車輪 PID が効いて
1.5 m/s から 0.133 m で止まっていた。実機 G474 はこの条件で omniStopAll()
に入り(state_func.c:314)、4輪の電圧を 0 にするだけで(omni_wheel.c:62)、
CAN フレームは duty の float のみでブレーキビットを持たない(actuator.c:12)。
つまり実機は制動ではなく惰走する。

シミュレータが実機より 4.3 倍短い距離で止まるということは、安全停止の検証が
実機より良く見えるということで、方向として危険な側に外れている。CM4 の
applySafetyStop() は STOP_EMERGENCY を立てるので、crane 断・feedback 断・
vision 断の安全停止がすべてこの経路に入る。

move_command を出さないことで SimRobot の !has_move_command() 早期 return に
落ち、車輪 PID を通らず惰走する。指令途絶時の standby 経路と同じ道になる。
実測で 0.571 m となり、指令途絶時の 0.573 m と一致した。指令自体は届き続ける
ので feedback の速度は正しく 0 まで減衰する。

prev_vx/prev_vy のクリアは残した。これは mode 3 の加速度制限の状態であって
move_command とは無関係で、消すと停止解除後の最初の指令が停止前の速度から
ランプしてしまう。

mode 4 が届いた場合の停止は能動制動のまま変更していない。この分岐は実機に
対応物が無く、構成ミスでシミュレータまで mode 4 が来たときの合図なので、
惰走させると発見が遅れる。
fix(simulator): 安全停止を実機と同じ惰走にし、停止挙動を実測に合わせて訂正
PR #10 で記載した停止距離(能動制動 0.126 m / 緊急停止 0.571 m /
指令途絶 0.573 m)は誤りだった。測定時にロボットが壁や他のロボットに
当たっており、自由な惰走を測れていなかった。

ロボットの開始位置は y = -2.8 付近で、-y は壁、±x は他のロボットに塞がれて
いる。塞がれた向きで測ると停止距離が 0.12〜0.40 m の範囲で不規則にばらつく
のだが、値そのものは「それらしく」見えるため、読みからは異常と分からない。
実際 PR #10 の 0.571 と 0.573 は互いによく一致していたので、同じ経路を通った
証拠として扱ってしまった。偶然に過ぎなかった。

+y 方向で blue と yellow の両方で測り直した結果(1.45 m/s から):

| 停止のかけ方 | 停止距離 |
|---|---|
| mode 3 で r = 0(能動制動) | 0.13 m |
| STOP_EMERGENCY / vision 断(惰走) | 0.63 m |
| 指令が届かない(惰走) | 0.76 m |

両チームの差は 0.001 m 以内で、自由惰走であることが確認できる。

あわせて、緊急停止と指令途絶が一致するという記述も訂正した。指令途絶の方が
0.13 m 長いのが正しい。SimRobot は最後の指令から 0.1 s 経つまで前の指令を
実行し続けるので、その間は駆動力がかかっているため。この 0.1 s は実機の
250 ms(connected_ai タイムアウト)より短いので、指令途絶時の惰走距離は
シミュレータの方が実機より短く出るという忠実度ギャップも追記した。

測定手順を data/scripts/ibis-stop-distance.py として残す。+y 方向で測ること、
1 測定 1 プロセスにすること(速度フィールドの凍結を前の測定から持ち越すと
加速せずに測定に入ってしまう)を組み込み、両チームの一致も検査する。

PR #10 のコード変更(緊急停止を惰走にする)自体は正しく、変更していない。
fix(docs): 停止距離の実測値を測り直して訂正し、測定スクリプトを追加
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant