我透過 HSV 顏色篩選,從相機畫面中挑出紅色區域,再透過遮罩(Mask)尋找輪廓。現在程式已經可以找到紅色候選區域了,下一步就是把這個區域的位置算出來。
如果之後想讓 TurtleBot3 自動追蹤目標,只有知道「畫面裡有紅色」還不夠。今天先不急著讓機器人移動,而是先回答一個問題:
目標現在是在畫面的左邊、右邊,還是中央?
今天先不急著讓機器人移動,而是從昨天找到的紅色區域繼續往下,取得目標的位置,並算出目標和畫面中心之間的水平偏差。
昨天透過 Mask 找出紅色區域後,可以使用 findContours() 找到畫面中的輪廓,再利用 Bounding Box 取得候選目標的位置與大小。
如果畫面中有多個紅色區域,目前先簡單選擇面積最大的輪廓作為追蹤目標:
if contours:
largest = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(largest)
這裡的 if contours: 是先確認有沒有找到輪廓。有找到,才接著挑出面積最大的那一個,並使用 cv2.boundingRect() 取得包住它的矩形外框,也就是 Bounding Box。接下來的目標中心與誤差,也都只在找到輪廓時才計算。
這四個數值分別代表:
| 數值 | 意義 |
|---|---|
| x | 外框左上角的 X 座標 |
| y | 外框左上角的 Y 座標 |
| w | 外框寬度 |
| h | 外框高度 |
也就是先用一個矩形,把找到的紅色區域框起來。這裡取得的是外框的位置與大小,如果想在畫面上看到框線,還需要另外把它畫出來。
不過,選擇最大輪廓只是目前練習場景中的簡化方式。如果畫面中出現更大的紅色物體,程式也可能選到它。之後還需要搭配面積門檻或其他條件,判斷哪一個區域才是要追蹤的目標。
有了 x、y、w、h,就可以從外框左上角的位置,分別加上一半的寬度和高度,算出外框的中心:
target_x = x + w // 2
target_y = y + h // 2
這裡的 // 是整數除法,方便把結果表示成整數像素座標。目前先用 Bounding Box 的中心,代表目標在畫面中的位置。
我的追蹤目前先處理左右方向,所以會先關注 target_x。它代表目標中心在影像 X 軸上的位置。影像座標通常以左上角為起點,X 值越大,位置就越靠右;Y 值越大,位置就越靠下。例如影像寬度是 640 像素,而 target_x = 500,就表示目標中心比較靠近畫面的右側。不過,要知道它到底偏了多少,還需要先找到畫面中心,作為比較的基準。
在 Day 5 中,我已經把 ROS 2 的影像訊息轉成 OpenCV 可以處理的畫面。這裡以 image 代表轉換後的影像,可以透過 shape 取得它的高度和寬度,再計算水平方向的中心:
height, width = image.shape[:2]
center_x = width // 2
這裡要注意,image.shape[:2] 取得的順序是高度、寬度,所以計算左右方向的中心時,要使用 width。
例如影像寬度是 640 像素,程式使用的畫面中心就是 center_x = 320。如果目標的 target_x 也是 320,就表示目標中心剛好對準這個位置;小於 320 時在左側,大於 320 時則在右側。640 只是這裡方便說明的示例寬度。實際計算時,我會直接使用影像的 width,這樣即使相機解析度改變,也不用手動修改畫面中心的數值。
知道目標中心與畫面中心之後,就可以把兩者相減,算出水平方向的誤差:
error_x = target_x - center_x
這裡的 error_x,代表目標中心相對於畫面中心,向左或向右偏了多少像素。它的正負號可以用來判斷方向,數值的絕對值則表示偏離的程度。
以 center_x = 320 為例:
| 目標中心 target_x | 計算方式 | 誤差 error_x | 代表的位置 |
|---|---|---|---|
| 150 | 150 − 320 | -170 | 在畫面中心左側,偏了 170 像素 |
| 320 | 320 − 320 | 0 | 對準程式設定的畫面中心 |
| 500 | 500 − 320 | 180 | 在畫面中心右側,偏了 180 像素 |
負數在左邊,正數在右邊,接近 0 就表示接近中央。這個方向判斷和減法順序有關,目前使用的是「目標中心減掉畫面中心」,如果順序反過來,正負號的意義也會跟著改變。這裡計算的是影像上的左右偏移,單位是像素,還不是目標與機器人之間的實際距離。
因為之後要讓 TurtleBot3 對準目標,就需要把畫面中的位置,轉成可以用來控制機器人的資訊。目標在左側時,機器人需要往左轉;在右側時,就需要往右轉,讓目標逐漸回到畫面中央。除了方向,偏離的程度也很重要。如果目標只偏了一點點,和目標已經跑到畫面邊緣,機器人需要的轉向速度應該不太一樣。error_x 就能提供這個判斷依據,讓後續的控制程式決定要往哪裡轉,以及要轉多快。
今天先做到位置計算。實際如何把誤差轉成旋轉速度,以及多接近中央時可以停止轉向,會在接下來的控制部分繼續處理。
這一步完成後,程式拿到的就不只是畫面中有沒有紅色,還能進一步知道目標偏離畫面中心多少。接下來,才有辦法把這個數值用在 TurtleBot3 的轉向控制上。
下一篇會接著把 error_x 用來控制 TurtleBot3:目標在左邊就往左轉,在右邊就往右轉。如果目標偏得越多,是不是也應該轉得越快?