前一天,我把 Dead Zone、P 控制和最大角速度限制放在一起,讓 TurtleBot3 在目標接近畫面中央時減少左右微調。到目前為止,機器人主要還是在原地轉向:它知道紅色方塊在哪個方向,卻沒有往目標靠近。
如果要讓它真正開始追蹤,下一個問題就是:
什麼時候該前進,又該在什麼時候停下來?
前面使用 /cmd_vel 控制轉向時,Twist 訊息中的前進速度一直設為 0:
twist.linear.x = 0.0
twist.angular.z = angular_z
angular.z 負責讓 TurtleBot3 朝目標旋轉;linear.x 則控制它往前或往後移動。只要給 linear.x 一個正值,機器人就能往前走。
可是不能只因為「有看到紅色」就一直前進。如果目標已經很近,機器人還繼續往前,可能會靠得太近;如果目標偏在畫面邊緣,直接往前走也可能讓目標離開視野。所以前進前,還要看目標在畫面中的位置和大小。
前面用 error_x 判斷目標偏左還是偏右。要決定是否繼續靠近,還可以觀察紅色方塊在影像中佔了多大面積。
例如使用目標的 Bounding Box(外接框)時,可以用寬乘上高,得到畫面中的面積:
box_area = box_width * box_height
對同一個紅色方塊來說,靠近相機時通常會在畫面中變大,離遠時則會變小。因此,可以設定一個 AREA_THRESHOLD,作為「靠近到這裡就先停下」的判斷依據。
這裡的 box_area 是影像中的大小,不能直接當成實際距離。相機解析度、方塊的朝向,以及辨識出的外接框,都會影響數值。門檻要根據 Gazebo 畫面觀察與調整,不能把某個面積值直接理解成「距離方塊幾公尺」。
先假設程式已經找到紅色方塊。前進的條件可以簡化成兩個:目標接近畫面中央,而且它在畫面中還沒有大到需要停下。
if abs(error_x) <= DEAD_ZONE and box_area < AREA_THRESHOLD:
linear_x = FORWARD_SPEED
else:
linear_x = 0.0
也就是說,方向大致對準、目標還不夠近,才往前走。目標偏離中央時,先讓機器人轉向;目標的面積達到門檻時,就停止前進,避免一直靠近。停止前進不代表停止轉向,angular.z 仍然可以根據目標位置調整方向。
這裡先用固定的 FORWARD_SPEED 理解整個流程。實際速度要從較小的值開始觀察,因為走得太快,可能還沒來得及根據下一張影像修正,就已經衝到目標附近了。
讓 TurtleBot3 朝紅色方塊移動,是追蹤的起點;如果以後想讓它做移動跟拍,還得多考慮相機拍到的畫面。
| 想做到的事 | 目前需要觀察的重點 |
|---|---|
| 追蹤目標 | 目標是否維持在視野內,機器人能否朝它移動並適時停下 |
| 移動跟拍 | 除了跟得上目標,畫面是否平順、目標大小是否合適、移動是否突然 |
例如機器人一直急停、再突然前進,或是邊走邊快速轉向,即使沒有跟丟方塊,拍出來的畫面也可能很晃。這也是我想先從簡單的前進門檻開始,再慢慢調整速度的原因。
linear.x 與 angular.z現在可以把兩個速度分工來看:angular.z 處理「目標在哪個方向」,linear.x 處理「要不要往前靠近」。它們最後都會放進同一個 Twist 訊息,發布到 /cmd_vel:
twist.linear.x = linear_x
twist.angular.z = angular_z
這兩個數值需要一起判斷,不能只顧著往前走。這篇先整理前進的條件與兩種速度各自負責的工作;下一步再把它們放進同一段追蹤流程,觀察 TurtleBot3 在移動時能不能持續對準目標。
linear.x 與 angular.z 各自控制的動作。error_x 與 box_area,決定是否前進。現在 TurtleBot3 不只需要知道「方塊在哪邊」,還要開始判斷「我是不是已經靠得夠近了」。不然好不容易追上目標,下一秒就變成紅色方塊的特寫。
下一篇會把轉向與前進控制整合,實際觀察 TurtleBot3 移動時能不能持續讓目標留在畫面中。同時加入目標消失時的停止機制,避免機器人失去辨識結果後還繼續往前走。