器人常用命令實(shí)戰(zhàn):從SSH連接到ROS2調(diào)試排查)
我到現(xiàn)在還記得第一次去現(xiàn)場(chǎng)調(diào)移動(dòng)機(jī)器人的情景屏幕上日志滾得飛快測(cè)試工程師問(wèn)我下一步該敲什么命令我愣了好一會(huì)兒最后只能心虛地回一句“我查一下文檔”。后來(lái)這些年項(xiàng)目越做越雜從底盤(pán)通信、機(jī)械臂軌跡到傳感器標(biāo)定哪一層都離不開(kāi)命令行“機(jī)器人常用命令”這八個(gè)字也逐漸變成我真正的武器庫(kù)。無(wú)論你接觸的是AGV、協(xié)作機(jī)械臂還是輪式巡檢機(jī)器人只要底層跑著Linux系統(tǒng)和ROS生態(tài)這套玩轉(zhuǎn)命令行的方法幾乎都能復(fù)用。這篇就來(lái)盤(pán)一盤(pán)我真正用得高頻、也踩過(guò)不少坑的那些命令尤其適合剛進(jìn)機(jī)器人行業(yè)、不想每次調(diào)試都靠到處搜答案的朋友。1. 動(dòng)手之前先理思路機(jī)器人命令的三種作用層次1.1 從“連不上”到“不會(huì)動(dòng)”再到“查不清”先說(shuō)一個(gè)很容易踩的認(rèn)知誤區(qū)很多人以為機(jī)器人常用命令就是背幾個(gè)rostopic echo、ros2 topic list結(jié)果一到現(xiàn)場(chǎng)就露餡。因?yàn)椤懊睢边@三個(gè)字背后其實(shí)藏著一整套解決問(wèn)題的分層邏輯。我習(xí)慣把機(jī)器人調(diào)試?yán)镉龅降那闆r分成三類(lèi)連不上、不會(huì)動(dòng)、查不清。連不上指的是你的電腦跟機(jī)器人之間壓根沒(méi)有建立通信常見(jiàn)于現(xiàn)場(chǎng)網(wǎng)絡(luò)配置亂、USB串口沒(méi)權(quán)限不會(huì)動(dòng)是系統(tǒng)已經(jīng)連上了但底盤(pán)不轉(zhuǎn)、機(jī)械臂不響應(yīng)問(wèn)題多半出在控制話(huà)題、服務(wù)或使能邏輯上查不清是機(jī)器人確實(shí)在跑但行為詭異時(shí)好時(shí)壞這時(shí)候就得靠日志、錄包、診斷工具去還原現(xiàn)場(chǎng)。這三類(lèi)問(wèn)題對(duì)應(yīng)三類(lèi)命令。連接命令負(fù)責(zé)打通鏈路控制命令負(fù)責(zé)讓機(jī)器人執(zhí)行動(dòng)作排查命令負(fù)責(zé)定位異常。所以我在給團(tuán)隊(duì)成員做培訓(xùn)時(shí)從來(lái)不說(shuō)“你把這個(gè)命令記一下”而是讓他們先判斷當(dāng)前處于哪一層再?zèng)Q定下一步敲什么。層與層之間有順序關(guān)系連接都斷了就直接發(fā)速度指令沒(méi)反應(yīng)很正常不是機(jī)器人壞了而是你跳過(guò)了前提。1.2 機(jī)器人其實(shí)是一套分布式系統(tǒng)別把它當(dāng)成一個(gè)“設(shè)備”很多新手會(huì)下意識(shí)地把機(jī)器人理解成“一個(gè)能跑的箱子”或者“一條機(jī)械臂”但實(shí)際拆開(kāi)看一輛AGV里至少有一個(gè)工控機(jī)、一個(gè)底層運(yùn)動(dòng)控制板、一臺(tái)激光雷達(dá)、若干電機(jī)驅(qū)動(dòng)器一臺(tái)協(xié)作機(jī)械臂則有關(guān)節(jié)伺服、力矩傳感器、安全控制板和安全I(xiàn)O。它們之間的溝通大量依賴(lài)局域網(wǎng)絡(luò)、CAN總線(xiàn)、串口和USB協(xié)議。命令行的核心作用就是把這一堆離散的硬件抽象成“節(jié)點(diǎn)”“話(huà)題”“服務(wù)”“設(shè)備文件”讓你可以在統(tǒng)一的地方觀察和控制。也正因?yàn)槿绱宋也艜?huì)在下面花大量篇幅講lsusb、ip addr、ros2 node list這類(lèi)看起來(lái)基礎(chǔ)得不像“機(jī)器人技術(shù)”的命令?;A(chǔ)不代表不重要恰恰相反現(xiàn)場(chǎng)運(yùn)維百分之六七十的時(shí)間都在跟這些基礎(chǔ)命令打交道。你理解了機(jī)器人是分布式的自然就理解為什么“排查要從底層往上走”——先確認(rèn)硬件在系統(tǒng)里存在再確認(rèn)驅(qū)動(dòng)正常再確認(rèn)協(xié)議通信正常最后才是上層控制邏輯的問(wèn)題。這套排查順序是比任何一條具體命令都重要的心法。2. 先把機(jī)器人“接進(jìn)來(lái)”連接與通信命令實(shí)戰(zhàn)2.1 網(wǎng)線(xiàn)和SSH最快能控制機(jī)器人的方式不論什么機(jī)器人到了現(xiàn)場(chǎng)第一件事往往是確認(rèn)網(wǎng)絡(luò)鏈路。工控機(jī)上一般都會(huì)預(yù)裝好機(jī)器人的運(yùn)行環(huán)境我們要做的就是用SSH連進(jìn)去操作。如果你的機(jī)器人帶屏幕和鍵鼠直接在它上面開(kāi)終端也行但我更推薦SSH因?yàn)楝F(xiàn)場(chǎng)鍵盤(pán)鼠標(biāo)經(jīng)常被擋在設(shè)備內(nèi)部而且一旦機(jī)器人跑起來(lái)你要有辦法在安全的距離外遠(yuǎn)程觀察和控制。先看網(wǎng)絡(luò)地址。工控機(jī)通常有網(wǎng)口IP可能是靜態(tài)的也可能是DHCP自動(dòng)獲取的拿到IP前不要急著敲SSH。老派命令是ifconfig新系統(tǒng)里很多已經(jīng)不帶這個(gè)了我一般直接用ip addr show ping 192.168.1.100ip addr show會(huì)輸出所有網(wǎng)卡狀態(tài)找eth0、eth1或wlan0下的inet字段那就是本機(jī)IP。連不上時(shí)先ping一下目標(biāo)地址能通再考慮SSH否則后面全是白費(fèi)。ssh ros_user192.168.1.100提一句現(xiàn)場(chǎng)最常踩的坑機(jī)器人工控機(jī)可能默認(rèn)沒(méi)有開(kāi)啟SSH服務(wù)你會(huì)看到Connection refused。這時(shí)候如果沒(méi)法直接按鍵操作只能通過(guò)顯示器登錄后確認(rèn)一下服務(wù)狀態(tài)sudo systemctl enable --now ssh sudo systemctl status ssh我用過(guò)一個(gè)巡檢機(jī)器人項(xiàng)目最初出廠(chǎng)鏡像里就是沒(méi)開(kāi)SSH團(tuán)隊(duì)到現(xiàn)場(chǎng)傻眼最后只能拆開(kāi)外殼外接屏幕才搞定。后來(lái)我們把這一步寫(xiě)進(jìn)了出廠(chǎng)檢查表從此再?zèng)]出過(guò)這類(lèi)問(wèn)題。2.2 USB串口和虛擬網(wǎng)口沒(méi)有屏幕時(shí)的救命通道有些嵌入式主控板比如常見(jiàn)的樹(shù)莓派、RK3588核心板、NVIDIA Jetson系列可以通過(guò)USB線(xiàn)直接給電腦虛擬出一個(gè)網(wǎng)口或者串口。這種方式在機(jī)器人沒(méi)配屏幕的時(shí)候幾乎就是唯一的調(diào)試入口。先確認(rèn)系統(tǒng)有沒(méi)有識(shí)別到USB設(shè)備。我慣用的兩步是lsusb dmesg | grep -E ttyUSB|ttyACM|usb | tail -n 20如果板子通過(guò)USB虛擬成了串口一般會(huì)生成/dev/ttyUSB0或/dev/ttyACM0??吹皆O(shè)備之后用串口工具連上去screen /dev/ttyUSB0 115200 # 或 minicom -D /dev/ttyUSB0 -b 115200如果機(jī)器人類(lèi)似開(kāi)發(fā)板走的是USB虛擬網(wǎng)口模式RNDIS或NCM你會(huì)在本機(jī)看到多出一張usb0網(wǎng)卡它通常會(huì)被分配一個(gè)固定地址常見(jiàn)的是192.168.55.1或192.168.42.1之類(lèi)。廠(chǎng)商不同地址不同但只要你看到虛擬網(wǎng)卡直接看它的inet字段然后SSH到對(duì)應(yīng)的對(duì)端IP即可。這里最折磨人的問(wèn)題是串口工具的權(quán)限報(bào)錯(cuò)。screen打開(kāi)設(shè)備時(shí)提示Permission denied別慌先看當(dāng)前用戶(hù)是否在dialout用戶(hù)組里sudo usermod -a -G dialout $USER改完組成員要注銷(xiāo)重新登錄才生效。很多教程沒(méi)有強(qiáng)調(diào)“重新登錄”這一步導(dǎo)致用戶(hù)反復(fù)敲sudo chmod 777 /dev/ttyUSB0那種做法只能臨時(shí)用一次拔插設(shè)備后權(quán)限又會(huì)變回原來(lái)的樣子。2.3 不用屏幕的IP急救思路真到了機(jī)器人既沒(méi)屏幕又沒(méi)記住IP的處境先別拆機(jī)。如果機(jī)器人通過(guò)網(wǎng)線(xiàn)跟電腦直連給它手動(dòng)配一個(gè)同網(wǎng)段的靜態(tài)IP通常能解決問(wèn)題。你可以把電腦的有線(xiàn)網(wǎng)卡IP配成192.168.1.50/24然后用nmap或arp-scan去掃局域網(wǎng)里可能的設(shè)備。注意這類(lèi)掃描工具在生產(chǎn)環(huán)境要慎用有些工業(yè)交換機(jī)會(huì)有告警但點(diǎn)到為止我自己用得最多的其實(shí)是先把ping 192.168.1.100這類(lèi)常見(jiàn)地址挨個(gè)試一遍不行再掃。如果板子仍然支持串口登錄那就更直接了用USB轉(zhuǎn)串口接上調(diào)試串口進(jìn)入系統(tǒng)后敲ip addr拿IP順便還能改靜態(tài)配置。說(shuō)到底急救通道要靠平時(shí)留好后門(mén)而不是現(xiàn)場(chǎng)臨陣磨槍。我一般在交項(xiàng)目時(shí)會(huì)專(zhuān)門(mén)在部署文檔里寫(xiě)一頁(yè)“無(wú)頭模式應(yīng)急登錄指南”包含串口波特率、默認(rèn)賬號(hào)、SSH開(kāi)關(guān)命令這是現(xiàn)場(chǎng)最能救命的一頁(yè)紙。3. 核心控制用ROS/ROS 2命令讓機(jī)器人真正動(dòng)起來(lái)3.1 ROS 1與ROS 2常用命令的對(duì)應(yīng)關(guān)系現(xiàn)在機(jī)器人行業(yè)依然有ROS 1存量項(xiàng)目但新項(xiàng)目基本都切到ROS 2了。兩個(gè)版本的命令風(fēng)格差異很大ROS 1是rostopic、rosservice、rosnode這類(lèi)分散式設(shè)計(jì)ROS 2則是統(tǒng)一前綴ros2 topic/service/node/param/action。給出對(duì)照表能少走很多彎路功能ROS 1命令ROS 2命令查看節(jié)點(diǎn)列表rosnode listros2 node list查看話(huà)題列表rostopic listros2 topic list打印話(huà)題數(shù)據(jù)rostopic echo /topicros2 topic echo /topic手動(dòng)發(fā)布話(huà)題rostopic pub /topic std_msgs/String data: hiros2 topic pub /topic std_msgs/msg/String {data: hi} --once查看話(huà)題頻率rostopic hz /topicros2 topic hz /topic調(diào)用服務(wù)rosservice call /srv data: 1ros2 service call /srv std_srvs/srv/SetBool {data: true}修改參數(shù)rosparam set /param 1ros2 param set /node param 1ROS 2剛上手的人最容易記混的就是消息類(lèi)型帶不帶msg。ROS 1里寫(xiě)std_msgs/StringROS 2里卻要寫(xiě)std_msgs/msg/String漏掉/msg就會(huì)告訴你找不到類(lèi)型。這個(gè)坑我現(xiàn)在閉著眼都能避開(kāi)因?yàn)椴冗^(guò)的次數(shù)實(shí)在太多了。3.2 話(huà)題通信看數(shù)據(jù)、發(fā)數(shù)據(jù)、測(cè)頻率話(huà)題是ROS生態(tài)里機(jī)器人“廣播電臺(tái)”導(dǎo)航節(jié)點(diǎn)發(fā)布速度指令底盤(pán)驅(qū)動(dòng)節(jié)點(diǎn)去訂閱。所以調(diào)機(jī)器人“動(dòng)不動(dòng)”第一件事永遠(yuǎn)是看相關(guān)話(huà)題上有沒(méi)有數(shù)據(jù)。以最常見(jiàn)的cmd_vel話(huà)題為例ros2 topic list | grep cmd_vel ros2 topic echo /cmd_vel如果你一直盯著終端沒(méi)有任何輸出說(shuō)明這個(gè)話(huà)題沒(méi)有發(fā)布者或者發(fā)布頻率極低。這時(shí)候要查節(jié)點(diǎn)圖是不是導(dǎo)航模塊沒(méi)起來(lái)或者控制指令沒(méi)有真正輸出。再測(cè)一下話(huà)題頻率ros2 topic hz /cmd_vel如果顯示no new messages大概率是上游沒(méi)發(fā)布如果hz忽高忽低那要懷疑工控機(jī)負(fù)載問(wèn)題。頻率能穩(wěn)在十幾赫茲以上數(shù)據(jù)鏈路基本才算正常。想要手動(dòng)讓機(jī)器人動(dòng)起來(lái)可以直接發(fā)布速度指令但真機(jī)上操作要極其克制。我自己只會(huì)在輪子懸空、底盤(pán)被架起、急停按鈕在手邊這三個(gè)條件同時(shí)滿(mǎn)足時(shí)才敢在真實(shí)設(shè)備上這么干ros2 topic pub /cmd_vel geometry_msgs/msg/Twist {linear: {x: 0.1, y: 0.0, z: 0.0}, angular: {z: 0.0}} --rate 10這條命令會(huì)以10Hz持續(xù)發(fā)布敲CtrlC停掉。記得把速度調(diào)小發(fā)布完馬上觀察電機(jī)有沒(méi)有“嗡”一聲通電響應(yīng)。我見(jiàn)過(guò)有人用--once只發(fā)一次結(jié)果機(jī)器人沒(méi)反應(yīng)就以為電機(jī)壞了其實(shí)是速度指令只到了一個(gè)瞬間驅(qū)動(dòng)還沒(méi)來(lái)得及響應(yīng)就已經(jīng)停了。3.3 讓底盤(pán)和機(jī)械臂執(zhí)行動(dòng)作的高頻命令移動(dòng)機(jī)器人到底盤(pán)部分核心接口是/cmd_vel但很多機(jī)器人底盤(pán)還要求先調(diào)用“使能/去使能”服務(wù)不然電機(jī)會(huì)處于抱閘或待機(jī)狀態(tài)。這時(shí)候用服務(wù)調(diào)用命令ros2 service list | grep -E enable|power ros2 service call /driver/enable std_srvs/srv/SetBool {data: true}機(jī)械臂的控制方式則更偏向動(dòng)作接口Action。先列出可用的Actionros2 action list -t假設(shè)機(jī)械臂控制器暴露了一個(gè)關(guān)節(jié)軌跡Action例如很多項(xiàng)目里常見(jiàn)的/arm_controller/follow_joint_trajectory發(fā)送目標(biāo)需要帶上目標(biāo)關(guān)節(jié)角度、速度、時(shí)間。命令行手寫(xiě)JSON比較痛苦我通常會(huì)打開(kāi)另一個(gè)終端先echo一下當(dāng)前關(guān)節(jié)狀態(tài)ros2 topic echo /joint_states --once把當(dāng)前關(guān)節(jié)角度抄下來(lái)再按目標(biāo)位置構(gòu)造Action目標(biāo)。實(shí)際發(fā)送的完整命令會(huì)很長(zhǎng)這也是為什么我更推薦用腳本或RViz里手動(dòng)拖拽來(lái)測(cè)試機(jī)械臂但當(dāng)你只需要驗(yàn)證通信鏈路時(shí)命令行仍然是最快的探針。對(duì)于ROS 1存量機(jī)械臂還會(huì)遇到直接用服務(wù)使能的習(xí)慣比如rosservice call /arm_driver/servo_on {} rosservice call /arm_driver/move_joint joint1: 0.5, joint2: 0.8這些服務(wù)名和字段跟具體廠(chǎng)商強(qiáng)相關(guān)沒(méi)有統(tǒng)一標(biāo)準(zhǔn)。拿到一個(gè)不熟悉的機(jī)械臂項(xiàng)目我會(huì)先rosservice list或者ros2 service list看全部接口再?gòu)闹胁鲁觥笆鼓?、停止、回零”這幾個(gè)關(guān)鍵語(yǔ)義比直接翻SDK文檔更快。3.4 錄包回放調(diào)試問(wèn)題時(shí)的“時(shí)光機(jī)”機(jī)器人跑著跑著突然抽風(fēng)是偶發(fā)問(wèn)題這時(shí)候不要急著改代碼先錄一段數(shù)據(jù)把現(xiàn)場(chǎng)原樣保存下來(lái)。ROS 2里錄包命令很簡(jiǎn)潔ros2 bag record -a -o fault_bag-a會(huì)錄制所有話(huà)題數(shù)據(jù)量比較大但調(diào)試現(xiàn)場(chǎng)我常常無(wú)腦全錄因?yàn)槭潞蠓治鰰r(shí)你很難預(yù)料哪個(gè)話(huà)題才是關(guān)鍵。錄完以后用ros2 bag info fault_bag查看錄到了哪些內(nèi)容回放則用ros2 bag play fault_bag回放時(shí)需要注意一點(diǎn)回放只會(huì)發(fā)話(huà)題消息不會(huì)自動(dòng)觸發(fā)機(jī)械臂執(zhí)行動(dòng)作因?yàn)檎鎸?shí)的控制鏈路上通常還有安全邏輯、狀態(tài)機(jī)、心跳檢測(cè)。所以錄包回放主要用來(lái)離線(xiàn)復(fù)現(xiàn)感知和定位問(wèn)題而不是把整個(gè)機(jī)器人帶回放狀態(tài)。ROS 1時(shí)代對(duì)應(yīng)的是rosbag record -a和rosbag play邏輯一樣。4. 傳感器與底層硬件排查機(jī)器人“感知不到”怎么辦4.1 相機(jī)、雷達(dá)、IMU有沒(méi)有被系統(tǒng)正常識(shí)別機(jī)器人不上電傳感器不工作很多時(shí)候根本不是算法問(wèn)題而是系統(tǒng)層面壓根沒(méi)發(fā)現(xiàn)設(shè)備。排查思路很樸素先看USB層再看驅(qū)動(dòng)層最后看ROS話(huà)題層。首先確認(rèn)USB設(shè)備識(shí)別情況lsusb如果你在列表里看不到相機(jī)的廠(chǎng)商ID大概率是供電不足、線(xiàn)材問(wèn)題或者設(shè)備壓根沒(méi)上電。USB線(xiàn)對(duì)相機(jī)的影響很大我在現(xiàn)場(chǎng)換過(guò)不止一次看起來(lái)一模一樣但內(nèi)部線(xiàn)序不同的線(xiàn)所以遇到設(shè)備偶爾掉線(xiàn)先懷疑線(xiàn)材成本最低。攝像頭檢測(cè)還可以用V4L2工具v4l2-ctl --list-devices激光雷達(dá)則要看串口或網(wǎng)口。很多單線(xiàn)雷達(dá)走串口用上一章的方法確認(rèn)/dev/ttyUSB0存在后再去看雷達(dá)的話(huà)題有沒(méi)有輸出。IMU有時(shí)候走I2C或者SPI和主控是固定連接的這時(shí)候dmesg | grep -i imu往往能快速看到驅(qū)動(dòng)加載日志。系統(tǒng)層識(shí)別沒(méi)問(wèn)題后再進(jìn)入ROS話(huà)題層確認(rèn)ros2 topic list | grep -E scan|pointcloud|image|camera_info ros2 topic hz /scan話(huà)題有頻率說(shuō)明驅(qū)動(dòng)和硬件都正常。頻率為0則優(yōu)先看驅(qū)動(dòng)進(jìn)程是否還活著進(jìn)程活著再去翻日志這就是我說(shuō)的自下而上排查順序。4.2 用CAN命令和驅(qū)動(dòng)器對(duì)話(huà)底盤(pán)電機(jī)驅(qū)動(dòng)器很大一部分走CAN總線(xiàn)。Linux下把CAN接口拉起來(lái)是有固定流程的需要先把接口配置成CAN模式sudo ip link set can0 up type can bitrate 500000常見(jiàn)波特率有250K、500K和1M具體參考電機(jī)驅(qū)動(dòng)手冊(cè)。設(shè)置好之后可以用ip -details -statistics link show can0查看接口狀態(tài)重點(diǎn)關(guān)注有沒(méi)有ERROR-ACTIVE如果出現(xiàn)BUS-OFF說(shuō)明總線(xiàn)上有比較嚴(yán)重的錯(cuò)誤多半是波特率不匹配、終端電阻缺失或線(xiàn)序接錯(cuò)。查看總線(xiàn)上實(shí)際流動(dòng)的數(shù)據(jù)老牌命令是candumpcandump can0如果總線(xiàn)上有驅(qū)動(dòng)器周期上報(bào)狀態(tài)這里就會(huì)不斷滾幀。不滾幀不代表總線(xiàn)壞了可能只是當(dāng)前節(jié)點(diǎn)沒(méi)有報(bào)文需要你主動(dòng)讓某個(gè)驅(qū)動(dòng)器回復(fù)。測(cè)試CAN發(fā)送可以這樣發(fā)一顆標(biāo)準(zhǔn)幀cansend can0 123#DEADBEEF手動(dòng)測(cè)CAN時(shí)最好只針對(duì)測(cè)試設(shè)備比如PID調(diào)試器或?qū)iT(mén)設(shè)置的從站ID。直接對(duì)車(chē)上正常工作的驅(qū)動(dòng)器發(fā)亂幀輕則通訊中斷重則觸發(fā)急停甚至損壞設(shè)備。這類(lèi)問(wèn)題的排查不能只靠試。4.3 電源、溫度和資源占用其實(shí)也是一等公民機(jī)器人看著像軟件問(wèn)題查到最后往往是硬件狀態(tài)不對(duì)。最常見(jiàn)的是電池電壓跌落導(dǎo)致電機(jī)驅(qū)動(dòng)器過(guò)壓保護(hù)或欠壓報(bào)警。某些底盤(pán)會(huì)把電池狀態(tài)直接發(fā)成ROS話(huà)題比如/battery_state直接用ros2 topic echo /battery_state --once如果廠(chǎng)商標(biāo)定不標(biāo)準(zhǔn)那就去底層電源管理接口找。一些工控板會(huì)把電池電量暴露在sysfs里cat /sys/class/power_supply/BAT0/capacity cat /sys/class/power_supply/BAT0/voltage_now另外還要養(yǎng)成看系統(tǒng)資源的習(xí)慣機(jī)器人運(yùn)動(dòng)控制對(duì)延遲敏感高負(fù)載可能導(dǎo)致話(huà)題卡頓、指令超時(shí)。老牌工具就是htop和free沒(méi)有就裝一個(gè)。我一般用htop按CPU占用排序看哪個(gè)進(jìn)程在偷跑。溫度方面對(duì)工控機(jī)和Jetson這類(lèi)平臺(tái)尤其關(guān)鍵sensors如果提示沒(méi)有傳感器Jetson板子可以用自帶的tegrastats查看CPU/GPU溫度和頻率。多核機(jī)器人偶爾出現(xiàn)“跑一會(huì)就死機(jī)”八成跟散熱和降頻有關(guān)千萬(wàn)不要一上來(lái)就懷疑是代碼死循環(huán)。5. 遇到疑難雜癥別慌日志與異常排查命令梳理5.1 日志檢索先定層別直接翻底朝天機(jī)器人系統(tǒng)分層多出問(wèn)題時(shí)如果直接去翻所有日志很容易被刷屏帶偏。我給自己定的習(xí)慣是先確認(rèn)是“啟動(dòng)失敗”還是“運(yùn)行中偶發(fā)故障”再?zèng)Q定看哪個(gè)日志。啟動(dòng)失敗去看launch和systemd日志運(yùn)行中故障則優(yōu)先看ROS節(jié)點(diǎn)的終端輸出、核心轉(zhuǎn)儲(chǔ)和系統(tǒng)日志。現(xiàn)在很多機(jī)器人項(xiàng)目會(huì)把核心進(jìn)程做成systemd服務(wù)查服務(wù)狀態(tài)用sudo systemctl status robot_bringup journalctl -u robot_bringup -f-f相當(dāng)于持續(xù)跟蹤相當(dāng)于把終端拉到服務(wù)日志最末端。調(diào)試現(xiàn)場(chǎng)我經(jīng)常用另一個(gè)終端專(zhuān)門(mén)掛著日志一邊操作機(jī)器人一邊看實(shí)時(shí)輸出出現(xiàn)異常馬上能定位是哪一層報(bào)的錯(cuò)。如果進(jìn)程不是systemd管理而是手動(dòng)在終端里啟動(dòng)的啟動(dòng)時(shí)順手加一句管道重定向會(huì)省很多事ros2 launch mybot_bringup mybot_launch.py 21 | tee launch.logtee讓輸出既上屏又落盤(pán)現(xiàn)場(chǎng)沒(méi)記錄下來(lái)后面就很難復(fù)盤(pán)。注意別把日志文件寫(xiě)到根目錄或系統(tǒng)盤(pán)里寫(xiě)滿(mǎn)空間曾經(jīng)見(jiàn)過(guò)一個(gè)項(xiàng)目把rosbag全錄在/下最后系統(tǒng)盤(pán)滿(mǎn)了機(jī)器人直接罷工。5.2 ROS自帶的診斷命令千萬(wàn)別忽視ROS 2自帶一套診斷工具很多人不知道。每次現(xiàn)場(chǎng)排查前我先跑一遍整體體檢ros2 doctor它會(huì)自動(dòng)檢查環(huán)境變量、網(wǎng)絡(luò)配置、話(huà)題通信、DDS發(fā)現(xiàn)問(wèn)題等并給出警告。簡(jiǎn)單說(shuō)它會(huì)幫你判斷ROS層是不是“健康”的。如果輸出的紅色項(xiàng)較多先處理紅色項(xiàng)再調(diào)機(jī)器人否則后面做什么都像在雷區(qū)里走路。還有兩個(gè)很基礎(chǔ)的排除手段常被忽略ros2 node info能看某個(gè)節(jié)點(diǎn)發(fā)布/訂閱了哪些話(huà)題以及服務(wù)、Action、參數(shù)等信息用來(lái)判斷節(jié)點(diǎn)是否真的把接口都建好了ros2 node info /robot_driver另外ROS 2的通信依靠DDS如果節(jié)點(diǎn)明明存在但話(huà)題互相看不到可能是DDS發(fā)現(xiàn)機(jī)制罷工了。這時(shí)可以重啟ROS 2守護(hù)進(jìn)程ros2 daemon stop ros2 daemon start這個(gè)動(dòng)作不是玄學(xué)它解決了不少“節(jié)點(diǎn)列表顯示不全”的詭異現(xiàn)象。我在一個(gè)項(xiàng)目里調(diào)試時(shí)明明四個(gè)節(jié)點(diǎn)都活著但ros2 node list只顯示兩個(gè)重啟daemon后四個(gè)都正常了。5.3 進(jìn)程崩潰信息與核心轉(zhuǎn)儲(chǔ)判斷節(jié)點(diǎn)進(jìn)程直接掛掉、終端也沒(méi)捕獲到異常這類(lèi)情況要靠系統(tǒng)日志。段錯(cuò)誤常見(jiàn)于C節(jié)點(diǎn)訪(fǎng)問(wèn)了非法內(nèi)存先快速瞄一眼內(nèi)核日志dmesg | tail -n 30如果看到segfault或core dump關(guān)鍵字說(shuō)明進(jìn)程確實(shí)在底層崩了再進(jìn)一步用coredumpctl查看是否有完整轉(zhuǎn)儲(chǔ)coredumpctl list coredumpctl info pid有了core文件就可以用GDB回溯崩潰現(xiàn)場(chǎng)這里不做展開(kāi)但排查思路上要明確一點(diǎn)底層崩潰往往是內(nèi)存問(wèn)題、接口不匹配或者第三方庫(kù)版本沖突不是單純靠加日志能解決的。5.4 桌面運(yùn)維里的高頻問(wèn)題速查表把現(xiàn)場(chǎng)遇到最常見(jiàn)的問(wèn)題整理成一張表每次照著走能省不少時(shí)間?,F(xiàn)象排查命令判斷依據(jù)USB設(shè)備插上沒(méi)反應(yīng)lsusb、dmesg | tail列表里沒(méi)有設(shè)備或報(bào)權(quán)限錯(cuò)誤先查線(xiàn)和供電串口能打開(kāi)但亂碼minicom -D /dev/ttyUSB0波特率不對(duì)或串口被其他程序占用節(jié)點(diǎn)列表只顯示部分ros2 daemon stop; ros2 daemon startdaemon緩存和DDS發(fā)現(xiàn)不同步話(huà)題有發(fā)布但沒(méi)訂閱ros2 topic info /topic -v看Publishers和Subscribers數(shù)量底盤(pán)指令發(fā)了不走ros2 topic echo /cmd_vel、服務(wù)列表確認(rèn)指令數(shù)據(jù)量再查驅(qū)動(dòng)使能服務(wù)CAN總線(xiàn)BUS-OFFip -details link show can0線(xiàn)序、終端電阻、波特率錯(cuò)誤程序偶發(fā)崩潰dmesg | tail、coredumpctl是否有段錯(cuò)誤和core文件這張表我寫(xiě)進(jìn)過(guò)團(tuán)隊(duì)內(nèi)部的排錯(cuò)手冊(cè)收到的反饋是“比看幾十頁(yè)開(kāi)發(fā)文檔有用”。6. 讓命令從“背下來(lái)”變成“順手用”的實(shí)操習(xí)慣6.1 環(huán)境變量和alias重復(fù)勞動(dòng)自動(dòng)化如果你的機(jī)器人項(xiàng)目固定在某個(gè)工作空間每次開(kāi)終端都要source環(huán)境日積月累非常煩。我會(huì)在.bashrc里加幾行別名把最長(zhǎng)、最容易打錯(cuò)的命令縮短alias cwcd ~/robot_ws source install/setup.bash alias rbros2 launch mybot_bringup mybot_launch.py alias kbotpkill -f bringup這里不建議盲目把source /opt/ros/humble/setup.bash寫(xiě)進(jìn)全局.bashrc因?yàn)橥粋€(gè)終端里如果加載了多個(gè)ROS版本容易互相污染。我的習(xí)慣是每個(gè)項(xiàng)目建一個(gè)專(zhuān)屬環(huán)境文件進(jìn)入項(xiàng)目前手動(dòng)加載這樣換項(xiàng)目不會(huì)產(chǎn)生靈異提問(wèn)。如果在真機(jī)運(yùn)行的時(shí)候要啟動(dòng)一整套系統(tǒng)我通常會(huì)寫(xiě)一個(gè)啟動(dòng)腳本腳本先把固定參數(shù)配好再按順序拉起節(jié)點(diǎn)。腳本里不要直接用sudo開(kāi)各種服務(wù)建議把需要特權(quán)操作的命令單獨(dú)抽出來(lái)用systemd或polkit管理這樣系統(tǒng)重啟后也能自動(dòng)恢復(fù)機(jī)器人服務(wù)不至于每次都要手動(dòng)敲一串長(zhǎng)長(zhǎng)的啟動(dòng)命令。6.2 tmux一套保留完整調(diào)試現(xiàn)場(chǎng)的辦法機(jī)器人調(diào)試往往需要同時(shí)盯好幾個(gè)終端一個(gè)看導(dǎo)航狀態(tài)一個(gè)盯驅(qū)動(dòng)日志一個(gè)隨時(shí)準(zhǔn)備發(fā)指令。對(duì)SSH連接來(lái)說(shuō)如果斷線(xiàn)重連原先終端里的進(jìn)程和輸出就都丟了。用tmux可以解決tmux new -s robot進(jìn)入tmux后按CtrlB再按C可以開(kāi)新窗口按CtrlB再按%可以左右分屏按CtrlB再按”是上下分屏。最實(shí)用的地方在于即使SSH斷開(kāi)tmux里的session還在后臺(tái)跑重新連接后執(zhí)行tmux attach -t robot現(xiàn)場(chǎng)就完整地找回來(lái)了。我用這個(gè)方法把一個(gè)導(dǎo)航調(diào)試過(guò)程保持了幾天中間反復(fù)斷網(wǎng)重連但所有終端狀態(tài)都還在效率提升非常明顯。6.3 建立一份屬于你自己的機(jī)器人命令速查清單每個(gè)機(jī)器人項(xiàng)目的接口都不一樣別人給的命令再全也未必完全匹配你的車(chē)。我強(qiáng)烈建議你從接手第一個(gè)機(jī)器人項(xiàng)目開(kāi)始就建立一份自己的“命令速查.md”內(nèi)容按這幾類(lèi)記錄每次部署時(shí)用到的網(wǎng)絡(luò)配置和SSH登錄方式、底盤(pán)和機(jī)械臂的驅(qū)動(dòng)使能方法、所有關(guān)鍵話(huà)題名稱(chēng)和消息類(lèi)型、廠(chǎng)商文檔里藏得比較深的服務(wù)接口、現(xiàn)場(chǎng)解決問(wèn)題時(shí)使用的排查命令。格式不用復(fù)雜Markdown表格就夠了。寫(xiě)文檔的最大價(jià)值不在于“以后忘了能翻”而在于記錄過(guò)程中你會(huì)被迫梳理接口之間的關(guān)系。這比單純背命令有效得多。我也是從第一臺(tái)機(jī)器人開(kāi)始這樣做現(xiàn)在累積的速查筆記已經(jīng)成了整個(gè)團(tuán)隊(duì)接手新項(xiàng)目時(shí)的第一手資料。最后再說(shuō)一個(gè)我的個(gè)人習(xí)慣每次現(xiàn)場(chǎng)解決完一個(gè)疑難問(wèn)題我都會(huì)順手往速查清單里補(bǔ)一條“現(xiàn)象命令結(jié)論”的記錄。半年后翻出來(lái)看很多當(dāng)初覺(jué)得玄乎的問(wèn)題背后原因其實(shí)都非常樸素。機(jī)器人常用命令從來(lái)不是背會(huì)的是在每一次現(xiàn)場(chǎng)折騰里用會(huì)的。希望這篇分享能幫你把那些“對(duì)著屏幕不知道敲什么”的時(shí)刻壓縮得更短把省下來(lái)的時(shí)間拿去處理真正的機(jī)器人工程問(wèn)題。