# Program00 轉向流程與旗標參考

本文記錄提交 `6b0b32cd013409a328a92c89b0d348b7e864337c` 的 `navigation_core` 實際行為，供重寫狀態機時對照。數值以這份程式碼的預設值為準；執行期設定仍可能覆寫可調參數。這是單一 Program00 導航實作的追蹤，不代表 ESP32 雙控制器專案的 UART 工作細節。

主要來源：[狀態宣告](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00_state.h#L4)、[Program00 狀態與宣告](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00.cpp#L99)、[直線與定位控制](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L7)、[轉向與執行器](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L272)、[轉向政策](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/turn_control.h#L8)、[IMU 積分與修正](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/imu_turn_control.h#L8)、[參數預設值](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/config/program00_config.h#L41)。

## 目前流程

車輛沿直線行進時，`updateStraightSegmentTracking()` 以編碼器進度推算下一格中心。一般 DFS 決策點在中心前 `ENCODER_COUNT_TOLERANCE`，姿態與格子只有到名義中心才提交；側邊開口經確認時，則可能提早到開口邊緣加偏移的位置做決策。決策資料就緒後，該函式設定 `CallDfs`。DFS 結果由上層協調者處理；它把絕對目標方向寫入 `navigation.pendingHeading`，並設定 `hasPendingTurn`、回溯資訊與轉向交易資料。協調者就是 `runProgram00()`：先執行 `checkTimeFlag()`，再清除並消費 `CallDfs`，接著處理待轉向與PWM。完整排程見[主流程第4章](FINAL_MAIN_FLOW_AND_REFACTOR_GUIDE.md#4-主-loop-與-10-ms-tick-的真正邊界)。[決策點與中心提交](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L2873) · [設定待轉向資料](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L1630)

```mermaid
flowchart TD
  A["直線編碼器進度與感測觀察"] --> B["決策點到達或確認側邊開口"]
  B --> C["設定 CallDfs"]
  C --> D["外層協調者執行 DFS"]
  D --> E["記錄絕對目標 heading 與 pending turn"]
  E --> F["pendingTurnNeedsApproach 選出定位條件"]
  F --> G["handlePendingTurn 到達實際觸發點"]
  G --> H["同步執行 blocking 轉向 helper"]
  H --> I{"所有轉向成功條件成立？"}
  I -->|"否"| J["清除 pending 狀態並停止導航"]
  I -->|"是"| K["提交格子與 heading"]
  K --> L["建立新直線基準，啟動轉後保護"]
```

`handlePendingTurn()` 先用 `(targetHeading + 4 - currentHeading) & 3` 換算相對動作：`0` 直行、`1` 右轉、`2` 原地迴轉、`3` 左轉。直行只清除 pending turn，不跑旋轉程序。若上一個尚未離開新格的動作也是迴轉，程式會視為 DFS／地圖矛盾而停車。[方向換算與迴轉矛盾檢查](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1225)

## 定位條件與轉向觸發

轉向決策與馬達旋轉是兩件事。DFS 可先選好方向，車體再沿原航向走到指定物理參考點，最後才執行旋轉。參考點優先順序如下：

| 參考點 | 使用條件與觸發 | 程式行為 |
|---|---|---|
| 已鎖定的側邊地標 | 側邊開口已確認，且沒有更早成立的前牆或地圖前牆排除該地標；進入目標格時有特例 | 開口邊緣編碼器進度加左／右開口偏移（預設各 11 counts）。地標鎖定後，較晚到來的 Echo 不會搶走其觸發權。 |
| 前方牆面 | 決策快照確認前牆，且不是要採用側邊地標 | 以有效前方 Echo 接近前牆；一般 90 度轉向預設要低於 90 mm 門檻才觸發，已驗證死巷迴轉則在 90 mm 以下（含門檻）觸發。 |
| 編碼器位置 | 前方開放或使用 encoder fallback | 一般 90 度轉向目標為名義格中心前 5 counts；迴轉目標是格中心。側邊地標會使用自己的邊緣加偏移位置。 |

前牆轉向若缺少有效 Echo，ESP32 分支會先停車，等待最多 100 ms 與至少兩筆新讀值。一般左／右轉最多做一次慢速 3-count 倒退，再重新等新 Echo；若符合安全條件，可在「中心到中心加容差」的範圍內改用編碼器位置觸發。已驗證死巷迴轉不倒退，也不使用這個 encoder fallback；等待失敗就結束導航。開始等待前，非同步馬達連線必須先回報已套用 0/0，等待計時才會起算。[觸發參考選擇](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1051) · [Echo 等待與復原](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L898) · [觸發門檻](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1288)

剛轉完後若立即又有轉向，第一個新直線區段至少要前進預設 35 counts；目標會截在下一個名義格中心。只有有效 Echo 確認的死巷迴轉可提早解除這道門檻，並把實際接近進度補償到下一次門檻。一般「回溯」本身不足以認定死巷，還需前、左、右都是 Wall，且左右觀察有效。[迴轉與連續轉向政策](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/turn_control.h#L108) · [預設值](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/config/program00_config.h#L56)

## 旋轉執行與完成條件

正式旋轉前會停止直線段追蹤並要求馬達 0/0。轉向 helper 是同步阻塞函式：`turnLeftBlocking()`、`turnRightBlocking()` 包裝單段 `turn90EncoderBlocking()`；ESP32 上該 helper 透過非同步輸出介面等待輸出套用或命令送出，等待期間仍在函式內執行迴圈與短延遲。這種馬達輸出傳輸的非同步性，不會讓導航呼叫端的轉向函式變成非阻塞狀態。本附錄不追查外部傳輸工作本身。[直線追蹤停止與執行分派](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1565) · [單段轉向選路](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L600)

預設 `IMU_TURN_ENABLED=1`、`IMU_TURN_REQUIRED=1`。MPU-6050 可用時，每段 90 度轉向都走 IMU 路徑；感測器未就緒時回報失敗並停止，不會默默改用編碼器路徑。只有關閉 required 或未啟用 IMU，才可能走 encoder-only 路徑。[IMU 選路與必要性](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L600) · [設定預設](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/config/program00_config.h#L176)

IMU 單段流程如下：

1. 先要求停止輸出；ESP32 等待從馬達狀態確認套用 0/0，再重設編碼器與計時起點。
2. 設定左／右旋轉方向，雙輪以轉向 PWM（預設 100）輸出，並等到該方向的輸出已套用。
3. 讀取修正後的陀螺儀 Z 軸，依旋轉方向與 `IMU_GYRO_Z_SIGN` 正規化，再依取樣時間積分相對角度。主轉動在達到最低角度或理想轉動時間時停止，最長 5000 ms。
4. 停止輸出並等 0/0 套用，等候穩定時間（預設 12 ms）後取樣。角度小於 89.0° 時朝原方向補脈衝；大於 91.0° 時反向補脈衝；落在 89.0° 至 91.0° 才是目標範圍。預設細脈衝 20 ms；仍差超過 10°、20°、30° 時，朝原方向脈衝分別放大為 1.5、2、3 倍，反向修正維持細脈衝。
5. 修正階段沒有總經過時間上限，但每次命令傳輸／停止確認各有等待上限，且最多 100 次修正。陀螺儀讀取失敗、飽和、馬達輸出故障、編碼器最大轉數超過 `target × 4 + 20`，或修正脈衝用盡都會使該段失敗。
6. `reachedTarget` 還要求感測器健康、角度在範圍、未觸發編碼器防護、旋轉輸出曾成功套用，而且沒有輸出故障。編碼器落在目標容差內是附帶證據，不取代 IMU 角度成功條件。

[IMU 主轉與修正](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L272) · [修正量與成功判定](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L387) · [IMU 整合與修正方向](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/imu_turn_control.h#L29)

若走 encoder-only 路徑，起步、停止與每個修正命令同樣要求輸出確認。雙輪先以相同 PWM 轉過預設理想時間，再等待編碼器進入目標容差；未到下界時朝原方向補脈衝，超過上界時反向補脈衝。欠量較大時使用 15 ms 基本脈衝的 4 倍或 2 倍，接近目標時回到基本脈衝；超量也只用基本脈衝。此路徑受 5000 ms 總限時約束。成功要求目標達成、旋轉輸出成功套用且全程無輸出故障。[encoder-only 路徑](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L624) · [脈衝政策](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/turn_control.h#L267)

### 原地迴轉

原地迴轉由兩段 90 度轉向加兩段直線反向動作組成。順序是：先要求停車並以雙輪倒退到至少 1 count；第一段 90 度；再要求 0/0、倒退預設 50 ms；第二段同方向 90 度。預設兩段都向左，每段目標 25 counts、容差 20 counts、理想時間 300 ms，並各自套用 IMU 驗證與該段 5000 ms 主轉上限。任何一步失敗，後續步驟不執行，整個轉向宣告失敗。入口倒退另有 250 ms 停車套用上限。轉後邏輯進度加入入口實測倒退 counts 和預設 `-10` counts 出口偏移；兩段旋轉與中間倒退不納入新編碼器基準。[迴轉順序](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L798) · [迴轉呼叫鏈](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1571) · [出口進度偏移](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1672)

## 成功、失敗與轉後定位

任一必要的旋轉段或迴轉倒退失敗，`handlePendingTurn()` 設定 `isDFSFinished`、清除 `isMotorRunning` 與 pending turn，清除轉向交易、PWM 遙測歸零並呼叫 `stopMotors()`。成功時才會：標記走過的局部通道、將決策格寫入 `carX/carY`、把 `carHeading` 更新為目標絕對方向、清除 pending turn、提交任務路線格、建立新的直線追蹤基準，並要求下一段新 Echo。非 tentative probe 會選一側做轉後 IR 重錨；回溯路徑若沒有已知 Open-to-Wall 地標，不會臆造重錨邊緣。最後把 `lastTick` 設為當前時間，避免阻塞期間錯過的 10 ms 更新被補跑。[成功／失敗分支](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1620) · [轉後初始化與 TURN_DONE](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1638)

轉後 IR 重錨只在新直線段尚未觸發 DFS、尚未提交格子時有效。它要求先觀察到可信的 Open，再觀察到 Wall；預設需連續 2 筆 Open、3 筆 Wall，且取樣窗也要確認 Wall。候選位置最多使用 30 counts 範圍，對新段編碼器基準的修正會截在正負一個 `ENCODER_COUNT_TOLERANCE`。成功會重設觀察窗，只把已證實的前一段 Wall 當作下一次 Wall-to-Open 偵測的前態，不沿用舊樣本。超過範圍且沒有 Wall 候選時，重錨標記為未確認並重新開始觀察。[轉後重錨更新](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L2677) · [重錨參數](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/config/program00_config.h#L168)

轉向與重錨狀態可簡化為：

```mermaid
stateDiagram-v2
  [*] --> PendingDecision
  PendingDecision --> Approach: "有 pending heading 且定位條件未到"
  Approach --> WaitFreshEcho: "前牆觸發但沒有新 Echo"
  WaitFreshEcho --> Approach: "兩筆新 Echo 到達"
  WaitFreshEcho --> EncoderFallback: "一般 90 度轉向且位置仍安全"
  WaitFreshEcho --> Failed: "無安全復原條件"
  Approach --> Execute: "參考點到達"
  EncoderFallback --> Execute: "中心位置通過安全檢查"
  Execute --> Failed: "輸出、IMU、編碼器或倒退步驟失敗"
  Execute --> Commit: "每段旋轉均成功"
  Commit --> Reanchor: "一般已提交轉向"
  Commit --> ProbeHold: "tentative side probe"
  Reanchor --> Complete: "觀察重錨結束或被略過"
  ProbeHold --> Complete: "交由 probe 復原流程"
  Complete --> [*]
  Failed --> [*]
```

## 六個 `FuncFlags`

旗標是 Program00 的軟體流程狀態。它們本身不能證明實際馬達已停、PWM 已套用、車體已到中心、感測器有效或 DFS 已完成。ESP32 的輸出套用仍需馬達狀態確認；感測條件另由讀值、修訂序號與結果結構驗證。[旗標索引](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00_state.h#L4) · [輸出確認例](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L127)

| 旗標 | 寫入／清除位置（本次追蹤範圍） | 對狀態機的意義 | 不是什麼 |
|---|---|---|---|
| `isMotorRunning` | `motorStart()` 設為 true；轉向等待、錯誤與停止路徑設為 false。 | Program00 是否將導航視為行進中。IR steering 與 stall 判斷會讀取它。 | 不是硬體回報的馬達運轉證明。 |
| `isNeedToCenter` | `motorStart()` 與轉向成功後設為 true；`driveStraightWithoutCentering()` 設為 false。 | 表示中心修正路徑的軟體狀態；觀察與 IR steering 會在它為 true 時更新。`runProgram00()` 在未被 hold 時依此旗標選 `centerCar()` 或 `driveStraightWithoutCentering()`。 | 不代表已經抵達格中心；中心提交由編碼器進度與 DFS 旗標條件決定。 |
| `isDFSFinished` | 初值 false；DFS 終止、任務完成、回溯／轉向安全錯誤時設為 true。`resetProgram00()` 將所有 FuncFlags 重設為 false。 | 導航流程已進入終止狀態，停止直線跟蹤並停止輸出。新任務由生命週期模組重設。 | 不等於已完成完整探索；錯誤停止也會設成 true。 |
| `isRightway` | 只在 `program00_state.h` 列為索引；指定追蹤檔案沒有讀寫。 | 這份轉向流程沒有可證實的作用。 | 不從名稱推定方向或路徑正確。 |
| `NeedToSlowDown` | stall／超音波復原與前牆 Echo 復原會設 true；成功完成轉向後設 false。 | 速度策略仍需維持慢速；直線決策也據此避免恢復一般速度。 | 不代表 PWM 已實際降下來；PWM/套用狀態另由命令與回報管理。 |
| `CallDfs` | 直線感測決策處與 stall 重試路徑設 true；`runProgram00()` 於 tick 返回後先清除此旗標，再呼叫 `nextNavigationDecision()`。 | 提醒協調者用已保存的格子座標與感測值執行一次 DFS 決策。中心提交明確要求此旗標已被消化。 | 不代表 DFS 已執行，也不包含 DFS 結果。 |

定義與初始值見 [旗標宣告](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00_state.h#L4) 及 [全域初始化](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00.cpp#L392)；主要寫入點見 [motion helpers](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L47)、[直線決策與提交](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L3339)、[轉向結果處理](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1620)。

## 可供重寫採用的其他狀態群

`TimeFlags` 另有 `isReadLeft`、`isReadRight`，但全核心搜尋只找到宣告、陣列與重設，沒有有效讀寫或排程用途。真正的 10 ms 節奏由 `lastTick` 判斷，左右 IR 在同一 tick 一起讀取。`isRightway` 也沒有實際使用，重寫時不必因舊名稱而再造功能。

這些欄位比六個 `FuncFlags` 更接近轉向流程的明確資料模型。重寫時建議依用途分組，不把它們合併成「轉彎中」單一布林值。

| 狀態群 | 關鍵欄位 | 用途 |
|---|---|---|
| `NavigationState` | `pendingHeading`、`hasPendingTurn`、`pendingWasBacktracking`、`backtrackingActive`、`previousTurnWasAround`、`goalReached`、`turnExitVerification` | 保存目標絕對 heading、待執行動作、回溯屬性與轉後驗證狀態。 |
| `StraightSegmentTracking` | 起始 encoder、`logicalProgressOffsetCounts`、`committedCells`、決策格與 F/L/R wall/observe 快照、側邊地標目標、`dfsTriggeredForNextCell`、`observationHoldPosition`、`postTurnFirstCellSlow` | 把直線路段位置、臨時決策格、物理 hold、中心提交與轉後門檻分開追蹤。 |
| `CellObservation` | 左右狀態、兩窗確認、開口邊緣進度與確認、前 Echo revision、前牆狀態 | 描述「觀察到什麼」及新資料是否晚於 DFS 決策；不代替導航命令。 |
| `TurnEntryTransaction` | 起點格、原 heading、嘗試 heading、相對動作、DFS pathDepth 前後、探索／回溯／tentative 標記 | 在尚未安全通過新通道時保留可回復的轉向交易資訊。 |
| `EncoderTurnResult` | 實際 signed/absolute counts、耗時、修正次數、`reachedTarget`、`encoderInRange`、輸出套用／故障 | 單段 helper 的回傳證據。IMU 成功路徑還另外要求 IMU 角度有效；不可只看 `encoderInRange`。 |
| `TurnExitReanchorState` | 選定側、起始 encoder、候選 Wall encoder、連續 Open/Wall 數、是否見過可信 Open/window | 控制轉後位置重錨，避免把轉彎期間或舊格樣本當成新格觀察。 |
| `frontTurnEchoWait` 與 `FalseTurnRecovery` | Echo wait 起始時間／revision／是否已倒退；復原階段與累積倒退量 | 讓無 Echo、假路徑與 tentative probe 復原各自有界，不把它們當一般 turn success。 |

欄位定義集中在 [狀態結構](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/program00.cpp#L139)；位置與提交由 [直線追蹤](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L2829) 管理，待轉向流程由 [轉向處理器](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1225) 管理。

## RunLog 位元與解讀

下面的 flags 是不同 `RunLog` record 各自的欄位，不是共用 enum。低位元的相對方向多採 `0=直行、1=右、2=迴轉、3=左`，但每種 record 的高位元含義不同；解析時必須先看 record type。此處列出轉向重寫最有用的 record。

| Record | flags 位元 | 意義 |
|---|---|---|
| `RunLogTurnCommand` | bits 0–1 相對動作；bit 2 前牆觸發；bit 3 回溯；bit 4 側邊地標觸發；bit 5 決策後融合的新前牆；bit 6 側邊地標與前牆衝突；bit 7 encoder fallback。 | 記錄「為何在這裡啟動哪種轉向」。 |
| `RunLogTurnInitial` | bit 0 左轉；bit 1 初始旋轉輸出曾套用；bit 2 輸出故障。 | 起始旋轉階段結果。 |
| `RunLogTurnBrake` | bit 0 左轉；bit 1 停止輸出已確認；bit 2 IMU 取樣健康。 | IMU 主旋轉後的煞停與穩定取樣狀態。 |
| `RunLogTurnCorrection` | bit 0 目標動作是左轉；bit 1 此次脈衝實際向左；bit 2 停止已確認；bits 3–7 脈衝序號，最高飽和至 31。 | 可分辨原方向補不足與反向修過頭。 |
| `RunLogTurnEncoder` | bit 0 左轉；bit 1 曾修正；bit 2 encoder 在範圍；bits 3–7 修正脈衝數（最多記到 31）。 | encoder 結果與脈衝數；不單獨代表 IMU 路徑成功。 |
| `RunLogTurnGuard`（ESP32） | bit 0 左轉；bit 1 最大 count 非零；bit 2 未超過 `target × 4 + 20`；bit 3 signed counts 為零；bit 4 輸出曾套用；bit 5 輸出故障；bit 6 整段成功。 | IMU 轉向的 encoder/輸出護欄。 |
| `RunLogTurnImu` | bit 0 左轉；bit 1 曾修正；bit 2 到達目標；bit 3 MPU I2C fault；bit 4 gyro 飽和；bit 5 encoder guard；bit 6 主轉逾時或修正次數上限；bit 7 馬達輸出故障。 | IMU 最終判定與失敗原因。IMU 必要但未就緒的早退紀錄只有方向與 I2C fault 位元可用。 |
| `RunLogAroundEntryReverse` | bit 0 entry reverse 成功；bit 1 倒退輸出套用；bit 2 輸出故障；bit 3 encoder 目標到達；bit 4 開始前停止確認；bit 5 最後停止確認。 | 迴轉第一段倒退的明確檢查點。 |
| `RunLogTurnReanchor` | bit 0 左側（清除代表右側）；bit 1 成功 anchor；bit 2 曾有確認 Open；bit 3 曾有確認 Open window；bit 4 encoder 基準修正曾被截限；bit 5 成功完成。 | 成功 anchor 記錄會有 bits 1、5；超範圍略過記錄沒有這兩個成功位。 |
| `RunLogTurnEchoWait` | bits 0–1 相對動作；bit 2 開始等待；等待成功記錄 bit 3 加 bit 7；encoder fallback 記錄 bit 4 加 bit 5；故障停止記錄 bit 5、bit 6，bit 7 表示 `frontReady`。 | 同一 type 在不同階段重用高位元，需搭配低位方向及相鄰記錄讀取。 |
| `RunLogConsecutiveTurnGuard`（ESP32） | bits 0–1 相對動作；bit 2 hold；bit 3 release；bit 4 有效 around wall 提早放行；bit 5 記錄補償後門檻。 | 轉後連續轉向的直線進度門檻。 |
| `RunLogTurnDone` | bits 0–1 相對動作；bits 2–3 完成後絕對 heading。 | 只在全部轉向步驟成功、pose/heading 已提交後寫入。 |

位元組合的來源分別是 [IMU／encoder 記錄](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L549)、[Echo wait 記錄](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L922)、[轉向命令／完成記錄](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1551) 與 [重錨記錄](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_motion.h#L2700)。`RunLogTurnDrift` 是轉後短期漂移診斷，供分析用，不是轉向成功條件。[漂移診斷](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L17)

## 非阻塞 FSM 草案（提案，非目前實作）

若要把流程改成每次主迴圈只推進一步，可保留上述判定與資料，新增明確階段 enum，將同步 helper 改為 `begin/poll` 介面。此圖是設計提案；現有程式仍呼叫 blocking helper。

```mermaid
stateDiagram-v2
  [*] --> Idle
  Idle --> Positioning: "DFS 輸出 pending heading"
  Positioning --> WaitStopAck: "參考點到達且命令停止"
  WaitStopAck --> WaitFreshEcho: "前牆模式且 Echo 無效"
  WaitStopAck --> StartLeg: "入口0/0 已套用且觸發有效"
  WaitFreshEcho --> Positioning: "新 Echo 有效或一般轉向可安全 fallback"
  WaitFreshEcho --> Failed: "等待與有界復原耗盡"
  StartLeg --> WaitOutputAck: "送出方向與 PWM"
  WaitOutputAck --> Integrate: "輸出已套用"
  WaitOutputAck --> Failed: "套用逾時或 transport fault"
  Integrate --> StopLeg: "主轉時間到、角度到或修正脈衝結束"
  Integrate --> Failed: "感測、時間或 encoder guard 失敗"
  StopLeg --> LegStopAck: "要求本段停止"
  LegStopAck --> Settle: "新的0/0確認"
  LegStopAck --> Failed: "停止確認逾時"
  Settle --> CorrectPulse: "角度未合格且可修正"
  CorrectPulse --> WaitOutputAck: "送有界修正脈衝，保存脈衝期限"
  Settle --> NextStep: "本腿角度與護欄合格"
  NextStep --> StartLeg: "尚有下一個90度腿，先完成有界reverse"
  NextStep --> Commit: "所有子步驟成功"
  Commit --> Reanchor: "建立新直線段與轉後驗證"
  Reanchor --> Complete: "驗證完成或合法略過"
  Failed --> [*]: "輸出清零並關閉導航，回報失敗"
  Complete --> [*]
```

FSM 的一項重要規則是輸出設定、輸出已套用、感測器判定、位置提交要各有狀態。不要用 `isMotorRunning` 代替 acknowledgement，也不要在「最後一段轉向命令送出」時提前提交 heading。原地迴轉要把兩段 90 度與兩段 reverse 當作同一筆未提交交易；任一步失敗都走相同 fail-stop 終態。每次停車、轉向、倒退與 Echo 等待都沿用現有 timeout／次數上限；唯一沒有 aggregate timeout 的是 IMU 修正迴圈，但它仍受 100 脈衝與每個輸出確認 timeout 約束。[阻塞 helper 的輸出確認](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L127) · [整體停止分支](https://github.com/ccccchhhheeenng/mouse_maze_solver/blob/6b0b32cd013409a328a92c89b0d348b7e864337c/navigation_core/src/navigation_core/modules/program00_turn.h#L1620)
