設(shè)計與實現(xiàn))
簡介計算機視覺與機器人控制的交叉應(yīng)用正在重塑智能分揀領(lǐng)域。深度學習目標檢測模型能夠?qū)ξ矬w進行實時定位與分類而運動規(guī)劃框架則為機械臂提供安全可靠的軌跡生成能力。YOLOv5作為輕量級檢測模型在推理速度與識別精度之間取得了良好平衡MoveIt作為ROS生態(tài)下的機器人運動規(guī)劃框架可高效完成運動學求解、碰撞檢測與路徑規(guī)劃。將二者結(jié)合可構(gòu)建“感知-決策-執(zhí)行”的完整閉環(huán)在垃圾自動分類、工業(yè)抓取、服務(wù)機器人等場景中具有廣泛的應(yīng)用價值。以一套基于YOLOv5和MoveIt的桌面垃圾分類機械臂項目為例系統(tǒng)介紹了從數(shù)據(jù)集構(gòu)建、模型訓練、手眼標定、URDF建模到狀態(tài)機集成的完整技術(shù)鏈路并針對實機調(diào)試中的識別混淆、坐標轉(zhuǎn)換誤差、規(guī)劃失敗等典型問題給出了具體解決方案為從事視覺機械臂開發(fā)的工程師和學生提供了一份可落地的工程實踐參考。 做這個項目的起因特別樸素實驗室樓下的垃圾分類督導員每天要拆開幾百個垃圾袋把塑料瓶、易拉罐、紙張、剩飯分門別類工作量巨大。當時正好在啃YOLOv5和MoveIt就想著能不能做一個長了眼睛的機械臂自動識別垃圾類別然后抓起來扔進對應(yīng)垃圾桶。斷斷續(xù)續(xù)折騰了三個多月整套系統(tǒng)跑通之后我把源碼、設(shè)計文檔、訓練好的權(quán)重和調(diào)試記錄整理成了一個壓縮包也就是你在標題里看到的那個分類機器人源碼設(shè)計文檔.zip。這篇博文就把整個項目從選型到落地、從原理到踩坑的過程梳理一遍給打算做類似視覺機械臂項目的朋友一個比較完整的參考。這個項目適合兩類人看一類是正在做機器人類畢業(yè)設(shè)計或競賽項目的學生YOLOv5MoveIt的組合覆蓋面廣、工程量適中、演示效果好另一類是剛接觸ROS和機械臂控制、想找一個完整案例把視覺和運動控制串起來的開發(fā)者。我會先講為什么選這套方案再拆解視覺模塊和機械臂模塊各自怎么實現(xiàn)然后是聯(lián)動和調(diào)試最后說說源碼和文檔怎么用以及后續(xù)可以怎么擴展。1. 為什么選YOLOv5MoveIt選型背后的真實考量很多人一上來就問為什么用YOLOv5不用YOLOv8為什么用MoveIt不用自己寫逆解這類問題其實沒有標準答案關(guān)鍵看項目約束條件。我這個項目的約束非常明確ROS Melodic環(huán)境下開發(fā)機械臂是自組裝的6自由度桌面臂算力平臺是帶GTX 1660Ti的筆記本目標物體是塑料瓶、易拉罐、廢紙團、果皮等常見垃圾識別類別一共四類。在這個前提下YOLOv5和MoveIt幾乎是當時最穩(wěn)的組合。1.1 視覺方案對比YOLOv5比傳統(tǒng)圖像處理和更重模型更合適傳統(tǒng)圖像處理方案我也試過比如用顏色閾值提取塑料瓶的藍色、用形狀匹配識別易拉罐的圓柱輪廓。效果在單一光照、單一背景的實驗室里還可以但只要把垃圾桶放到窗邊陽光一照顏色全飄誤檢率直接崩到?jīng)]法看。深度學習檢測模型對光照、角度、遮擋的魯棒性明顯更好而YOLOv5在速度和精度的平衡上剛剛好。為什么不選更重的模型當時也考慮過Faster R-CNN和YOLOv5x。Faster R-CNN在COCO上的mAP確實高一些但在筆記本CPU上跑一次推理要兩三百毫秒加上機械臂運動時間整個流程會拖得很長YOLOv5x同理權(quán)重文件150多MB前向推理速度快不到哪去。垃圾識別場景的物體類別不算精細不需要檢測出農(nóng)夫山泉還是怡寶只要能區(qū)分塑料瓶易拉罐這種大類YOLOv5s或者YOLOv5m的精度完全夠用而且推理時間能壓到20ms以內(nèi)給后面的機械臂規(guī)劃留出了充足的系統(tǒng)余量。還有一個很實際的原因YOLOv5的生態(tài)太成熟了。從數(shù)據(jù)標注到訓練、導出onnx、推理腳本網(wǎng)上有海量現(xiàn)成資料遇到問題搜一下就有解決方案。對于一個需要控制整體進度的項目來說選一個社區(qū)活躍度高的模型框架比單純追求指標上的一點提升重要得多。1.2 機械臂控制框架MoveIt在ROS生態(tài)中的地位與優(yōu)勢機械臂控制的方案選擇上我當時糾結(jié)過三條路一是用廠商自帶的SDK直接控制二是用ROS Industrial的插件三是用MoveIt。自組臂用的是舵機步進電機沒有廠商SDK所以第一條路直接pass。ROS Industrial的驅(qū)動鏈雖然成熟但主要面向工業(yè)機器人配置起來相當繁瑣而且對自組臂不友好。剩下MoveIt它其實不是一個控制庫而是一個運動規(guī)劃框架把運動學求解、碰撞檢測、路徑規(guī)劃、軌跡執(zhí)行這些模塊整合到了一起還提供了圖形化配置工具。也就是說我只需要提供機械臂的URDF模型MoveIt就能自動生成運動學求解器、規(guī)劃場景、執(zhí)行器接口然后用幾行代碼就能讓機械臂從A點運動到B點。MoveIt還內(nèi)置了多種規(guī)劃器比如OMPL里的RRT、RRTConnect、BKPIECE等。我實際用得最多的是RRTConnect原因后面細講。對ZERO基礎(chǔ)的人來說可能一下子理解不了MoveIt是框架這句話可以類比成Photoshop你不用自己寫圖像處理算法只需要通過它提供的界面和接口把素材拖進去選擇合適的工具就能處理圖像。MoveIt就是機械臂領(lǐng)域的Photoshop你負責提供模型和期望的目標位姿它負責算出一條無碰撞的軌跡。1.3 整體系統(tǒng)框架與數(shù)據(jù)流整個系統(tǒng)的物理組成其實很簡單一臺帶GPU的電腦一個RGB相機一臺6自由度桌面機械臂幾個對應(yīng)類別的垃圾桶。軟件層面分成三個模塊視覺識別節(jié)點YOLOv5推理、決策調(diào)度節(jié)點狀態(tài)機、機械臂控制節(jié)點MoveIt控制器。數(shù)據(jù)流是這樣的相機實時采集桌面畫面YOLOv5推理出畫面中垃圾的類別和像素坐標決策節(jié)點拿到檢測結(jié)果后根據(jù)預設(shè)的類別-垃圾桶映射表計算出機械臂的目標抓取位姿和投放位姿MoveIt規(guī)劃出從當前位姿到目標位姿的無碰撞軌跡發(fā)布給底層控制器執(zhí)行機械臂抓取垃圾后移動到對應(yīng)垃圾桶上方松開夾爪完成一次分類投放。這套流程聽起來直接但實際調(diào)試時每一步都有坑尤其是坐標轉(zhuǎn)換和狀態(tài)同步這兩個環(huán)節(jié)后面我會單獨拿出兩個章節(jié)重點講。2. 視覺識別模塊從數(shù)據(jù)集構(gòu)建到Y(jié)OLOv5模型部署的完整鏈路視覺模塊是整個系統(tǒng)里工程量最大、也最容易出問題的地方。網(wǎng)上很多人分享YOLO訓練教程大多是拿公開數(shù)據(jù)集跑一遍demo但到了自己采集數(shù)據(jù)、標注、訓練、部署就會發(fā)現(xiàn)各種意想不到的問題。我把這一整條鏈路拆成三個關(guān)鍵階段每個階段里面都標注了我認為最重要的細節(jié)。2.1 垃圾數(shù)據(jù)集的采集與標注細節(jié)垃圾數(shù)據(jù)集的第一個問題就是樣本不均衡。網(wǎng)上的公開垃圾數(shù)據(jù)集比如TrashNet塑料瓶和易拉罐的圖片非常多但果皮、廢紙團的樣本相對少。如果直接用這個數(shù)據(jù)集訓練模型對果皮的識別效果會很差。我的做法是自己采集了一部分真實場景數(shù)據(jù)把桌面背景、光照條件、垃圾形態(tài)都做了一次擴充。采集時我踩了一個很大的坑只采集了干凈、完整的物體忽略了真實垃圾往往有變形、壓扁、貼標簽、沾污漬等情況。比如一個壓扁的易拉罐角度稍微偏一點模型就認為是塑料瓶。后來我在采集中特意加入了殘缺、遮擋、堆疊、不同光照時段的樣本識別率才真正提上來。如果你也想做類似項目建議一開始就收集臟亂差樣本而不是商品展示圖樣本。標注環(huán)節(jié)用LabelImg格式選的YOLO的txt格式也就是每個目標一行包括類別id和歸一化的中心點坐標、寬高。這里有個細節(jié)標注框不要緊貼物體邊界。我一開始標注時為了框得準貼得很緊訓練出來的模型預測框在推理時經(jīng)常比實際物體小一圈導致機械臂抓取時定位偏上。后來我重新把標注框擴大到物體邊界外2%到5%左右問題才解決。原因是YOLO的預測框回歸在邊界處不穩(wěn)定稍微留一點余量可以讓框的中心點更準機械臂抓取中心位置也更可靠。2.2 模型訓練關(guān)鍵參數(shù)與調(diào)優(yōu)筆記我選的預訓練權(quán)重是YOLOv5s訓練了大概200輪batch size選16輸入分辨率640x640。官方默認的hyp.scratch.yaml里很多超參數(shù)直接可用但有兩個參數(shù)我單獨做了調(diào)整一是mosaic增強的概率保持默認0.7這個對垃圾這類小目標很有用二是fliplr水平翻轉(zhuǎn)我調(diào)到了0.2因為機械臂抓取時物體從相機視角看主要是正立和略帶旋轉(zhuǎn)的狀態(tài)太強的水平翻轉(zhuǎn)會引入不自然的視角。還有一個容易被忽略的參數(shù)image_weights。這個參數(shù)如果開啟采樣時會優(yōu)先選那些損失較大的圖片能緩解樣本不均衡。我在訓練后期開了它val集的mAP提升了大概2個百分點。不要小看這兩個點在垃圾識別這種類別間相似度較高的場景里mAP的微小提升可能就意味著某些易混淆類別能被分對。訓練過程中最需要盯的是PR曲線和混淆矩陣。我最初訓練的模型在易拉罐和塑料瓶之間頻繁混淆看混淆矩陣發(fā)現(xiàn)鋁罐在強光下的反光區(qū)域非常像塑料瓶的高光紋理。解決方法是采集數(shù)據(jù)時特意加入強反光樣本同時把亮度增強參數(shù)hsv_v調(diào)低從默認的0.4降到0.2避免模型把反光當成目標特征。調(diào)完訓練一輪后這兩類的混淆比例降了大概40%。2.3 實時推理的性能優(yōu)化TensorRT與模型輕量化訓練完的PyTorch模型直接跑推理在1660Ti上大概30ms一幀純看視覺是夠用的但一旦和MoveIt同時跑CPU、GPU爭搶資源整個系統(tǒng)的響應(yīng)延遲會明顯上升。為了給機械臂規(guī)劃留出余量我把模型導出成TensorRT的FP16 engine。導出流程網(wǎng)上資料很多但有幾個坑值得提醒一是YOLOv5官方倉庫的export.py在導出TensorRT時如果CUDA、cuDNN和TensorRT版本不匹配很容易報錯或者導出的engine體積異常。我最后用的組合是CUDA 10.2cuDNN 7.6.5TensorRT 7.1.3在2023年之后這個組合偏舊但穩(wěn)定性非常好。二是FP16的精度損失在垃圾識別里完全可接受我實測mAP只掉了0.8%推理時間從30ms降到了12ms。三是TensorRT的engine文件跟推理環(huán)境強綁定換一臺電腦必須重新導出沒法直接拷著用這個要提前在交付文檔里寫清楚。還有一個更輕量的優(yōu)化是降低輸入分辨率。640x640降到480x480推理時間從12ms降到7msmAP僅下降1.1%。在桌面固定抓取場景下物體距離相機很近YOLO在低分辨率下同樣能檢測到所以我覺得這個trade-off很劃算。最終部署時我選了480x480輸入這樣即使MoveIt規(guī)劃需要占用大量計算資源視覺端的幀率也能穩(wěn)定在25FPS以上。3. 機械臂控制模塊MoveIt運動規(guī)劃與抓取邏輯的實現(xiàn)機械臂控制是整個系統(tǒng)的執(zhí)行層任何識別上的誤差最終都會體現(xiàn)在抓不到或者碰倒物體上。MoveIt本身封裝了很多東西但用好它需要理解幾個核心概念URDF機器人模型、規(guī)劃組、運動學求解器、規(guī)劃場景和坐標系變換。3.1 機械臂URDF建模與MoveIt配置助手的使用自組機械臂沒有現(xiàn)成的URDF我是從零開始建的。建模時最重要的事情是把每個關(guān)節(jié)的運動范圍搞對。拿我用的MG996R舵機來說名義上是180度但實際PWM信號在500到2500微秒之間對應(yīng)的角度才穩(wěn)定超出這個范圍舵機要么堵轉(zhuǎn)要么抖動。我當時沒仔細測實際行程直接按180度建模結(jié)果MoveIt規(guī)劃的路徑經(jīng)常讓舵機到達物理極限執(zhí)行時咔咔響。后來用角度尺實測了每個關(guān)節(jié)的極限角度重新改URDF問題才解決。URDF建好后用moveit_setup_assistant生成MoveIt配置包。這個工具會引導你定義規(guī)劃組Planning Group、生成碰撞矩陣并檢查URDF的完整性。需要注意的是規(guī)劃組里必須把末端執(zhí)行器也就是夾爪單獨列成一個group后面做抓取時可以直接指定末端目標位姿否則MoveIt只控制機械臂本體夾爪的開合狀態(tài)沒法納入規(guī)劃。還有一個細節(jié)moveit_setup_assistant生成的默認碰撞矩陣采用的是相鄰連桿不檢查碰撞的自動碰撞矩陣。我原本以為這個就很可靠但實際有一個非相鄰連桿在特定姿態(tài)下會干涉比如大臂在抬高到某個角度時會碰到肩部的一個支架。所以我后來手動給這兩個連桿添加了碰撞對讓規(guī)劃器把它們視為不可碰撞才徹底避免了規(guī)劃出的路徑掃過自身支架的情況。3.2 抓取位姿估計與坐標變換TF的坑視覺檢測出來的是像素坐標機械臂要用的是機器人基座坐標系下的三維坐標。兩者之間的橋梁是TF樹。我這邊固定了相機在機械臂正上方大約70cm處要求相機坐標系camera_link到機械臂基座坐標系base_link的變換是固定的。這個變換可以通過手眼標定得到也可以直接通過精確測量安裝位置和角度推導出來。我當時圖省事想直接量尺寸算坐標變換結(jié)果誤差大到離譜。為什么相機安裝的俯仰角只要偏2度在60cm遠的桌面上位置誤差就能到3cm以上機械臂夾爪張開寬度才5cm直接抓空。后來老老實實做了手眼標定。用的事OpenCV的經(jīng)典張正友標定板配合eye-in-hand標定算法但這里的eye-in-hand其實是錯誤說法因為相機是固定的應(yīng)該是eye-to-hand。網(wǎng)上大量代碼把eih和eth混在一起我一開始照著eye-in-hand的流程走標定結(jié)果一直在跳查了半天才發(fā)現(xiàn)坐標系關(guān)系搞反了。標定流程總結(jié)下來就是機械臂帶著標定板或者相機帶著標定板看你用哪種方法移動到多個不同姿態(tài)采集標定板圖像并記錄各個姿態(tài)下機械臂末端位姿然后通過AXXB方程求解相機相對機械臂基座的變換。手眼標定的誤差非常依賴采樣數(shù)據(jù)的多樣性機械臂姿態(tài)變化范圍越大、標定板在圖像中的位置越分散標定結(jié)果越準確。我只做了一次旋轉(zhuǎn)角范圍較小結(jié)果誤差有5mm后來重新采集了30個姿態(tài)覆蓋不同高度和不同水平位置誤差穩(wěn)定在1.5mm以內(nèi)。3.3 運動規(guī)劃與避障基于實際場景的規(guī)劃器選擇MoveIt默認的規(guī)劃器是RRTConnect我一開始照默認用。RRTConnect的原理是同時從起點和終點生長兩棵樹直到兩棵樹相遇所以它在無障礙場景下規(guī)劃速度非???。但缺點是生成的路徑通常比較繞路徑平滑度差。機械臂執(zhí)行時會出現(xiàn)不自然的擺動速度控制也沒那么流暢。后來我測試了OMPL里的RRTstar和STOMP。RRTstar的路徑質(zhì)量明顯好但規(guī)劃時間動不動就幾秒在實時交互場景里太慢。STOMP需要配置代價代價函數(shù)調(diào)起來麻煩。最后我的方案是先讓MoveIt用RRTConnect快速規(guī)劃一條路徑然后對路徑做一步簡化再通過MoveIt的平滑處理模塊做軌跡重采樣最終的軌跡既快又平滑。這種快速規(guī)劃后處理的思路效果非常顯著機械臂運動過程中的抖動基本消除。規(guī)劃場景Planning Scene里的障礙物設(shè)置也是一個重要環(huán)節(jié)。如果你不告訴MoveIt桌面上哪里有障礙物它規(guī)劃時只考慮機械臂自身碰撞可能規(guī)劃出一條直接穿桌子的路徑。我在項目中把桌面、垃圾桶都作為固定障礙物加到了規(guī)劃場景里。做法是在節(jié)點的/planning_scene話題上發(fā)布CollisionObject消息幾何形狀用box表示加上對應(yīng)的位姿。這樣MoveIt規(guī)劃出的路徑會自然地繞過垃圾桶而不是把機械臂的手臂插進垃圾桶里。4. 視覺與機械臂的聯(lián)動系統(tǒng)集成的關(guān)鍵代碼路徑與狀態(tài)機視覺和機械臂單獨跑都能工作但把它們連起來才真正暴露問題。這一章重點講ROS節(jié)點怎么組織、坐標怎么轉(zhuǎn)換、狀態(tài)機怎么設(shè)計這些是系統(tǒng)能否穩(wěn)定跑通的關(guān)鍵。4.1 ROS節(jié)點劃分與消息通信設(shè)計整個項目我拆成了四個ROS節(jié)點camera_node負責讀取相機圖像發(fā)布sensor_msgs/Image和sensor_msgs/CameraInfodetection_node訂閱圖像用TensorRT engine做推理發(fā)布檢測結(jié)果自定義的消息類型trashDetectionArray包含類別、置信度、像素坐標和檢測框大小controller_node訂閱檢測結(jié)果做決策生成目標位姿調(diào)用MoveIt接口規(guī)劃并執(zhí)行運動gripper_node訂閱夾爪開合指令控制夾爪舵機節(jié)點之間用ROS話題通信好處是解耦清晰。detection_node和controller_node之間不需要等待函數(shù)調(diào)用檢測結(jié)果一發(fā)布控制節(jié)點就會觸發(fā)回調(diào)。調(diào)試時還可以用rostopic echo實時查看檢測數(shù)據(jù)非常方便。這里有一個設(shè)計細節(jié)值得說為什么不在detection_node里做坐標轉(zhuǎn)換而是把像素坐標直接發(fā)布出去因為detection_node不需要關(guān)心機械臂的坐標轉(zhuǎn)換參數(shù)如果相機換了安裝位置只需要在controller_node里修改變換關(guān)系視覺節(jié)點完全不用動。這種低耦合設(shè)計讓我在后期調(diào)整標定時省了大量時間。4.2 從檢測框到抓取點的坐標轉(zhuǎn)換流程檢測到目標類別后下一個問題就是抓哪里。我用最直接的辦法用檢測框的中心點作為抓取點在圖像上的投影。由于桌面上的垃圾主要是一個個平放或稍微傾斜的物體它們的抓取點大致在物體幾何中心。通過相機內(nèi)參將像素坐標轉(zhuǎn)為相機坐標系下的坐標再通過手眼標定得到的變換矩陣轉(zhuǎn)到機械臂基座坐標系下。公式上其實很簡單[X, Y, Z, 1]^T T_eye_to_base * [x_cam, y_cam, z_cam, 1]^T。但真正的問題是Z坐標怎么確定。相機是固定朝下的桌面就在一個固定的高度我直接設(shè)Z為桌面高度。這樣如果物體有一定高度取點會偏低但夾爪有足夠的容錯空間只要能抓住物體上半部分就行。另一個重要參數(shù)是抓取姿態(tài)。垃圾不具備固定朝向我用的是豎直向下的姿態(tài)夾爪末端朝向Z軸負方向垂直于桌面。這樣機械臂第五軸和第六軸的姿態(tài)是確定的只需要控制XY位置。實際上我測試過帶一點傾斜角度去抓取斜坡上的物體但成功率反而不如豎直抓取因為物體的實際朝向很難提前判斷。豎直向下抓取配合軟指夾爪對大多數(shù)扁平垃圾紙團、果皮和圓柱垃圾易拉罐都有不錯的適應(yīng)性。4.3 狀態(tài)機設(shè)計檢測、定位、抓取、投放的調(diào)度邏輯整個系統(tǒng)的核心邏輯是一個狀態(tài)機用Python實現(xiàn)狀態(tài)包括IDLE、DETECTED、REACHING、GRASPING、LIFTING、MOVING_TO_BIN、RELEASING、RETURNING。狀態(tài)之間的切換由MoveIt的回調(diào)和夾爪傳感器反饋觸發(fā)。這里我踩的坑是一開始沒有考慮到機械臂持續(xù)運動時的狀態(tài)同步。比如MoveIt執(zhí)行完REACHING狀態(tài)后需要夾爪閉合但夾爪閉合需要時間必須等夾爪完全閉合后才能進入LIFTING狀態(tài)。我最初用固定延時1秒結(jié)果機械臂上升太快把物體甩掉了。后來改成夾爪霍爾傳感器檢測到物體夾緊后再切換到下一狀態(tài)成功率一下子從70%提升到95%。狀態(tài)機還有一個關(guān)鍵點是超時和失敗處理。抓取不是每次都能成功比如物體太滑或者位置太偏。我給GRASPING狀態(tài)加了一個超時設(shè)定如果超過3秒還沒有檢測到夾緊信號狀態(tài)機就回到IDLE讓機械臂重新等待識別或調(diào)整目標點。否則機械臂會卡在原地整個系統(tǒng)就僵住了。5. 實機調(diào)試中踩過的坑與排查思路這一章是全文最想讓你仔細看的因為這些坑我都是在連續(xù)調(diào)試十幾個小時之后才找到根因的。每一類問題都值得拿出來單獨說。5.1 相機標定與手眼標定失敗的原因分析手眼標定失敗的最常見原因是數(shù)據(jù)采集不夠好而不是算法本身有問題。我第一次標定時機械臂只轉(zhuǎn)了10來種姿態(tài)而且標定板在圖像里的位置幾乎都在中間。標定結(jié)果每次運行都不一樣旋轉(zhuǎn)矩陣的方差特別大。后來我意識到手眼標定的核心是讓標定板在相機視野的不同區(qū)域都有足夠的約束姿態(tài)變化要足夠大才能解出穩(wěn)定的變換矩陣。另一個容易踩的坑是標定板角點的亞像素提取失敗。光照太強或標定板太舊時OpenCV的findChessboardCorners經(jīng)常返回False或者角點順序錯亂。解決辦法是把標定板放在陰影下并且事先做一次灰度歸一化。在采集時保持標定板平整不要彎折。否則標定出來的矩陣也會有明顯偏差。標定完成后一定要做一次交叉驗證把標定板放在桌面上幾個已知位置用機械臂末端去觸碰標定板上的特定角點對比兩個位置的實際坐標和通過標定矩陣換算出的坐標。如果誤差超過3mm就要重新標定。這套驗證流程看起來多花時間但能避免后面所有調(diào)試都在錯誤誤差前提下的浪費時間。5.2 MoveIt規(guī)劃失敗與碰撞檢測誤判的處理MoveIt規(guī)劃失敗最典型的報錯是Unable to solve the planning problem原因通常是目標位姿在機械臂的不可達區(qū)域之外。我畫了機械臂的工作空間包絡(luò)把所有目標抓取點都限制在包絡(luò)之內(nèi)但仍然有一小部分點規(guī)劃失敗。深入排查后發(fā)現(xiàn)原因是末端執(zhí)行器的默認方向太苛刻。我指定目標位姿時用了orientation constraint要求夾爪完全垂直于桌面但實際可達的抓取姿態(tài)會略微傾斜。后來我去掉了過于嚴格的方向約束只限定Z軸方向在正負5度范圍內(nèi)規(guī)劃成功率從85%提升到了100%。碰撞檢測誤判是指MoveIt認為某個路徑會碰撞但目測明明不會。這種情況多半是碰撞矩陣里包含了不該包含的碰撞對或者物體模型尺寸設(shè)置錯了。我用的是自組臂有些連桿的STL模型是從網(wǎng)上找的簡化版幾何形狀和實物有一定差異。比如末端夾爪的STL模型比實際夾爪長了一截導致MoveIt認為任何靠近桌面的抓取動作都會碰撞。解決辦法是直接簡化夾爪碰撞模型用一個圓柱體代替尺寸略小于實際夾爪給規(guī)劃器留一點冗余。5.3 實時性瓶頸排查CPU占用與推理延遲整個系統(tǒng)在運行時我用htop和nvtop監(jiān)控資源占用。發(fā)現(xiàn)MoveIt規(guī)劃時CPU單核占用達到100%而YOLOv5推理在GPU上只占60%但GPU內(nèi)存卻異常高導致顯存不足報警。排查后發(fā)現(xiàn)是TensorRT engine的輸入輸出buffer沒有釋放每次推理都重新分配內(nèi)存導致顯存泄漏。修正的方式是初始化時一次性分配好固定大小的buffer推理時只復制數(shù)據(jù)不重新分配。這一個小修改讓顯存占用從2.1GB降到了0.9GB長時間運行也不會再報錯。另一個瓶頸是ROS話題的傳輸延遲。默認的TCPROS在大圖像傳輸時會有幾百毫秒的延遲我通過設(shè)置roscpp的傳輸類型為UDPROS解決了這個問題。核心原理是UDP掉包重傳機制比TCP輕量適合圖像這種大流量實時性要求高的數(shù)據(jù)。實際測試下來圖像從相機到視覺節(jié)點的延遲從180ms降到了35ms這個提升對整個系統(tǒng)的實時響應(yīng)非常有幫助。6. 項目源碼與設(shè)計文檔的使用指引把源碼和設(shè)計文檔打包交付時我特意做了目錄整理和README說明因為我知道如果不寫清楚別人拿到這堆文件很容易卡在環(huán)境配置上。這里我也把源碼結(jié)構(gòu)和使用步驟大致講一下幫你少走彎路。6.1 源碼目錄結(jié)構(gòu)與關(guān)鍵文件說明壓縮包解壓后頂層目錄大概是這樣的trash_sorting_robot/ ├── README.md ├── src/ │ ├── vision/ │ │ ├── yolo_detector.py │ │ ├── export_tensorrt.sh │ │ └── weights/ │ ├── controller/ │ │ ├── state_machine.py │ │ ├── moveit_control.py │ │ └── gripper_control.py │ ├── calibration/ │ │ ├── hand_eye_calibration.py │ │ └── camera_calibration.py │ └── launch/ │ ├── system.launch │ └── moveit_plan.launch ├── docs/ │ ├── 設(shè)計文檔.pdf │ ├── 操作手冊.md │ └── 實驗數(shù)據(jù)記錄.xlsx └── requirements.txtvision/yolo_detector.py是視覺節(jié)點的核心文件里面包含了TensorRT engine的加載和推理邏輯。如果你沒有TensorRT環(huán)境也可以直接改成PyTorch推理但要注意修改輸入預處理的部分。controller/moveit_control.py封裝了MoveIt的所有接口包括設(shè)置目標位姿、執(zhí)行規(guī)劃、等待執(zhí)行完成等。controller/state_machine.py是完整的狀態(tài)機實現(xiàn)里面有一個TrashSortingStateMachine類你可以在__init__里修改垃圾桶對應(yīng)的類別列表比如把易拉罐對應(yīng)到可回收桶、紙團對應(yīng)到紙類桶。calibration/hand_eye_calibration.py是手眼標定腳本需要配合標定板使用。代碼里有詳細的注釋按步驟運行即可。6.2 環(huán)境部署步驟與ROS版本注意事項環(huán)境依賴寫在了requirements.txt里主要包括Python 3.6.9、PyTorch 1.7.1、TensorRT 7.1.3、OpenCV 4.5.2、ROS Melodic、MoveIt 1.0.3。這些版本組合我是實測過的如果完全照抄可以最大程度避免版本沖突。ROS版本這里重點說一下如果你用的是ROS NoeticUbuntu 20.04那么MoveIt版本是2.xAPI和Melodic下的1.x有一些差異主要的坑是moveit_commander的接口在Noetic下沒那么穩(wěn)定建議優(yōu)先使用MoveIt的C API。如果你仍然想用Python API需要切換到moveit_py包或者使用ROS2的moveit2。我這邊因為沒有ROS2的需求所以最終選擇了MelodicC接口組合穩(wěn)得很。在編譯過程中如果遇到moveit_ros_planning找不到的問題可以在安裝MoveIt時把ros-melodic-moveit和ros-melodic-moveit-ros-planning都裝全不要只裝核心包否則會缺失一些頭文件。6.3 設(shè)計文檔中不被注意但很實用的細節(jié)設(shè)計文檔里除了常規(guī)的需求分析、系統(tǒng)架構(gòu)、模塊設(shè)計還有三塊我覺得價值最大一是實驗數(shù)據(jù)記錄.xlsx其中原始記錄了不同光照、不同角度下識別成功率的具體數(shù)據(jù)比如在光照充足時四類垃圾的平均識別率是97.2%逆光時降到了88.5%這些數(shù)據(jù)可以幫你預判實際使用環(huán)境的表現(xiàn)二是機械臂末端夾爪的結(jié)構(gòu)設(shè)計圖紙我用了3D打印的軟性硅膠指套比硬塑膠夾爪對不規(guī)則垃圾的適應(yīng)性好很多設(shè)計文檔里有STL文件三是操作手冊.md里關(guān)于硬件上電順序的警告控制器必須先上電再啟動ROS節(jié)點否則機械臂會處于未使能狀態(tài)導致無法規(guī)劃。這些細節(jié)單獨看起來不起眼但都是我在實測中累積出來的經(jīng)驗如果你只是照著設(shè)計文檔的框架去做很可能忽略掉這些隱藏信息然后在某個環(huán)節(jié)卡住很久。7. 后續(xù)擴展與個人心得項目做到這個程度已經(jīng)能在實驗室穩(wěn)定演示了。但如果你有興趣往更實用方向走或者想把這套東西改造成更完整的系統(tǒng)我有幾個思路和一些發(fā)自肺腑的建議。7.1 從單分類到可回收資源分揀的升級思路目前的系統(tǒng)只能做一次抓一個的離散抓取吞吐量不高。想提升效率的話可以把視覺模塊升級為流式檢測多目標排隊也就是一次識別畫面里多個垃圾然后按優(yōu)先級逐個規(guī)劃抓取不需要每次抓完都重新識別整個場景。這樣能減少機械臂空等時間吞吐量理論上能提升50%以上。另外可以嘗試把識別類別擴展到更多細分比如把塑料瓶細分為PET、HDPE等材質(zhì)類型。這需要更多的數(shù)據(jù)樣本和更精細的模型但意義很大直接對接后端回收產(chǎn)業(yè)鏈。如果你對垃圾分類感興趣可以往細粒度材質(zhì)識別方向深挖這個方向目前公開數(shù)據(jù)集相對較少但也是實際需求比較旺盛的領(lǐng)域。機械臂部分也可以做動態(tài)抓取。目前桌面上的物體是靜止的如果你把物體放到一個緩慢轉(zhuǎn)動的轉(zhuǎn)盤上讓YOLOv5在運動過程中實時識別并預測抓取點MoveIt就需要和視覺聯(lián)動做在線軌跡規(guī)劃。這個復雜度會上升好幾個臺階但做好了就是一個完整的視覺伺服項目含金量很高。7.2 關(guān)于學習路徑的一些建議做這類項目最忌諱一上來就攤大餅把YOLO、MoveIt、ROS、標定、機械臂建模一起學很容易被勸退。我的建議是分三步走第一周先跑通YOLOv5官方訓練流程用公開數(shù)據(jù)集訓練一個模型重點理解模型接口和推理流程第二周用MoveIt控制機械臂到達固定點不做視覺理解TF和規(guī)劃流程第三周才開始把兩者串起來。每走一步都要確保自己能解釋清楚原理再進入下一步。還有一個小建議項目中出現(xiàn)問題時盡量自己通過rostopic echo、rosrun rqt_graph、rosrun rqt_tf_tree這些工具去定位而不是直接查代碼。因為很多問題出在運行時的話題連接或坐標系關(guān)系上看代碼看不出什么但一看TF樹和數(shù)據(jù)流就一目了然了。我用這套排查方法節(jié)省了大把時間。最后動手做永遠比看教程學得快。哪怕你的機械臂很簡陋、相機是幾十塊的USB攝像頭先把一個最簡單的識別-抓取-投放閉環(huán)跑通后面的優(yōu)化才有意義。真實項目里你學到的排錯能力才是完全不亞于技術(shù)本身的東西。本文還有配套的精品資源點擊獲取