2026年11月7日 星期六

Pi 5 Mecanum Robot Car: Web Control, Gyro Straight-Line Hold, Distance & Route Maps, Obstacle Avoidance | Pi 5 麥克納姆輪自走車:網頁遙控、陀螺儀走直線、距離圖與路線圖、自動避障實車調校

這台麥克納姆輪車之前是 Arduino UNO/Mega2560+ESP32-CAM 的取物機器人。這次把 Arduino 拿掉,改成 Raspberry Pi 5 直接用 GPIO 驅動兩片 L298N,加上 Logitech C310 攝影機、HC-SR04 超聲波(裝在 MG90S 伺服上可以轉頭)、MPU6050 陀螺儀,做成一台用手機網頁遙控、會自己避障的自走車。這篇記錄硬體接法、網頁介面,以及實車調校時一個一個踩到的坑。

硬體與接線

零件用途接到 Pi 5
L298N ×2四顆 TT 馬達(每片管一邊兩顆)EN 用 GPIO12/13/18/19(PWM 調速,板上 ENA/ENB 跳帽要拔),IN 用 8 支 GPIO
HC-SR04前方距離Trig GPIO17;Echo 是 5V,經 1kΩ/2kΩ 分壓才接 GPIO27
MG90S超聲波轉頭(±80°)GPIO22,電源吃 L298N 的 5V(不吃 Pi 的 5V)
MPU6050(GY-521)陀螺儀:車頭方向、撞擊偵測I2C:SDA 3 腳、SCL 5 腳、VCC 3.3V
Logitech C310影像+YOLO 物件偵測USB(640×480 YUYV,MJPG 會一直噴壞 JPEG)
18650 ×3 串聯馬達電源(12.6V 滿)→ 保護板 → 開關 → 兩片 L298N,GND 跟 Pi 共地
行動電源Pi 5 電源USB-C,跟馬達電源分開




三顆 18650 滿電 12.6V,扣掉 L298N 約 2V 壓降,馬達還是吃到 9~10V,而 TT 馬達額定 3~6V,所以程式把 PWM 上限壓在 60%。Pi 5 用行動電源供電,網頁上會顯示 Pi 的 5V 電壓;手邊這顆只有 4.75~4.89V,常跳「曾電壓不足/曾降頻」,之後要換 PD 的行動電源。

網頁操作

程式是 Python(aiohttp+lgpio+OpenCV+onnxruntime),開機自動跑成 systemd 服務,手機連 http://pi5-2:8080/:左邊搖桿前後+左右平移,右邊橢圓搖桿原地旋轉,影像上疊距離,YOLO(yolo26n,CPU 每張約 32 ms、限每秒 10 次)把看到的東西框出來。網頁每 0.1 秒送一次搖桿,超過 0.5 秒沒收到就停車(斷線、手機鎖螢幕)。


實車調校一:輪子方向與超聲波

  • 輪子方向:網頁有「單輪測試」,每顆按「正轉」都應該讓車往前推。實車 RL(左後)和 FR(右前)是反的,不用改線,在 config.yaml 把那兩顆的 invert 改成 true。
  • 超聲波第一次完全沒回音:Pi 送了 10 次 Trig,GPIO27 一次都沒變高——是接線問題(實體腳號和 GPIO 編號很容易搞混,第 13 腳是 GPIO27,GPIO13 是第 33 腳),重接後馬上量到 40 cm。
  • 轉頭後距離不會變:原本程式只在伺服朝正前方時才量,轉到 -10° 就凍結在最後一個值。改成任何角度都量,網頁標出方向(例如「63 cm 右 10°」);「太近不准往前」只看 ±15° 內的讀值。

實車調校二:陀螺儀讓車走直線

TT 馬達左右快慢不一樣,速度 50% 直走 3 秒,車頭就往左偏了 10°。加上 MPU6050 之後,搖桿沒有旋轉時程式會鎖住起步那一刻的車頭方向,偏了就讓兩邊輪子一快一慢拉回來(PD 控制),另外再學一個「這台車直走本來就要往右補多少」的固定量(積分,前/後/左/右分開學)。

版本3 秒直走的車頭偏移
沒修正10°
kp 0.037°
kp 0.1、ki 0.05、上限 0.5最多 2°,之後自己拉回,停下時 1.3°;實際走 115 cm,眼睛看是直的

兩個坑:

  • 兩個網頁互相蓋掉:我從遠端送「前進」做測試時,使用者手機的網頁還開著、每 0.1 秒送一次「搖桿放著=0」,車子其實是走走停停,第一次量到的「3 秒 80 cm」是錯的(真的是 115 cm)。改成每個網頁的搖桿分開記,有人在推就用最新推的那個,全部放開才停。
  • 積分飽和:自動模式卡在櫃子前推不動時,修正量一路累積到上限 -0.5,右輪只剩 14% 出力,車子一直往右繞。改成偏差小於 10° 才學、修正量上限 0.2、偏超過 20°(被撞歪、手轉、卡住)就改用現在的方向當目標。

