戰(zhàn):去中心化機(jī)器人操作系統(tǒng)詳解)
本文是獨(dú)立成篇的 ROS2 筆記涵蓋從命令行到仿真的全鏈路。建議配合 Ubuntu 22.04 Humble 食用。1. 什么是 ROS2為什么要用它ROS 全稱 Robot Operating System它不是傳統(tǒng)意義的操作系統(tǒng)像 Linux/Windows而是一套運(yùn)行在 Linux 上的機(jī)器人軟件框架硬件抽象屏蔽雷達(dá)、攝像頭、IMU、電機(jī)驅(qū)動的底層差異中間件提供標(biāo)準(zhǔn)化的節(jié)點(diǎn)間通信類似你之前學(xué)的 MQTT但更強(qiáng)大工具鏈調(diào)試、可視化RViz2、仿真Gazebo、錄包bag算法庫導(dǎo)航 Nav2、建圖 SLAM、MoveIt2 機(jī)械臂規(guī)劃開箱即用不用 ROS2 的痛點(diǎn)假設(shè)機(jī)器人有 3個模塊激光雷達(dá)、攝像頭、底盤控制由3個團(tuán)隊開發(fā)。沒 ROS2雷達(dá)團(tuán)隊寫個 socket 發(fā)數(shù)據(jù)攝像頭團(tuán)隊寫個串口底盤團(tuán)隊寫個 CAN接口對不上聯(lián)調(diào)地獄。有 ROS2大家都說Topic普通話雷達(dá)發(fā)/scan導(dǎo)航訂閱/scan底盤訂閱/cmd_vel即插即用。2. ROS1 vs ROS2一次顛覆性的重構(gòu)特性ROS1 (Noetic)ROS2 (Humble/Iron)核心架構(gòu)中心化必須有一個roscore / Master節(jié)點(diǎn)Master掛了全掛去中心化無Master基于 DDS 自動發(fā)現(xiàn)無單點(diǎn)故障通信中間件自定義 TCPROS/UDPROSDDS 標(biāo)準(zhǔn)工業(yè)級航空/國防在用實(shí)時性弱TCP延遲不可控強(qiáng)DDS 可配置 QoS 實(shí)時策略跨平臺主要 LinuxLinux/Windows/macOS/RTOS構(gòu)建系統(tǒng)catkinament colconPythonPython2Python3節(jié)點(diǎn)生命周期無節(jié)點(diǎn)啟動即運(yùn)行有可配置 unconfigured/inactive/active/shutdown適合產(chǎn)品安全性無加密SROS2支持認(rèn)證加密一句話總結(jié)ROS1 是實(shí)驗室玩具ROS2 是能上產(chǎn)線的商業(yè)系統(tǒng)。3. ROS2 架構(gòu)五層分層模型從底到頂DDS層 (Data Distribution Service)廠商實(shí)現(xiàn)Fast DDS默認(rèn)、CycloneDDS、Connext。負(fù)責(zé)真正的網(wǎng)絡(luò)傳輸U(kuò)DP多播/單播、共享內(nèi)存。RMW層 (ROS Middleware Interface)抽象層把不同廠商 DDS API 統(tǒng)一成一套標(biāo)準(zhǔn)接口rmw_*。好處今天用 Fast DDS明天切 CycloneDDS代碼一行不改。RCL層 (ROS Client Library)C語言實(shí)現(xiàn)的rcl封裝節(jié)點(diǎn)、話題、服務(wù)等核心概念??蛻舳藥鞂觬clcpp(C)、rclpy(Python)、rcljava(Java) 等面向開發(fā)者。應(yīng)用層你的節(jié)點(diǎn)、Nav2、MoveIt2。所以 ROS2 刪除 Master 后怎么發(fā)現(xiàn)彼此靠 DDS 的SPDP SEDP自動發(fā)現(xiàn)新節(jié)點(diǎn)上線DDS 向239.255.0.1多播發(fā)廣播我來了我有/scan發(fā)布器。其他節(jié)點(diǎn)收到回復(fù)我是誰我有什么訂閱。兩邊 QoS 兼容則建立 P2P 直連后續(xù)不經(jīng)過任何中心。4. 五大核心通信機(jī)制嵌入式類比秒懂對于 STM32 開發(fā)者用 FreeRTOS 類比ROS2組件FreeRTOS類比核心功能通信模式典型場景節(jié)點(diǎn) Node任務(wù) Task最小功能單元-激光驅(qū)動、電機(jī)控制、路徑規(guī)劃各一個節(jié)點(diǎn)話題 Topic消息隊列 串口廣播連續(xù)數(shù)據(jù)流異步、多對多雷達(dá)發(fā)/scan相機(jī)發(fā)/image_raw底盤發(fā)/odom消息 Messagestruct 結(jié)構(gòu)體數(shù)據(jù)格式定義-geometry_msgs/Twist速度,sensor_msgs/LaserScan雷達(dá)服務(wù) Service函數(shù)調(diào)用 問答一次性遠(yuǎn)程調(diào)用同步、一對一拍照、查詢電量、重置里程計動作 Action狀態(tài)機(jī) 帶反饋任務(wù)長時間可取消任務(wù)異步 反饋導(dǎo)航到目標(biāo)點(diǎn)NavigateToPose可中途取消看進(jìn)度參數(shù) ParameterFlash配置 全局變量節(jié)點(diǎn)動態(tài)配置-PID增益、最大速度不重啟即可修改4.1 話題 Topic最常用80%場景publisher_node -- /scan (sensor_msgs/LaserScan) -- subscriber_node(s)特點(diǎn)解耦發(fā)布者不知道誰在聽訂閱者不知道誰發(fā)的。4.2 服務(wù) Service客戶端發(fā) Request等待服務(wù)端 Response。同步阻塞但 ROS2 中是異步回調(diào)不會卡死。/client 請求 計算路徑 - /planner 服務(wù)端 - 返回路徑4.3 動作 Action服務(wù)話題的合體處理3秒以上的長任務(wù)導(dǎo)航、機(jī)械臂抓取。包含3部分Goal目標(biāo) Feedback周期反饋 Result最終結(jié)果且可取消。底層由3個話題2個服務(wù)實(shí)現(xiàn)但對你透明。4.4 參數(shù) QoS參數(shù)ros2 param set /turtlesim background_r 128動態(tài)改背景顏色無需重啟。QoS可靠性 RELIABLE / BEST_EFFORT歷史 KEEP_LAST持久性 TRANSIENT_LOCAL 等可針對雷達(dá)/圖像/控制指令精細(xì)配置。4.5 ROS2命令行操作ROS2 90%的調(diào)試都靠命令行掌握它就掌握了機(jī)器人的控制臺。命令族常用命令作用ros2 nodelist,info /turtlesim,kill查看/殺死節(jié)點(diǎn)ros2 topiclist -t,echo /scan,hz /scan,bw,pub /cmd_vel ...調(diào)試連續(xù)數(shù)據(jù)流最常用ros2 servicelist -t,call /spawn ...,type,find調(diào)用一次性服務(wù)ros2 actionlist,info /navigate_to_pose,send_goal調(diào)試導(dǎo)航等長任務(wù)ros2 paramlist,get /node param,set,dump/load動態(tài)改參運(yùn)行時調(diào)PIDros2 interfaceshow geometry_msgs/msg/Twist,list,proto查看消息結(jié)構(gòu)ros2 bagrecord -a,play,info錄包回放復(fù)現(xiàn)偶發(fā)Bugros2 runrun turtlesim turtlesim_node運(yùn)行單個節(jié)點(diǎn)ros2 launchlaunch nav2_bringup bringup_launch.py一鍵啟動多節(jié)點(diǎn)colconcolcon build --symlink-install,--packages-select編譯工作空間實(shí)戰(zhàn)口訣# 不知道有什么 ros2 topic list -t # 看話題 ros2 node list # 看節(jié)點(diǎn) ros2 service list # 看服務(wù) ? # 不知道數(shù)據(jù)格式 ros2 interface show sensor_msgs/msg/LaserScan ? # 看數(shù)據(jù)流是否正常 ros2 topic echo /scan --no-arr # 不打超長數(shù)組 ros2 topic hz /scan # 看雷達(dá)頻率是不是10Hz5. ROS2開發(fā)環(huán)境配置推薦組合Ubuntu 22.04 ROS2 Humble HawksbillLTS到2027年最穩(wěn)。5.1 一鍵安裝sudo apt update sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALLen_US.UTF-8 LANGen_US.UTF-8 ? sudo apt install software-properties-common -y sudo add-apt-repository universe sudo apt install curl gnupg lsb-release -y sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null ? sudo apt update sudo apt install ros-humble-desktop -y # 桌面完整版含RViz2Gazebo示例 sudo apt install python3-colcon-common-extensions python3-rosdep ros-dev-tools -y ? # 環(huán)境變量 echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc ? # 初始化 rosdep sudo rosdep init || true rosdep update5.2 多機(jī)隔離與工具# 多機(jī)器人同WiFi防串臺 export ROS_DOMAIN_ID10 # 0~101同一ID才能互通 ? # VSCode插件ROS, URDF, 記得裝 ? # Docker版Windows/Mac用戶 docker pull osrf/ros:humble-desktop驗證ros2 run demo_nodes_cpp talker ros2 run demo_nodes_py listener # 看到 hello world 互通即成功6. 工作空間與功能包開發(fā)過程的大本營工作空間 Workspace是項目的物理邊界。官方推薦~/ros2_ws。四個子目錄src放所有功能包源碼git clone都在里buildcolcon 編譯中間文件不用管install編譯產(chǎn)物setup.bash在這log編譯日志功能包 Package是最小復(fù)用單元。包含package.xml依賴聲明CMakeLists.txt(C) 或setup.py(Python) 代碼。mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src ? # 創(chuàng)建C包 ros2 pkg create my_cpp_pkg --build-type ament_cmake --dependencies rclcpp std_msgs ? # 創(chuàng)建Python包 ros2 pkg create my_py_pkg --build-type ament_python --dependencies rclpy std_msgs ? # 創(chuàng)建帶消息定義的包 ros2 pkg create my_interfaces --build-type ament_cmake --dependencies rosidl_default_generators ? cd ~/ros2_ws rosdep install -i --from-path src --rosdistro humble -y colcon build --symlink-install source install/setup.bash--symlink-install對Python極其重要改.py不用重編直接運(yùn)行。7. 節(jié)點(diǎn)機(jī)器人的工作細(xì)胞節(jié)點(diǎn) Node 是 ROS2 最小執(zhí)行單元一個獨(dú)立進(jìn)程只做一件事。import rclpy from rclpy.node import Node ? class MyNode(Node): def __init__(self): super().__init__(my_first_node) # 節(jié)點(diǎn)名唯一 self.get_logger().info(Hello, 我是工作細(xì)胞) self.timer self.create_timer(1.0, self.tick) ? def tick(self): self.get_logger().info(心跳...) ? def main(): rclpy.init() rclpy.spin(MyNode()) # 阻塞處理回調(diào) rclpy.shutdown()節(jié)點(diǎn)生命周期ROS2 獨(dú)有產(chǎn)品級必用unconfigured - inactive - active - finalized可通過生命周期管理器lifecycle_manager統(tǒng)一配置機(jī)器人啟動時先加載參數(shù)再激活。8. 話題節(jié)點(diǎn)間傳遞數(shù)據(jù)的橋梁話題 Topic 是異步、多對多廣播最常用。/demonstration Publisher ---(/cmd_vel, geometry_msgs/Twist)--- Subscriber(s)Python 發(fā)布者self.pub self.create_publisher(Twist, /cmd_vel, 10) msg Twist() msg.linear.x 0.2 self.pub.publish(msg)訂閱者self.sub self.create_subscription(LaserScan, /scan, self.scan_callback, 10) def scan_callback(self, msg): print(f最前面障礙物距離: {msg.ranges[0]})命令行調(diào)試ros2 topic list -t ros2 topic echo /turtle1/pose ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist {linear: {x: 0.5}, angular: {z: 0.5}} -1QoS 可選RELIABLE/BEST_EFFORT雷達(dá)用 BEST_EFFORT 少延遲控制指令用 RELIABLE 保到達(dá)。9. 服務(wù)節(jié)點(diǎn)間的你問我答服務(wù) Service 是同步一對一請求響應(yīng)類似函數(shù)調(diào)用。Client ---(Request: 拍照請求)--- Server Client ---(Response: 照片結(jié)果)--- Serverturtlesim 典型服務(wù)ros2 service list ros2 service call /spawn turtlesim/srv/Spawn {x: 2, y: 2, theta: 0.2, name: turtle2}代碼from turtlesim.srv import Spawn self.client self.create_client(Spawn, /spawn) req Spawn.Request(x2.0, y2.0, namet2) future self.client.call_async(req) rclpy.spin_until_future_complete(self, future)適用重置里程計、保存地圖、觸發(fā)拍照等只需執(zhí)行一次的任務(wù)。10. 通信接口數(shù)據(jù)傳遞的標(biāo)準(zhǔn)結(jié)構(gòu)所有話題/服務(wù)/動作的數(shù)據(jù)類型都有標(biāo)準(zhǔn)定義放在msg/srv/action文件中。Message (.msg)話題載體# Twist.msg Vector3 linear Vector3 angularService (.srv)請求---響應(yīng)# AddTwoInts.srv int64 a int64 b --- int64 sumAction (.action)目標(biāo)---結(jié)果---反饋# Fibonacci.action int32 order # Goal --- int32[] sequence # Result --- int32[] partial_sequence # Feedback自定義接口# my_interfaces/msg/Student.msg string name int32 age # CMakeLists.txt rosidl_generate_interfaces(${PROJECT_NAME} msg/Student.msg srv/AddTwoInts.srv )ros2 interface show就能看到。11. 動作完整行為的流程管理Action Service(可取消) Topic(周期反饋)。處理長時間任務(wù)導(dǎo)航、機(jī)械臂執(zhí)行。Client --Goal-- Server (開始去(2,2)) --Feedback-- (已走30%) --Feedback-- (已走80%) --Cancel-- (用戶中途取消) --Result-- (成功/失敗/取消)命令行ros2 action list ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose {pose: {pose: {position: {x: 2, y: 2}}}}Nav2 導(dǎo)航就是一個 Action ServerRViz2 點(diǎn)目標(biāo)點(diǎn)就是 Client。12. 參數(shù)機(jī)器人系統(tǒng)的全局字典參數(shù) Parameter 是節(jié)點(diǎn)的可動態(tài)配置變量無需重啟。self.declare_parameter(kp, 1.0) kp self.get_parameter(kp).value # 支持回調(diào)參數(shù)一改控制立刻生效 self.add_on_set_parameters_callback(self.param_callback)命令行ros2 param list ros2 param get /turtlesim background_r ros2 param set /turtlesim background_r 200 ros2 param dump /turtlesim params.yaml ros2 param load /turtlesim params.yaml應(yīng)用現(xiàn)場調(diào)小車最大速度、PID增益、相機(jī)曝光直接param set不用重新編譯燒錄。13. 分布式通信多計算平臺的任務(wù)分配ROS2 原生支持分布式機(jī)器人端Jetson Orin 跑激光相機(jī)驅(qū)動PC端高性能主機(jī)跑建圖、Nav2導(dǎo)航只要同一局域網(wǎng)同一ROS_DOMAIN_ID同一WiFi節(jié)點(diǎn)自動發(fā)現(xiàn)話題互通。# 機(jī)器人端192.168.1.10 export ROS_DOMAIN_ID42 ros2 run rplidar_ros rplidar_composition # PC端192.168.1.20 export ROS_DOMAIN_ID42 ros2 topic echo /scan # 就能收到機(jī)器人雷達(dá) rviz2 # 就能可視化比 ROS1 需要ROS_MASTER_URI配置簡單太多。進(jìn)階DDS支持跨廣域網(wǎng)、SROS2加密適合車隊。14. DDS機(jī)器人的神經(jīng)網(wǎng)絡(luò)DDS 是 ROS2 的底座工業(yè)級標(biāo)準(zhǔn)。Domain邏輯隔離域ROS_DOMAIN_ID就是 DDS Domain ID。Participant每個進(jìn)程一個 Participant。Topic DataWriter/DataReader底層真正收發(fā)。QoS策略Reliability: RELIABLE/BEST_EFFORTDurability: VOLATILE/TRANSIENT_LOCAL新訂閱者能否收到歷史History: KEEP_LAST depthRMWROS中間件接口層抽象不同廠商 DDS。切換實(shí)現(xiàn)# 默認(rèn) Fast DDS export RMW_IMPLEMENTATIONrmw_fastrtps_cpp # 換 CycloneDDS更輕量 sudo apt install ros-humble-rmw-cyclonedds-cpp export RMW_IMPLEMENTATIONrmw_cyclonedds_cpp理解 DDS調(diào)透 QoS是解決圖像卡頓、控制丟包的關(guān)鍵。15. Launch多節(jié)點(diǎn)啟動與配置腳本機(jī)器人啟動要啟動 10 節(jié)點(diǎn)雷達(dá)、IMU、底盤、TF、RViz...一個個ros2 run會累死Launch 一鍵搞定。ROS2 Launch 是 Python 腳本比 ROS1 XML 強(qiáng)大百倍支持 if 判斷、變量。# my_robot/launch/robot.launch.py from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node(packagerplidar_ros, executablerplidar_composition, parameters[{serial_port: /dev/ttyUSB0, frame_id: laser}]), Node(packagemy_robot, executablebase_driver, outputscreen), Node(packagetf2_ros, executablestatic_transform_publisher, arguments[0,0,0.2,0,0,0,base_link,laser]), Node(packagerviz2, executablerviz2, arguments[-d, my.rviz]) ])運(yùn)行ros2 launch my_robot robot.launch.py帶參ros2 launch my_robot robot.launch.py use_sim_time:true16. TF機(jī)器人坐標(biāo)系管理器機(jī)器人有無數(shù)坐標(biāo)系map地圖、odom里程計、base_link底盤、laser雷達(dá)、camera_link。TF就是管理它們關(guān)系的時變樹。TF 是話題/tf和/tf_static每個變換都是父坐標(biāo) - 子坐標(biāo) 時間戳tf2可查詢?nèi)我鈺r刻任意兩坐標(biāo)系的關(guān)系。ros2 run tf2_ros static_transform_publisher 0 0 0.2 0 0 0 base_link laser ros2 run tf2_tools view_frames # 生成PDF的TF樹 rviz2 - Add - TF 看坐標(biāo)軸代碼監(jiān)聽from tf2_ros import Buffer, TransformListener self.tf_buffer Buffer() self.tf_listener TransformListener(self.tf_buffer, self) trans self.tf_buffer.lookup_transform(map, base_link, rclpy.time.Time())沒有 TF導(dǎo)航、建圖全崩。必須保證TF樹連通無環(huán)。17. URDFROS機(jī)器人建模方法URDF (Unified Robot Description Format) 用 XML 描述機(jī)器人連桿 Link 關(guān)節(jié) Joint。robot namemy_car link namebase_link visualgeometrycylinder length0.1 radius0.2//geometry/visual /link link nameleft_wheel/ joint nameleft_wheel_joint typecontinuous parent linkbase_link/child linkleft_wheel/ origin xyz0 0.2 0/axis xyz0 1 0/ /joint /robotlink外觀visual、碰撞collision、慣性inertialjointfixed固定、continuous連續(xù)旋轉(zhuǎn)輪子、revolute有限轉(zhuǎn)角舵機(jī)、prismatic滑軌XacroURDF 宏支持變量、計算復(fù)雜機(jī)器人必用。sudo apt install ros-humble-joint-state-publisher-gui ros2 launch urdf_tutorial display.launch.py model:my_car.urdf # 在RViz里拖動滑桿看關(guān)節(jié)運(yùn)動URDF TF 機(jī)器人完整數(shù)字孿生。18. Gazebo三維物理仿真平臺Gazebo 是獨(dú)立的物理引擎支持重力、摩擦、碰撞、光照、傳感器仿真雷達(dá)噪聲、相機(jī)畸變。Gazebo 負(fù)責(zé)讓機(jī)器人動起來有物理Rviz 負(fù)責(zé)把數(shù)據(jù)畫出來無物理經(jīng)典流程# 安裝 sudo apt install ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros2-control # 啟動仿真 ros2 launch my_robot gazebo.launch.py # 話題橋接Gazebo的 /clock, /scan 會自動轉(zhuǎn) ROS2 話題在URDF里加gazebo標(biāo)簽和插件如libgazebo_ros_diff_drive.so差速驅(qū)動Gazebo就開始仿真輪子轉(zhuǎn)。價值無需真機(jī)在仿真中測試 Nav2 算法撞爛了也不心疼。19. Rviz三維可視化顯示平臺Rviz2 不模擬只訂閱話題并可視化。常用 DisplayRobotModel顯示URDF模型LaserScan雷達(dá)點(diǎn)云PointCloud23D點(diǎn)云Map占據(jù)柵格地圖Path導(dǎo)航路徑TF坐標(biāo)系Image相機(jī)圖像rviz2 # Add - By topic - 勾選 /scan # 配置文件保存 .rviz下次一鍵加載配合 Nav2Rviz里可點(diǎn)2D Pose Estimate初始位姿2D Goal Pose導(dǎo)航目標(biāo)。20. RQT模塊化可視化工具RQT 是基于 Qt 的插件容器集成幾十個小工具rqt # 打開插件選擇器 rqt_graph # 節(jié)點(diǎn)圖最常用看話題連接上一章用過 rqt_plot # 畫曲線/odom速度波形 rqt_console # 日志過濾 rqt_reconfigure # 動態(tài)調(diào)參圖形化前端 rqt_bag # 播放 bag 包安裝sudo apt install ~nros-humble-rqt*調(diào)試三件套rqt_graphros2 topic echorviz2。21. ROS2應(yīng)用與進(jìn)階攻略到此你已學(xué)完 ROS2 核心。下一步應(yīng)用層導(dǎo)航 Nav2sudo apt install ros-humble-navigation2SLAM建圖slam_toolbox定位amcl一鍵導(dǎo)航。機(jī)械臂 MoveIt2抓取規(guī)劃。ros2_control統(tǒng)一硬件接口你的 STM32 底盤寫一個hardware_interface就能被 Nav2 調(diào)度。micro-ROS在 STM32F4/H7 上直接跑 ROS2 節(jié)點(diǎn)不再經(jīng)串口轉(zhuǎn)發(fā)訂閱/cmd_vel成為真·ROS2節(jié)點(diǎn)。進(jìn)階路線圖動手造小車差速底盤 RPLIDAR A1 IMU跑通URDF - Gazebo - Nav2全流程。視覺v4l2_cameraOpenCVYOLO話題/image_raw→ 目標(biāo)檢測。多機(jī)協(xié)同兩臺機(jī)器人同ROS_DOMAIN_IDNav2支持多機(jī)編隊。云邊端ROS2 MQTT橋機(jī)器人數(shù)據(jù)上阿里云參考你之前 MQTT 篇。最終建議先把turtlesim 話題服務(wù)玩熟再用URDF Rviz搭自己模型再進(jìn)Gazebo 仿真 Nav2 導(dǎo)航最后接真車總結(jié)本篇嚴(yán)格目錄獨(dú)立介紹了 ROS2 的命令行、環(huán)境、工程、五大通信、DDS、Launch、TF、URDF、Gazebo、Rviz、RQT。工具包Humble官方文檔 魚香ROS《ROS2機(jī)器人開發(fā)從入門到實(shí)踐》 古月居教程