2026年11月14日 星期六

Feiyu Gimbal (2): A Web Controller with Limit Protection and Overload Alerts | Feiyu 雲台(二):網頁控制介面、極限保護與「噹噹叫」的過載保護

上一篇把 Feiyu Vimble 3 雲台的藍牙協定從 App 反編譯出來,在樹莓派 4 上用 Python 讓它轉了起來。不過每次都要下指令列,也很容易一路轉到底頂住機構。這篇接著做幾件事:做一個手機就能操作的網頁控制介面、把雲台上手機的鏡頭變成即時影像放進頁面、加上極限保護避免撞到底,以及找出雲台「噹噹叫」到底在警告什麼。最後用手機相機拍照,逐項驗證。

網頁控制介面

伺服器用 Python 的 aiohttp 加 bleak,跑在靠近雲台的樹莓派 4 上,設成 systemd 使用者服務開機自動啟動。它常駐保持藍牙連線(斷線會自動重連),網頁透過 WebSocket 把搖桿的值傳過來,伺服器每 30 ms 重送一次搖桿封包。

安全上最重要的是「沒人操作就一定要停」:放開搖桿、關掉網頁,或伺服器 0.5 秒沒收到任何輸入(例如手機網路斷了),都會立刻送停止(deadman 機制)。另外速度設上限,避免一下子甩太快。

加上即時影像:雲台上的手機當網路攝影機

雲台夾著的就是一支手機,鏡頭跟著雲台轉,正好拿來當影像來源。手機上裝 IP Webcam(Play 商店的免費 App),它會把相機變成網頁伺服器,提供 MJPEG 串流(/video)和單張照片(/shot.jpg)。樹莓派和手機在同一個家用網路,由樹莓派的服務把串流轉送給瀏覽器(/cam/video)。這樣瀏覽器只要開控制頁這一個網址,不必另外連手機。

  • 只要影像,不要聲音:IP Webcam 預設會一起串流聲音,每次按 Start server 都會要麥克風權限。我把設定裡的「Audio mode」改成停用,麥克風權限也不給,這樣不會有任何聲音傳出去。
  • 降低解析度:預設 1920×1080 一張約 100 KB,連續串流太吃頻寬。用它的設定 API(/settings/video_size?set=640x480、/settings/quality?set=50)降到 640×480、畫質 50 後,一張約 21~30 KB,每秒約 28 張、約 600 KB/s。
  • 頁面上可以開關和旋轉:不看的時候關掉影像,節省流量和手機電量;手機在雲台上是橫裝的,畫面方向不對時可以旋轉,網頁會記住設定。
  • 免費版的限制:每次 Start server 之前要先看一段廣告;手機重開機後也要重新啟動。串流很耗電,長時間使用最好插著電。

讀懂雲台回報的姿態

要做極限保護,得先知道雲台現在轉到哪。配對(bond)並開啟 notify 之後,雲台會推送 cmd 0x1E 的姿態幀,但有個前提:要先送一次 GET GIMBAL_STATE(cmdId 48)它才開始送。資料裡是四個小端 int16,實際轉動各軸、比對數值變化後,對應如下:

欄位意義備註
軸 1俯仰單位 0.01°,重力基準的絕對角,上仰為正
軸 2橫滾正常時接近 0;馬達沒力、手機垂下時會跑到 −77° 左右
軸 3平移單位 0.01°,開機時鏡頭方向為 0,超過 ±180° 會繞回另一邊,右轉為負
軸 4 高位元組狀態碼02 馬達正常出力;保護關機後變 05

兩個要注意的地方:姿態推送固定只有約 4 Hz,主動多查幾次也不會變快;平移的 0° 每次開機都不一樣,而且會繞圈,程式裡要自己把繞圈接回來,變成連續的角度。

量測極限,再設計保護

先用慢速往每個方向轉到底,看角度停在哪:俯仰上限約 +32°、下限約 −25°;平移總行程約 280°。雲台協定裡其實有「角度到極限」的通知,但這支韌體轉到底時一筆都沒送,只能靠「數值停止變化」自己判斷。

第一版很單純:到了極限就不再送往那個方向的指令。結果俯仰還是一路衝到 34°。原因有兩個:

  • 雲台會滑行:它有運鏡平滑,送出停止後還會再轉一段。速度 250 時,停止後多轉了 17~22°。
  • 回報太慢:只有 4 Hz,0.25 秒內常常只有一筆資料,用它算速度常得到 0,等於沒有預判。