距離圖與路線圖

影像下面多了兩張圖:

  • 距離圖(車身座標,車頭朝上):超聲波每 5° 一格、15 秒內的讀值畫成綠點連成輪廓;按「掃描一圈」伺服從右 80° 轉到左 80°,每 10° 量一次。黃色扇形是攝影機視角,YOLO 認得的東西畫成黃點並估距離。
  • 路線圖(俯視,出發時車頭朝上):沒有編碼器,位置是「送給四輪的速度 → 麥克納姆反運算 → 乘實測 77 cm/s(全速)」推算,方向用陀螺儀;超聲波打到的點換成世界座標畫成紅點,跑久了房間的輪廓就出來了。

攝影機估距離用「地面法」:鏡頭離地 8 cm,東西放在地上時,它的底邊在畫面越下面就越近,距離=鏡頭高度 ÷ tan(底邊在水平線下的角度);底邊碰到畫面下緣(看不到落地點)時,改用人、椅子這類已知高度的東西用框的高度估。一開始櫃子被估成 28 cm,因為鏡頭被夾子夾得往上仰——用一個放在 100 cm 的箱子校正:箱子底邊在畫面第 350 列、遠處地面的水平線在第 290 列左右,算出鏡頭往上仰約 6°,校正後估成 87 cm(從車頭算),跟超聲波的 86 cm 對得上。


自動避障:一路改了好幾版

最初的邏輯很簡單:前方 25 cm 內有東西就停、伺服左右各看 60°、往比較空的一邊轉。實車跑起來的問題和改法:

實車問題原因改法
斜面的路障偵測不到聲波被斜面彈走,收不到回音,原本當成「前方沒東西」前進時伺服在 0/左 20°/0/右 20° 輪流量,取擋在車寬內最近的;±25° 都沒回音超過 1 秒就停下來看;沒回音或接近時半速
撞到東西還一直頂沒有感測MPU6050 的加速度:水平加速度突然超過 0.9 g 當撞擊,退一點再找路
一直誤判撞到(前方還有 124 cm)起步、急停的加速度也超過門檻穩定前進 0.8 秒之後才算撞擊
跑不出房間只看左右兩個角度、固定轉一個角度;轉完還用轉之前的前方距離看 5 個角度(右 80、右 40、正前、左 40、左 80),往最空的方向轉、每次最多 30°;轉完清掉舊讀值
左右來回換、轉不出去每次重新選邊記住閃避方向:沒走出 50 cm 又被擋就繼續往同一邊;那邊累積轉 120° 還不行就記住、改另一邊;兩邊都不行就後退重來
頂著抽屜櫃推 45 秒超聲波裝得低,從櫃子下面看出去一直量到 90 cm卡住偵測:輪子推算走了 25 cm 以上,正前方距離卻變不到 1/4 → 當成卡住,後退換方向
太快自動模式不吃網頁的「速度上限」自動前進、轉彎、後退都乘上速度上限;轉彎改用陀螺儀轉到角度就停

最嚴重的坑:重開服務時暴衝

有一次車子突然全速衝進躺椅腳靠底下。查了才發現兩件事疊在一起:

  1. lgpio 的 tx_pwm(…, 0) 不會把腳位拉低:它只是停掉 PWM,腳位停在最後的電位。用 pinctrl get 一看,四支 L298N 的 EN 一直都是 high。
  2. 我用 SIGKILL 重開服務(程式收到 SIGTERM 不會自己結束,之前都用強制結束),當時車子正在自動模式轉動:方向腳停在「轉」、EN 又是 high,新程式起來前那 2~3 秒就是 100% 全速。

修法:停車時一定 gpio_write(EN, 0);systemd 服務加 ExecStartPre/ExecStopPost 用 pinctrl set … op dl 把 12 支馬達腳全部拉低、TimeoutStopSec=3;收到關閉訊號第一件事先停車。之後就算程式當掉或被砍,馬達也不會停在轉動狀態。接馬達的專案,「程式死掉時腳位會停在哪」一定要先想好。

還沒解決的

  • 超聲波看不到比它高或低的東西(躺椅腳靠、櫃子下緣、門檻),打算車頂加一顆 VL53L1X 雷射測距,跟 MPU6050 共用 I2C。
  • Pi 5 的行動電源電壓偏低(4.75~4.89V),要換 PD 5V 3A。
  • 路線圖的平移效率(假設直走的 80%)還沒實測;跑久了會飄。

沒有留言:

張貼留言