3 点を反映しました。変更は pi05/run_policy_client.py の settle_target 周りのみ(未コミット)。 変えた行 ┌───────┬──────────────────────────────────────────────────────────────────┐ │ 行 │ 変更 │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ 23-25 │ docstring: 「動きが止まってから測る」「送信回数の上限」「どの終 │ │ │ わり方でも止めずに次へ」 │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ 94-96 │ 定数 3 つ追加: SETTLE_POLL_SEC=0.2 / SETTLE_STILL_DEG=0.2 / │ │ │ SETTLE_WAIT_SEC=2.0 │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ │ read_until_still() を新設(A2)。0.2 秒おきに読み、連続 2 │ │ 203-2 │ 回の差が 6 関節すべて 0.2° 未満で「止まった」。最長 2.0 │ │ 25 │ 秒で打ち切り、そのときの値を返す。途中で読めなくなったら直前の値 │ │ │ 、最初から読めなければ (None, False) │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ 228-3 │ settle_target を書き直し(A5・A6)。下記 │ │ 20 │ │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ 342-3 │ --settle-tol / --settle-retries のヘルプを「止まってから測る」「 │ │ 47 │ 送信回数の上限(中の再送も数える)」に更新 │ ├───────┼──────────────────────────────────────────────────────────────────┤ │ 530-5 │ 呼び出し側は変更なし(rec["settle"], rec["settle_retries_used"] │ │ 31 │ = settle_target(...)) │ └───────┴──────────────────────────────────────────────────────────────────┘ settle_target の中身: - A2: 最初の判定も送り直し後の判定も read_until_still() で読む。その値で |目標 − 実際| の最大を求め、tol 以下で終了、超えていれば次の送り直しへ - A5: ok, retried = _send_and_wait_arrival(...) を受け取り、sends += 2 if retried else 1。ループ条件は sends >= retries で打ち切り(= 送信回数の上限)。ok が False ならその回の記録を残してから send_failed で終了 - A6: 戻り値を dict にし、reason / tol / sends / final_err / joint / final_actual / tries(各回の max_err_before max_err_after joint sends success retried still actual)を残す。画面の最終行に理由・送信回数・最後の最大ずれを出す reason は指定の 5 つに加えて、--settle-retries 0 のときだけ "disabled" を入れています(ログだけを見て「送り直し機能が切られていた」と分かるように)。 不要なら外します。 止める条件は増えていません(settle_target はどの経路でもチャンクループを抜けず、exit_code も触りません)。_send_and_wait_arrival 本体・HOME・SPEED・SEND_WAIT・J4_SAFE・J 1_SAFE_MAX・apply_limits・h・q・グリッパー・ドライランは未変更です。 確認結果(偽の daemon・偽の時計、アームは動かさず) 偽の時計(sleep で進む)を使っているので、2.0 秒の打ち切りを実時間なしで確かめています。開始 j1=60.0° → 目標 j1=50.0°。 1. +2.0° → 送り直すと +0.4°(converged) [SETTLE] 送り直し 1 回目 最大ずれ 2.00°(関節 j1)→ 0.40° [SETTLE] converged (詰まった) 送信 1 回 最後の最大ずれ 0.40°(関節 j1) log: {"reason":"converged","tol":1.0,"sends":1,"final_err":0.4,"joint":1,"final _actual":[0.0,50.4,...]} 2. +2.0° のまま(not_converged) — 送信 2 回で打ち切り、[WARN] を出して終了コード 0 [SETTLE] not_converged (詰まらなかった) 送信 2 回 最後の最大ずれ 2.00°(関節 j1) 3. --settle-retries 0 — settle の read_angles 0 回・送信 0 回(本体の送信 1 回のみ) log: {"reason":"disabled","tol":1.0,"sends":0} 4. 読むたびに値が変わり続ける(0.5°/回のドリフト) — 2.0 秒=10 回ポーリングで打ち切り、そのときの値で判定 [SETTLE] 送り直し 1 回目 最大ずれ 7.50°(関節 j1)→ 5.90° (動きが止まらないまま 2.0 秒で打ち切り) [SETTLE] 送り直し 2 回目 最大ずれ 5.90°(関節 j1)→ 5.90° (動きが止まらないまま 2.0 秒で打ち切り) try に "still": false が残る 5. settle の送信が (False, True) を返す(send_failed) — 送信 2 回と数え、その回でやめる [SETTLE] 送り直し 1 回目 最大ずれ 2.00°(関節 j1)→ 2.00° (送信 2 回) [SETTLE] send_failed (送信が失敗) 送信 2 回 最後の最大ずれ 2.00°(関節 j1) log: {"reason":"send_failed","sends":2,...} try: {"sends":2,"success":false,"retried":true} 終了コード 0(止めずに次のチャンクへ) 5b(追加). (True, True)(中で 1 回再送して成功) — 2 回と数えて上限 2 に達し、1 回の送り直しで not_converged 終了。内部再送が予算に効いていることの確認です。 6. 最初の読み取りに失敗(read_failed) — 送信 0 回で戻る [SETTLE] read_failed (関節角を読めない) 送信 0 回. 送り直しません log: {"reason":"read_failed","tol":1.0,"sends":0,"final_err":null,"joint":null, "final_actual":null} 7(追加). ドライラン(--execute なし) — 送信 0 回、settle キーなし、read_angles は main の分のみ。 ひとつ補足です。ケース 5 で、_send_and_wait_arrival が チャンク本体の送信で (False, ...) を返した場合は、既存の「未到達 → --auto なら停止」が先に働くので settle には入りません(既存の停止条件をそのまま残しているためで、今回の変更で増 えた停止ではありません)。手動モードでは警告のあと settle に入ります。