制造一個(gè)“繪圖驅(qū)動(dòng)”的機(jī)器人
手勢(shì)捕獲和“繪制模式”
我們的項(xiàng)目從一個(gè)基于網(wǎng)絡(luò)攝像頭的手跟蹤界面開(kāi)始,它把一個(gè)簡(jiǎn)單的手勢(shì)變成了一個(gè)繪圖工具。使用MediaPipe,我們跟蹤一只手,并根據(jù)相對(duì)于手掌大小的指尖距離計(jì)算標(biāo)準(zhǔn)化的“開(kāi)放性”分?jǐn)?shù)。當(dāng)開(kāi)放度低于拳頭閾值時(shí),我們進(jìn)入拳頭狀態(tài),并開(kāi)始以像素坐標(biāo)記錄拳頭中心的二維路徑。當(dāng)拳頭保持關(guān)閉時(shí),我們只在拳頭移動(dòng)了至少一個(gè)小的最小步時(shí)附加點(diǎn),以避免噪音和重復(fù)。我們還通過(guò)在畫布覆蓋上繪制線段來(lái)渲染跟蹤,因此用戶可以確切地看到正在記錄的路徑。當(dāng)用戶再次張開(kāi)手(張開(kāi)程度超過(guò)張開(kāi)閾值),退出繪制模式,完成繪制輪廓。
關(guān)鍵點(diǎn)提取和路徑排序
輪廓確定后,我們通過(guò)檢測(cè)路徑上的關(guān)鍵特征,將繪制的筆畫轉(zhuǎn)換為一組穩(wěn)定的路徑點(diǎn)。我們將折線光柵化為二進(jìn)制遮罩,并運(yùn)行g(shù)oodFeaturesToTrack (GFTT)來(lái)找到代表整體形狀的角和突出點(diǎn),而無(wú)需傳輸每個(gè)原始痕跡樣本。由于GFTT可以返回聚類檢測(cè),我們應(yīng)用簡(jiǎn)單的基于半徑的非最大值抑制,因此最終的航點(diǎn)集在整個(gè)行程中分散。接下來(lái),我們通過(guò)將每個(gè)檢測(cè)到的點(diǎn)投影到原始折線上,并根據(jù)它們沿路徑的距離參數(shù)對(duì)它們進(jìn)行排序,從而按照正確的行程順序?qū)@些關(guān)鍵點(diǎn)進(jìn)行排序。這一步很重要,因?yàn)樘綔y(cè)器本身并不知道哪個(gè)點(diǎn)是“第一個(gè)”還是“下一個(gè)”,發(fā)送無(wú)序的點(diǎn)會(huì)產(chǎn)生不穩(wěn)定的運(yùn)動(dòng)。結(jié)果是一個(gè)路徑點(diǎn)的有序列表,它們遵循路徑繪制的相同方向。
標(biāo)準(zhǔn)化,縮放和網(wǎng)絡(luò)傳輸
在傳輸之前,我們將有序的像素路徑點(diǎn)轉(zhuǎn)換為機(jī)器人友好的坐標(biāo)(以米為單位),并確保它們適合有界的工作空間。我們平移整個(gè)路徑,使路徑點(diǎn)0成為原點(diǎn)(0,0),我們可以選擇翻轉(zhuǎn)y軸,因?yàn)橄鄼C(jī)坐標(biāo)向下增加,而機(jī)器人/世界坐標(biāo)通常向上增加。然后,我們統(tǒng)一縮放平移路徑,使最大絕對(duì)坐標(biāo)位于±1.25米內(nèi),保證機(jī)器人不會(huì)被命令超出預(yù)定范圍。最后的輸出被打包成一個(gè)標(biāo)準(zhǔn)的64-float有效負(fù)載,代表最多32個(gè)(x,y)點(diǎn);如果生成的點(diǎn)較少,我們用零填充其余的點(diǎn)(用作嵌入側(cè)的“停止”哨兵)。我們通過(guò)TCP將有效負(fù)載傳輸為8個(gè)數(shù)據(jù)包,每個(gè)數(shù)據(jù)包有8個(gè)浮點(diǎn)數(shù),由開(kāi)始和結(jié)束字節(jié)組成,并且我們故意在數(shù)據(jù)包之間添加短延遲以保持嵌入式接收器的穩(wěn)定。這給了機(jī)器人一個(gè)完整的、一致的路點(diǎn)列表,它獨(dú)立于相機(jī)分辨率或繪圖比例。
嵌入式接收、路點(diǎn)存儲(chǔ)和后續(xù)控制器
在嵌入式系統(tǒng)上,每個(gè)傳入數(shù)據(jù)包更新八個(gè)接收浮點(diǎn)數(shù),我們將這些浮點(diǎn)數(shù)視為每個(gè)數(shù)據(jù)包的四個(gè)(x,y)路點(diǎn)對(duì)。當(dāng)數(shù)據(jù)包到達(dá)時(shí),我們填充一個(gè)全局robotdest[]數(shù)組并增加計(jì)數(shù)器,直到我們接收到所有64個(gè)路點(diǎn),此時(shí)我們通過(guò)設(shè)置wp_rx_done = 1和重置statpos = 0從“等待”模式切換到“跟隨”模式。在等待完整的集合時(shí),機(jī)器人只是保持位置在(0,0),這樣它就不會(huì)過(guò)早地移動(dòng)部分?jǐn)?shù)據(jù)。一旦跟隨開(kāi)始,我們的控制循環(huán)就會(huì)反復(fù)調(diào)用xy_control(…)函數(shù),該函數(shù)接受機(jī)器人的估計(jì)(x,y,偏航)狀態(tài),并輸出指向當(dāng)前航路點(diǎn)的前進(jìn)速度參考和轉(zhuǎn)彎率命令。當(dāng)控制器報(bào)告到達(dá)當(dāng)前路徑點(diǎn)時(shí),我們前進(jìn)到下一個(gè)索引,并在到達(dá)恰好為(0,0)的路徑點(diǎn)時(shí)停止,由于Python填充方案,它充當(dāng)了一個(gè)明確的路徑結(jié)束標(biāo)記。停止后,我們繼續(xù)指揮最后的航路點(diǎn)作為等待位置,這樣機(jī)器人就可以穩(wěn)定下來(lái),而不是漂移。這種端到端管道讓繪制的手勢(shì)路徑成為機(jī)器人可以自主遵循的安全、有界、有序的路點(diǎn)計(jì)劃。
本文編譯自hackster.io