第二版改成一律提早煞車,結果又太保守,離極限還很遠就擋住了。最後的做法是依剩餘距離限速:速度同時用「回報推算」和「指令值換算(速度 250 約每秒 50°)」估,取比較大的那個;允許的最高速度=剩餘角度 ÷(0.5 秒 × 每單位約 0.2°/秒)。這樣越靠近越慢,貼近時才完全擋住。

俯仰是絕對角度,可以直接寫死軟體極限(+29°/−22°)。平移的零點每次開機都不同,所以改成「邊用邊學」:持續往同方向推,角度卻 0.6 秒幾乎沒動,就把這裡記成極限,之後在前 5° 擋下來。途中踩到一個坑:被擋住或限速時,雲台本來就會停,如果這時也做卡住偵測,會把擋下的位置誤當成極限,極限就越學越往內縮。所以只有在「真的有送動作指令」時才判斷卡住。

「噹噹叫」其實是過載保護

測試時雲台偶爾會「噹噹」叫,接著馬達沒力、手機垂下來。看藍牙紀錄,叫的時候雲台送了一個 cmd 0x62 的推送。一開始我在 RequestId 表裡查不到;後來才發現推送用的是另一張 AkProtocol.CmdId 表,兩張表的編號不一樣:0x62 是 GIMBAL_PROTECTION_SHUTDOWN_PUSH(保護關機)。原因碼的說明在 App 的 gimbal-core-common.json 裡:

原因碼說明
0低電量保護
1溫度過高保護
2手把空轉保護
3過載保護(實測叫聲就是這個)
4震動保護
5~7雲台內部錯誤
8~10俯仰/橫滾/航向未解鎖

過載不一定是頂到極限才發生:有一次只是用速度 200 上仰 0.8 秒、離極限還很遠,就觸發了。之後把測試速度降到 130~150,就沒有再發生過。不過樣本還不多,只能說「速度高比較容易觸發」。保護關機後,狀態碼會從 02 變成 05,要長按電源鍵重開雲台。伺服器現在看到 0x62 或狀態碼不是 02,就會清掉動作指令、暫停學習極限,網頁也會用紅字顯示原因。(另外電量太低時邊充邊用也撐不住,馬達一樣會沒力。)

用手機相機拍照驗證

手機就裝在雲台上,相機跟著轉,所以「拍照前後畫面有沒有移動」就是最直接的證據。Termux 的 termux-camera-photo 在這支手機上一直拍出 0 位元組,原因是 Android 擋了背景使用相機。改成用 adb 開系統相機、截預覽畫面才成功。截圖前還要先點一下螢幕,因為相機閒置一陣子會暫停預覽、變成黑畫面。腳本每段動作前後各截一張,搭配回報的角度,四個方向的極限都測過:

測試結果
右/左/上/下各轉 0.8 秒(速度 130)平移 −24.2°、+25.1°;俯仰 +14.5°、−26.3°,畫面都跟著移動
俯仰一直往上推 5 秒停在 26.6°(軟體極限 +29°)
俯仰一直往下推 5 秒停在 −19.3°(軟體極限 −22°)
平移往右推到底 → 再推 2 秒學到右極限;再推只動 0.1° 就被擋
平移往左推到底 → 再推 2 秒學到左極限;再推被擋
平移總行程約 284°
全程馬達正常,沒有觸發保護關機

測試途中藍牙短暫斷線一次,結果剛學到的右極限也被清掉了。原本的設計是「重連就清除」,因為雲台重開機後平移零點會變;但短暫斷線時零點其實沒變。現在改成:斷線超過 20 秒才視為雲台重開機、清除極限;短暫斷線就保留極限,角度也接續計算。

實際拍攝影片

下面是實際操作的錄影:右手在手機網頁上推搖桿,雲台上的手機鏡頭即時把畫面傳回控制頁。

小結

協定通了之後,難的反而是「安全地控制」:雲台會滑行、回報只有 4 Hz、平移零點每次開機都不同、轉到底不會通知,這些都要靠程式自己補。整理成幾句:姿態要先 GET 才會推送;俯仰用固定極限,平移邊用邊學;依剩餘距離限速,而不是到了才煞;0x62 是保護關機,原因碼 3 是過載;速度別開太高。

※ 本文為個人對自有裝置的互通性研究與學習紀錄。Feiyu、Vimble 等為飛宇科技的商標,與原廠無關。

沒有留言:

張貼留言