大多数ROS 2教程给你一个能运行的机器人代码然后就结束了。你复制代码它能运行但你不知道为什么。当出现问题时——比如错误的坐标系名称、缺失的主题、导致Gazebo崩溃的惯性值——你完全束手无策。这个系列不同。每个概念都从第一性原理解释。每行代码都有其理由。到最后你不仅会有一个能运行的AMR——你还会足够深入地理解它从而能够构建一个真正的机器人。你将构建什么一个模拟的差速驱动AMR能够映射环境、定位自身并自主地从A点导航到B点同时避开障碍物。所有内容都在ROS 2 Humble和Gazebo Classic上运行基于Ubuntu 22.04。适合谁了解Python和Linux基础知识想要构建严肃的机器人系统而不仅仅是跟着教程走的工程师。1、ROS 2到底是什么大多数介绍说ROS 2是中间件然后就结束了。让我们更精确一些。你正在构建一个AMR。在任何给定时刻你有一个激光雷达以10Hz的频率产生360个距离读数一个电机控制器以50Hz的频率等待速度指令一个SLAM算法消费激光雷达数据并生成地图一个导航算法消费地图并生成速度指令这些都是完全独立的进程。它们可能用不同的语言编写。在真实的生产机器人上它们可能在不同的计算机上运行。它们需要快速、可靠地通信而无需了解彼此的内部实现。ROS 2通过基于DDS数据分发服务的发布-订阅通信模型来解决这个问题——这是军事和自动驾驶系统中使用的相同协议而不是业余爱好者框架。关键的架构洞察节点是完全解耦的。激光雷达驱动程序不知道SLAM的存在。SLAM不知道Nav2的存在。如果SLAM崩溃激光雷达继续运行。如果你想更换SLAM算法只需替换一个节点——其他什么都不变。这正是生产级AMRAmazon Kiva、Boston Dynamics Spot、医院配送机器人这样构建的原因。2、安装先决条件Ubuntu 22.04至少4GB内存20GB磁盘空间。# 设置locale 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 # 添加ROS 2仓库 sudo apt install software-properties-common curl 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 # 安装ROS 2 Humble Desktop Full sudo apt update sudo apt install ros-humble-desktop-full # 安装构建工具 sudo apt install python3-colcon-common-extensions \ python3-rosdep \ ros-humble-gazebo-ros-pkgs # 在每个终端中source——添加到~/.bashrc echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc验证安装ros2 --help # 应该打印完整的ros2 CLI帮助——如果这能工作ROS 2就安装好了3、工作空间——实际发生了什么mkdir -p ~/amr_ws/src cd ~/amr_ws colcon build source install/setup.bash echo source ~/amr_ws/install/setup.bash ~/.bashrc大多数人运行colcon build然后等待。以下是它实际做的~/amr_ws/ ├── src/ ← 你在这里写代码 ├── build/ ← colcon的工作目录永远不要碰这个 ├── install/ ← 真正的输出——ROS 2从这里读取而不是src/ └── log/ ← 构建日志——构建失败时查看这里添加到~/.bashrc意味着每个新终端自动看到你的工作空间。4、创建你的第一个包cd ~/amr_ws/src ros2 pkg create --build-type ament_python amr_basics \ --dependencies rclpy std_msgs geometry_msgs这会创建包的脚手架。有两个文件定义每个ROS 2包package.xml—— 声明这个包需要什么来构建和运行?xml version1.0? package format3 nameamr_basics/name version0.0.1/version descriptionAMR基础——发布者、订阅者示例/description maintainer emailyouexample.comYour Name/maintainer licenseApache-2.0/license buildtool_dependament_python/buildtool_depend dependrclpy/depend dependgeometry_msgs/depend export build_typeament_python/build_type /export /packagesetup.py—— 将你的Python脚本注册为可执行的ROS 2节点from setuptools import setup package_name amr_basics setup( namepackage_name, version0.0.1, packages[package_name], install_requires[setuptools], entry_points{ console_scripts: [ # command_name package.module:function velocity_publisher amr_basics.velocity_publisher:main, velocity_subscriber amr_basics.velocity_subscriber:main, ], }, )5、节点——计算单元ROS 2节点是一个带有名称的进程运行在ROS 2网络中。这里是一个发布者节点发送速度指令——你的整个导航栈将使用的相同消息类型amr_basics/amr_basics/velocity_publisher.pyimport rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class VelocityPublisher(Node): def __init__(self): # 在DDS网络中将此节点注册为velocity_publisher # 其他节点现在可以通过ros2 node list发现它 super().__init__(velocity_publisher) # 在/cmd_vel主题上创建发布者 # Twist是移动机器人的标准速度消息类型 # 10 队列深度如果订阅者慢保留最后10条消息 self.publisher_ self.create_publisher(Twist, cmd_vel, 10) # 定时器每0.5秒调用send_velocity # ROS 2在内部管理这个——不是Python线程 self.timer self.create_timer(0.5, self.send_velocity) self.get_logger().info(Velocity publisher started) def send_velocity(self): msg Twist() # 对于差速驱动只有linear.x和angular.z重要 # linear.x 前进速度单位m/s正值前进 # angular.z 旋转速度单位rad/s正值左转 msg.linear.x 0.2 msg.angular.z 0.1 self.publisher_.publish(msg) self.get_logger().info( fPublishing: linear{msg.linear.x}, angular{msg.angular.z} ) def main(argsNone): rclpy.init(argsargs) # 初始化DDS通信 node VelocityPublisher() rclpy.spin(node) # 事件循环——在这里阻塞触发回调 node.destroy_node() rclpy.shutdown() if __name__ __main__: main()amr_basics/amr_basics/velocity_subscriber.pyimport rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class VelocitySubscriber(Node): def __init__(self): super().__init__(velocity_subscriber) self.subscription self.create_subscription( Twist, cmd_vel, self.velocity_callback, 10 ) def velocity_callback(self, msg: Twist): # 差速驱动运动学 # 将线速度角速度转换为单个轮速 # L 0.25m 轮距的一半两轮之间的距离/2 L 0.25 left_wheel msg.linear.x - (msg.angular.z * L) right_wheel msg.linear.x (msg.angular.z * L) self.get_logger().info( fLeft wheel: {left_wheel:.3f} m/s | fRight wheel: {right_wheel:.3f} m/s ) def main(argsNone): rclpy.init(argsargs) node VelocitySubscriber() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ __main__: main()构建并运行cd ~/amr_ws colcon build --packages-select amr_basics source install/setup.bash # 终端1 ros2 run amr_basics velocity_publisher # 终端2 ros2 run amr_basics velocity_subscriber # 终端3——检查实时系统 ros2 topic list # 所有活动主题 ros2 topic echo /cmd_vel # 实时打印消息 ros2 node list # 所有运行中的节点 ros2 node info /velocity_publisher # 这个节点的主题 rqt_graph # 可视化节点图——显示谁在跟谁通信终端2的预期输出[INFO] Left wheel: 0.175 m/s | Right wheel: 0.225 m/s [INFO] Left wheel: 0.175 m/s | Right wheel: 0.225 m/s6、理解rclpy.spin()——最容易被误解的一行代码rclpy.spin(node)这是一个事件循环——概念上等同于JavaScript的事件循环或Python的asyncio。它阻塞并等待定时器回调—— 你的0.5秒定时器触发send_velocity被调用订阅回调——/cmd_vel上收到消息velocity_callback被调用服务请求—— 客户端调用你的服务你的处理程序被调用没有spin()你的节点启动并立即退出。没有回调会被触发。这是ROS 2初学者从脚本背景过来时最常见的错误。7、TF2坐标系统——一切的基础这个概念在这里引入是因为它支撑了后续每个阶段。现在就把这个心理模型弄对以后调试一切都会更快。你机器人的每个物理部分都生活在一个命名的坐标系中。TF2跟踪每个坐标系如何随时间与其他每个坐标系相关联。你的完整AMR将有这个坐标系层次结构map ← 全局坐标系由SLAM创建 └── odom ← 连续里程计随时间漂移 └── base_footprint ← 机器人地面投影 └── base_link ← 机器人本体中心 ├── left_wheel ├── right_wheel ├── lidar_link ├── imu_link └── camera_link └── camera_optical_frame为什么odom和map是两个独立的坐标系——这让每个初学者都困惑odom是平滑的但会累积误差。轮子打滑、编码器噪声和不平的地面意味着行驶50米后基于里程计的位置可能比真实位置偏差1-2米。map是全局准确的但可能不连续跳跃。当SLAM识别到之前访问过的位置回环闭合时它会校正整个地图——map→odom变换更新以吸收漂移校正。Nav2使用odom进行瞬时速度控制平滑无抖动和map进行全局路径规划准确已校正。两个坐标系都是必需的。单独任何一个都不够。8、常见错误和解决方法“构建后找不到包”# 你忘记在构建后source source ~/amr_ws/install/setup.bash # 然后重试你的ros2命令节点运行但订阅者收不到任何东西# 检查主题名称完全匹配——拼写错误是隐形的 ros2 topic list ros2 topic echo /cmd_vel # 如果/cmd_vel没有出现发布者没有运行或有不同的主题名称colcon build失败显示找不到包# 你缺少依赖——安装它 sudo apt install ros-humble-package-name # 或者在package.xml中声明它并运行 rosdep install --from-paths src --ignore-src -r -yrqt_graph显示节点但没有连接# 如果没有消息流动这是正常的 # 先开始发布然后打开rqt_graph # 在rqt_graph中点击刷新按钮圆形箭头9、结束语此时你有一个正常工作的ROS 2 Humble安装一个具有正确构建系统的结构化工作空间一个交换速度指令的发布者和订阅者节点关于节点、主题和TF坐标系如何关联的清晰心理模型这是每个后续部分建立的基础。订阅者中的差速驱动运动学left v - ω*Lright v ω*L是你的ros2_control硬件接口将在第5部分实现的相同数学。在第2部分中我们从零开始建模物理机器人底盘、轮子、脚轮和所有传感器作为一个URDF文件。你将理解惯性张量、关节类型、坐标系约定以及为什么在模型中把这些弄对可以防止后来在模拟中花费数小时调试。原文链接ROS 2从零构建自主移动机器人 (1) - 汇智网