跳轉到

第 8 章:光流定點懸停

本章學習目標

  • 理解「光流」怎麼讓室內沒有 GPS 的飛機也能定點不漂
  • 看懂光流的三級串級:位置 → 速度 → 角度
  • 知道光流「品質」為什麼關鍵,沒它會怎樣

8.1 問題:為什麼飛機會自己漂走

姿態穩、高度也定了,但你會發現飛機還是會慢慢往某個方向飄——因為一點點傾斜、一陣微風, 都會讓它水平移動,而飛機並不知道自己在水平面上移動了(陀螺/加速度測不出「平移」)。 戶外靠 GPS,室內就靠光流

👁️ 生活化比喻:就像你低頭看地面走路 光流感測器(PMW3901)就是一顆朝下的小相機,比較前後兩張地面影像的位移, 就知道「飛機相對地面往哪、移動多少」——跟你低頭看地面磚就知道自己在走還是站著一樣。


8.2 光流給了什麼資料

光流模組透過串列埠把資料送進來,HARDWARE/flow.cFlow_Receive() 解析出:

HARDWARE/flow.c(已將 GBK 註解翻成正體中文)
mini.flow_x = ((s16)(*(RxBuffer+3)<<8) | *(RxBuffer+2));   // 水平位移 X
mini.flow_y = ((s16)(*(RxBuffer+5)<<8) | *(RxBuffer+4));   // 水平位移 Y
mini.qual   = *(RxBuffer+7);                               // 影像品質
// …
if (mini.qual < 25) mini.ok = 0;   // 品質太差 → 這筆不能用
else                mini.ok = 1;
資料 意義
flow_x / flow_y 相對地面的水平位移(累加起來就是位置)
qual 影像品質:地面太暗、太光滑、太遠都會變差
flow_High 模組量到的高度(第 7 章定高會優先用它)

沒有『品質』把關會很危險

地面若是純白瓷磚、地毯反光、或光線不足,相機看不出位移,資料就是垃圾。 程式用 qual < 25 → ok=0 把爛資料擋掉;Pixel_Flow_Fix()Flow_Err || !mini.ok 時直接清零。 沒有這層把關,飛機會拿著錯誤的位移亂修,反而暴衝。


8.3 三級串級:位置 → 速度 → 角度

定點比姿態多了兩層。完整的指揮鏈是五層串級(HAL/control.c):

flowchart LR
    P["光流位置環<br/>Flow_PosPid"] --> V["光流速度環<br/>Flow_SpeedPid"]
    V --> A["角度外環<br/>pidRoll/Pitch"]
    A --> R["角速度內環<br/>pidRateX/Y"]
    R --> M["馬達"]
HAL/control.c(節錄)
Flow_PosPid_x.measured = pixel_flow.loc_x;          // 外環:目前位置
pidUpdate(&Flow_PosPid_x, dt);
Flow_SpeedPid_x.desired = LIMIT(Flow_PosPid_x.out, -1000, 1000);  // 位置環輸出 → 速度環目標
Flow_SpeedPid_x.measured = pixel_flow.loc_x;        // 內環:目前速度
pidUpdate(&Flow_SpeedPid_x, dt);
pidPitch.desired = LIMIT(Flow_SpeedPid_x.out * 0.1, -15, 15);     // 速度環輸出 → 變成「該傾斜幾度」

讀法:「位置差多少 → 該用多快的速度修 → 該傾斜幾度去產生那個速度 → 角速度 → 馬達」。 每一層的輸出都是下一層的目標,跟第 4、7 章是同一個套路,只是疊了更多層。

注意最後一行 pidPitch.desired = …:光流控制的最終手段,還是傾斜飛機—— 因為四軸要水平移動,唯一方法就是「傾一個角度讓推力產生水平分量」。 所以定點本質上是「用傾角去抵消漂移」,這也是為什麼它必須建立在第 3、4 章的姿態控制之上。

增益(USER/INIT.c):位置環 Flow_PosPid.kp = 2.0、速度環 Flow_SpeedPid.kp = 0.51, kd = 0.40。 和調姿態一樣的原則:先調最內層、再往外


8.4 定點是怎麼「記住原點」的

進入定點時,程式把當下的位置記成目標

HAL/control.c(節錄)
Flow_PosPid_x.desired = pixel_flow.loc_x;   // 把「現在的位置」設為要守住的目標
Flow_PosPid_y.desired = pixel_flow.loc_y;

之後只要飄離這個點,位置環就會算出誤差 → 一路串到馬達,把飛機拉回原點。 鬆開搖桿不動 → 守在原地;推搖桿 → 暫時讓你移動,放手後在新位置重新「定樁」。


8.5 動手驗證

  • 開上位機看 mini.qual:把飛控朝下對著有紋理的地面移動,品質高;對著純白/反光面,品質掉、mini.ok 變 0。
  • flow_x/flow_y:手動把飛機水平平移,數值跟著動;靜止接近 0。
  • 室內低空試飛開定點:鬆開搖桿,看它能不能賴在原地不漂;用手輕推,看它會不會自己飄回來。

8.6 思考題

  • 為什麼光流控制「位置」最後是輸出「傾角」而不是直接控制馬達?(提示:四軸怎麼水平移動)
  • 地面換成一片純白,mini.qual 會怎樣?程式如何避免這時候亂飛?
  • 定點是五層串級,姿態是兩層。為什麼「先調內層再調外層」在這裡更重要?

8.7 本章對應的原始碼

檔案 看什麼
HARDWARE/flow.c Flow_Receive() 解析、Pixel_Flow_Fix() 濾波
HAL/control.c 光流三級串級(Flow_PosPidFlow_SpeedPidpidPitch/Roll
USER/INIT.c 光流各環 PID 增益

下一章:把姿態控制「反過來玩」——讓飛機故意翻滾特技(空翻)模式