ROS2入门:Ubuntu环境部署与Python/C++双语言实战
新手入门机器人开发最难的不是写代码而是把环境铺好。很多人在 Ubuntu 上装完 ROS2启动示例节点时发现找不到 package或者 VSCode 里无法自动补全 ROS2 的接口更别提用 Python 和 C 分别跑通发布订阅模型。网上教程虽然多但要么只讲一种语言要么跳过环境细节照着做总是卡壳。本文整理了一套面向零基础同学的 ROS2 环境部署与开发环境配置方案涵盖 Ubuntu 系统准备、ROS2 安装、VSCode 插件配置、Python 与 C 双语言 HelloWorld 实战以及常见报错排查。跟着步骤走你可以在自己的电脑上从零搭建一套可用的机器人开发环境。需要说明的是ROS2 已有多个发行版不同 Ubuntu 版本对应的 ROS2 版本不同。本文会先讲解版本选型原则再以最常见的组合为例演示完整流程。如果你使用的是其他版本配置思路是相通的只需把版本号替换为对应发行版即可。1. ROS2 环境部署前的概念梳理1.1 为什么是 ROS2 而不是 ROS1ROSRobot Operating System机器人操作系统并不是传统意义上的操作系统而是一套分布式通信框架它提供了进程之间的消息传递、设备驱动、功能包管理和可视化工具帮助开发者把传感器、算法、执行器组合成一个完整的机器人系统。ROS1 诞生较早在设计上存在一些明显局限通信依赖单个 Master 节点Master 挂掉则整个系统瘫痪实时性支持不足跨机器通信配置复杂Python 2 到 Python 3 的过渡也带来了兼容性问题。ROS2 重新设计了通信架构核心变化是对比维度ROS1ROS2通信机制基于 TCPROS/UDPROS依赖 Master基于 DDS数据分发服务去中心化实时性支持有限原生支持实时通信Python 版本依赖 Python 2早期原生支持 Python 3多机通信配置繁琐DDS 自动发现跨机部署方便安全性较弱支持 DDS 安全机制生命周期停止维护ROS1 Noetic 是最终版持续更新因此对于新手来说直接学习 ROS2 是更合理的选择。1.2 ROS2 的核心通信模型在开始写 HelloWorld 之前先理解 ROS2 中最基础的几个概念。节点Node一个可执行程序负责完成某一项具体任务例如读取激光雷达数据、控制电机、运行导航算法。话题Topic节点之间异步通信的通道。一个节点发布消息到话题另一个节点订阅该话题接收消息。适合传感器数据等持续更新的数据流。服务Service节点之间同步通信的方式。客户端发送请求服务端返回响应适合一次性调用例如拍照、开关灯。动作Action适合耗时较长的任务例如导航到目标点可以反馈进度并支持取消。工作空间Workspace通常指包含多个 ROS2 功能包的目录结构使用src目录存放源码通过colcon build编译。HelloWorld 通常实现的是发布者Publisher和订阅者Subscriber两个节点通过话题完成双向通信这也是理解 ROS2 通信机制最直观的入门示例。2. 环境准备与版本说明2.1 硬件要求运行 ROS2 对电脑要求不算高但虚拟机和实体机体验差异较大。建议配置如下处理器双核及以上推荐四核内存8GB 及以上硬盘至少 30GB 空闲空间操作系统Ubuntu 22.04 或 24.04推荐 22.04 长期支持版如果使用虚拟机建议给虚拟机分配至少 4GB 内存和 2 个 CPU 核心否则编译和运行 turtlesim 等示例会明显卡顿。若条件允许优先使用双系统或实体 Linux 主机。2.2 确认 Ubuntu 版本与 ROS2 发行版ROS2 的每个发行版都有对应的 Ubuntu 版本。截至本文写作时常见的对应关系如下Ubuntu 版本对应 ROS2 发行版支持状态Ubuntu 22.04 LTSHumble Hawksbill长期支持Ubuntu 24.04 LTSJazzy Jalisco长期支持Ubuntu 20.04 LTSFoxy Fitzroy已停止维护不建议新项目使用新手推荐使用 Ubuntu 22.04 搭配 ROS2 Humble原因是社区资料最多第三方功能包兼容性最好。若你已经在使用 Ubuntu 24.04可以安装 Jazzy步骤类似只需把仓库地址和版本代号替换为对应名称。2.3 安装前的系统设置打开终端依次执行以下操作。更新软件源sudo apt update sudo apt upgrade -y安装基础工具sudo apt install -y curl wget git build-essential cmake python3-pip设置 UTF-8 编码环境避免后续 ROS2 文本输出乱码sudo apt install -y locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALLen_US.UTF-8 LANGen_US.UTF-8 export LANGen_US.UTF-83. ROS2 完整环境部署3.1 方式一官方仓库安装ROS2 提供了官方 apt 仓库安装步骤明确适合希望了解底层细节的开发者。步骤 1添加 ROS2 软件源sudo apt install -y software-properties-common sudo add-apt-repository universe sudo apt update步骤 2添加 ROS2 GPG 密钥以 Ubuntu 22.04 安装 Humble 为例sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg如果下载慢或失败可以手动下载 ros.key 文件后放到对应目录。步骤 3添加 ROS2 仓库到 apt 源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步骤 4安装 ROS2 桌面版桌面版包含 ROS 基础功能、可视化工具、示例程序和模拟器适合学习和开发sudo apt update sudo apt upgrade -y sudo apt install -y ros-humble-desktop如果只需要核心通信功能可以安装精简版sudo apt install -y ros-humble-ros-base步骤 5设置环境变量echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc步骤 6安装构建工具ROS2 使用colcon编译工作空间需要单独安装sudo apt install -y python3-colcon-common-extensions同时建议安装rosdep用于自动安装依赖sudo apt install -y python3-rosdep sudo rosdep init rosdep update3.2 方式二使用社区一键安装工具国内网络环境下官方源可能较慢。社区提供了一键安装脚本可以自动检测系统版本并安装对应 ROS2 发行版适合希望快速搭建环境的初学者。在终端执行wget http://fishros.com/install -O fishros . fishros运行后按提示选择 ROS2 安装、选择对应发行版即可。脚本会自动配置镜像源和环境变量。需要说明的是社区脚本简化了安装流程节省时间但建议成功安装后仍然运行一次ros2 doctor检查环境完整性避免隐藏问题。3.3 验证安装是否成功安装完成后打开新终端执行ros2 --help如果输出 ROS2 命令帮助信息说明安装成功。再运行一个更直观的测试ros2 run turtlesim turtlesim_node如果弹出蓝色窗口的海龟模拟器说明 ROS2 核心功能运行正常。此时可以再打开一个新终端运行ros2 run turtlesim turtle_teleop_key然后用方向键控制乌龟移动。看到乌龟在窗口中响应按键说明节点间的通信机制已经正常工作这是一个比 HelloWorld 更直观的“居然跑起来了”验证。3.4 卸载与重装如果安装过程出错需要清理重来可以执行sudo apt remove ros-humble-* sudo apt autoremove删除仓库源文件sudo rm /etc/apt/sources.list.d/ros2.list sudo rm /usr/share/keyrings/ros-archive-keyring.gpg然后重新从第 3.1 节开始。4. VSCode 开发环境配置4.1 安装 VSCodeROS2 开发离不开一个好用的编辑器。VSCode 免费、插件丰富、对 Python 和 C 支持完善是 ROS2 开发的主流选择。方式一通过官方 deb 包安装访问 VSCode 官网下载.deb安装包然后在终端执行sudo dpkg -i code_*.deb sudo apt install -f -y方式二通过 Snap 安装sudo snap install code --classic安装完成后在终端输入code即可启动。4.2 中文界面配置第一次打开 VSCode 是英文界面。点击左侧扩展图标搜索Chinese (Simplified)安装第一个结果然后按CtrlShiftP打开命令面板输入Configure Display Language选择中文(简体)重启 VSCode 即可。4.3 必装插件清单以下插件建议全部安装覆盖 ROS2 开发全流程插件名称作用ROS提供 ROS2 消息类型识别、launch 文件高亮、调试配置等能力C/C微软官方 C 扩展提供智能提示、调试、代码跳转PythonPython 语言支持提供智能提示和调试CMakeCMake 语法高亮和辅助工具CMake Tools简化 CMake 项目构建流程Msg Language SupportROS2.msg消息文件语法支持URDFURDF 机器人模型文件语法高亮Markdown All in One写文档、写博客时使用GitLensGit 代码冲突、提交记录查看安装步骤点击左侧扩展图标在搜索框输入插件名点击 Install。4.4 配置 Python 开发环境打开 VSCode 设置搜索python.autoComplete.extraPaths把 ROS2 的 Python 模块路径加入自动补全列表{ python.autoComplete.extraPaths: [ /opt/ros/humble/lib/python3.10/site-packages ], python.analysis.extraPaths: [ /opt/ros/humble/lib/python3.10/site-packages ] }如果你使用的是 Ubuntu 24.04 和 Jazzy路径类似需要把humble替换为jazzyPython 版本号按实际目录调整。4.5 配置 C/C 开发环境ROS2 的 C 头文件位于/opt/ros/humble/include。为了让 VSCode 正确识别头文件路径需要在项目根目录创建.vscode/c_cpp_properties.json{ configurations: [ { name: Linux, includePath: [ ${workspaceFolder}/**, /opt/ros/humble/include/** ], defines: [], compilerPath: /usr/bin/g, cStandard: c17, cppStandard: gnu17, intelliSenseMode: linux-gcc-x64 } ], version: 4 }创建 ROS2 工作空间后建议把 workspace 文件夹直接用 VSCode 打开这样插件能正确加载整个项目的编译上下文。4.6 配置 launch.json 调试参数ROS2 Python 节点可以用 VSCode 调试。在项目根目录创建.vscode/launch.json{ version: 0.2.0, configurations: [ { name: Python: ROS2 Node, type: python, request: launch, program: ${file}, console: integratedTerminal, cwd: ${workspaceFolder}, env: { PYTHONPATH: /opt/ros/humble/lib/python3.10/site-packages:${env:PYTHONPATH} } } ] }调试 C 节点时需要先使用colcon build编译再在 launch.json 中指定可执行文件路径。5. 双语言 HelloWorld 实战接下来我们分别用 Python 和 C 实现 ROS2 的发布者与订阅者示例。先创建共享的工作空间再在src下分别创建两个功能包。5.1 创建 ROS2 工作空间mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src5.2 使用 Python 编写发布者与订阅者创建 Python 功能包cd ~/ros2_ws/src ros2 pkg create py_hello_world --build-type ament_python --dependencies rclpy std_msgs参数说明py_hello_world功能包名称--build-type ament_python指定 Python 构建类型--dependencies rclpy std_msgs声明依赖rclpy 是 ROS2 Python 客户端库std_msgs 提供标准消息类型命令执行后会在src下生成py_hello_world目录结构如下py_hello_world/ ├── package.xml ├── py_hello_world/ │ └── __init__.py ├── resource ├── setup.cfg ├── setup.py └── test编写发布者节点在py_hello_world/py_hello_world/目录下新建talker.py# 文件路径src/py_hello_world/py_hello_world/talker.py import rclpy from rclpy.node import Node from std_msgs.msg import String class TalkerNode(Node): def __init__(self): super().__init__(talker_node) self.publisher self.create_publisher(String, hello_topic, 10) self.timer self.create_timer(1.0, self.timer_callback) self.count 0 def timer_callback(self): msg String() msg.data fHello ROS2 from Python, count: {self.count} self.publisher.publish(msg) self.get_logger().info(fPublishing: {msg.data}) self.count 1 def main(argsNone): rclpy.init(argsargs) node TalkerNode() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ __main__: main()代码说明create_publisher(String, hello_topic, 10)创建了一个发布者消息类型为String话题名为hello_topic队列长度 10。create_timer(1.0, self.timer_callback)创建了一个周期为 1 秒的定时器每秒触发一次回调函数。rclpy.spin(node)让节点持续运行不断处理事件。编写订阅者节点在py_hello_world/py_hello_world/目录下新建listener.py# 文件路径src/py_hello_world/py_hello_world/listener.py import rclpy from rclpy.node import Node from std_msgs.msg import String class ListenerNode(Node): def __init__(self): super().__init__(listener_node) self.subscription self.create_subscription( String, hello_topic, self.listener_callback, 10 ) def listener_callback(self, msg): self.get_logger().info(fReceived: {msg.data}) def main(argsNone): rclpy.init(argsargs) node ListenerNode() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ __main__: main()注册节点入口编辑setup.py在entry_points中添加两个控制台脚本entry_points{ console_scripts: [ talker py_hello_world.talker:main, listener py_hello_world.listener:main, ], },保存后使用colcon build编译并运行见 5.4 节。5.3 使用 C 编写发布者与订阅者创建 C 功能包cd ~/ros2_ws/src ros2 pkg create cpp_hello_world --build-type ament_cmake --dependencies rclcpp std_msgs命令执行后生成cpp_hello_world目录。编写发布者节点在src/cpp_hello_world/src/目录下新建talker.cpp// 文件路径src/cpp_hello_world/src/talker.cpp #include chrono #include functional #include memory #include string #include rclcpp/rclcpp.hpp #include std_msgs/msg/string.hpp using namespace std::chrono_literals; class TalkerNode : public rclcpp::Node { public: TalkerNode() : Node(cpp_talker_node), count_(0) { publisher_ this-create_publisherstd_msgs::msg::String(hello_topic, 10); timer_ this-create_wall_timer( 1s, std::bind(TalkerNode::timer_callback, this)); } private: void timer_callback() { auto message std_msgs::msg::String(); message.data Hello ROS2 from C, count: std::to_string(count_); publisher_-publish(message); RCLCPP_INFO(this-get_logger(), Publishing: %s, message.data.c_str()); count_; } rclcpp::Publisherstd_msgs::msg::String::SharedPtr publisher_; rclcpp::TimerBase::SharedPtr timer_; size_t count_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedTalkerNode()); rclcpp::shutdown(); return 0; }编写订阅者节点在src/cpp_hello_world/src/目录下新建listener.cpp// 文件路径src/cpp_hello_world/src/listener.cpp #include memory #include rclcpp/rclcpp.hpp #include std_msgs/msg/string.hpp class ListenerNode : public rclcpp::Node { public: ListenerNode() : Node(cpp_listener_node) { subscription_ this-create_subscriptionstd_msgs::msg::String( hello_topic, 10, std::bind(ListenerNode::listener_callback, this, std::placeholders::_1) ); } private: void listener_callback(const std_msgs::msg::String msg) const { RCLCPP_INFO(this-get_logger(), Received: %s, msg.data.c_str()); } rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr subscription_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedListenerNode()); rclcpp::shutdown(); return 0; }修改 CMakeLists.txt编辑src/cpp_hello_world/CMakeLists.txt在文件末尾添加add_executable(talker src/talker.cpp) ament_target_dependencies(talker rclcpp std_msgs) add_executable(listener src/listener.cpp) ament_target_dependencies(listener rclcpp std_msgs) install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME} )保存后编译运行见 5.4 节。5.4 编译和运行回到工作空间根目录执行编译cd ~/ros2_ws colcon build --symlink-install--symlink-install参数对 Python 开发很友好修改 Python 源码后无需重新编译即可生效。编译完成后需要重新 source 环境让当前终端识别新功能包source ~/ros2_ws/install/setup.bash运行 Python 发布者与订阅者打开终端 Asource /opt/ros/humble/setup.bash source ~/ros2_ws/install/setup.bash ros2 run py_hello_world talker打开终端 Bsource /opt/ros/humble/setup.bash source ~/ros2_ws/install/setup.bash ros2 run py_hello_world listener预期输出终端 A 每 1 秒输出一条发布日志[INFO] [tal~er_node]: Publishing: Hello ROS2 from Python, count: 0 [INFO] [tal~er_node]: Publishing: Hello ROS2 from Python, count: 1终端 B 同步输出[INFO] [lis~er_node]: Received: Hello ROS2 from Python, count: 0 [INFO] [lis~er_node]: Received: Hello ROS2 from Python, count: 1运行 C 发布者与订阅者保持上述终端不变新开终端 C 运行source /opt/ros/humble/setup.bash source ~/ros2_ws/install/setup.bash ros2 run cpp_hello_world talker可以看到终端 B 同时接收到 Python 节点和 C 节点发布的消息。这验证了 ROS2 跨语言通信能力Python 写的发布者可以被 C 写的订阅者接收反之亦然。5.5 使用 rqt_graph 查看节点关系运行rqt_graph可以直观看到节点和话题之间的关系source /opt/ros/humble/setup.bash rqt_graph在弹出的窗口中选择一个相对简单的视图可以看到talker_node和listener_node通过hello_topic话题连接在一起形成一个通信链路。6. 常见问题与排查指南问题现象常见原因解决思路ros2: command not found环境变量未 source执行source /opt/ros/humble/setup.bash并写入~/.bashrcPackage py_hello_world not found未 source install/setup.bash编译后执行source ~/ros2_ws/install/setup.bashcolcon build找不到命令colcon 未安装执行sudo apt install python3-colcon-common-extensionsPython 脚本无法 import rclpyPython 环境未配置确认使用系统 Python检查/opt/ros/humble/lib/python3.10/site-packages是否存在C 编译报找不到头文件VSCode includePath 未配置在c_cpp_properties.json中添加/opt/ros/humble/include/**虚拟机运行 turtlesim 非常卡虚拟机资源不足提高虚拟机内存和处理器核心数或改用双系统rosdep update超时网络问题检查网络或使用社区镜像源多个终端反复 source 很麻烦每次新开终端都需要手动 source把 source 命令写入~/.bashrc并确保只写一次6.1 详细排查环境变量未生效如果你运行ros2命令时提示找不到命令最可能是环境变量没有生效。检查~/.bashrc末尾是否有source /opt/ros/humble/setup.bash如果没有手动添加echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc注意每次重新打开终端后source命令会自动执行但如果你手动运行bash而不是登录 shell可能需要重新执行。6.2 详细排查找不到功能包编译完工作空间后经常出现Package xx not found的情况。这是因为colcon build生成的可执行文件位于install目录需要 source 该目录下的setup.bash才能被 ROS2 发现。每次新开终端执行source ~/ros2_ws/install/setup.bash也可以将其写入~/.bashrc但要注意不要重复添加多次否则环境变量会异常。6.3 详细排查colcon build 失败如果编译时提示缺少依赖可以先用rosdep安装依赖cd ~/ros2_ws rosdep install -i --from-path src --rosdistro humble -y如果是 Python 包依赖问题可以使用 pip 安装pip3 install package-name编译失败时重点看终端最后几行报错信息常见是 CMakeLists.txt 中忘记添加ament_target_dependencies或者 Python 包入口写错。6.4 详细排查VSCode 无法跳转定义很多同学用 VSCode 打开 ROS2 项目时发现 Ctrl点击无法跳转到 rclcpp 或 rclpy 的定义位置。对于 C确认.vscode/c_cpp_properties.json中includePath包含了/opt/ros/humble/include/**。配置后重启 VSCode再执行一次C/C: Reset IntelliSense Database。对于 Python确认设置中的python.analysis.extraPaths路径正确。如果仍然无法跳转建议打开 VSCode 的命令面板执行Python: Clear Cache and Reload Window。7. 最佳实践与工程建议7.1 工作空间命名与组织建议使用~/ros2_ws作为默认工作空间在src下按功能划分目录例如src/ ├── bringup # 启动文件 ├── navigation # 导航相关功能包 ├── perception # 感知相关功能包 ├── robot_bringup # 机器人启动脚本 └── utils # 通用工具包功能包命名建议采用小写下划线风格例如robot_base_bringup、lidar_processing。避免使用中文、空格和特殊字符。7.2 管理环境变量不要把大量source命令无脑堆在~/.bashrc中。建议只保留基础环境source /opt/ros/humble/setup.bash工作空间的source在需要时手动执行或者通过别名管理alias wssource ~/ros2_ws/install/setup.bash这样每次新开终端只需输入ws即可。7.3 使用 launch 文件当节点数量增多后不要手动开多个终端分别ros2 run。建议编写 launch 文件统一启动。在 Python 功能包中创建launch/hello_launch.py# 文件路径src/py_hello_world/launch/hello_launch.py from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( packagepy_hello_world, executabletalker, namepy_talker ), Node( packagepy_hello_world, executablelistener, namepy_listener ), ])然后在setup.py中注册 launch 文件data_files[ (share_directory, [package.xml]), (os.path.join(share_directory, launch), glob(os.path.join(launch, *launch.py))), ],运行ros2 launch py_hello_world hello_launch.pylaunch 文件是 ROS2 项目中非常重要的工程化手段后期做机器人整机启动时几乎每天都会用到。7.4 版本管理与回滚建议在创建功能包后立刻初始化 Git 仓库cd ~/ros2_ws git init git add . git commit -m init ros2 workspace对功能包进行重大修改前先提交一次防止破坏性变更无法恢复。7.5 使用模拟器加快开发ROS2 生态中有多个模拟器可以不用真实硬件就能验证算法Gazebo经典机器人仿真平台支持传感器仿真和物理引擎可搭配 ROS2 使用。Rviz2数据可视化工具可以显示机器人模型、传感器数据、路径规划结果。Webots跨平台机器人仿真软件支持 ROS2 接口。Isaac SimNVIDIA 出品的仿真平台适合视觉和强化学习场景但对显卡要求较高。对于新手来说先掌握 Gazebo 和 Rviz2 是最稳妥的选择。后续学习 SLAM、导航、机械臂控制时这些工具会成为主力。7.6 常见安全与权限提醒如果需要修改系统级配置务必在测试环境验证后再操作。使用sudo时谨慎确认命令内容不要盲目执行来历不明的脚本。在真实机器人上运行节点前先在模拟器中验证逻辑和安全性尤其是涉及电机驱动、底盘控制的代码。如果涉及kill、删除文件等操作先确认进程和文件路径避免误删工作空间源码。8. 总结与学习路线通过本文你已经完成了 ROS2 开发环境的完整搭建掌握了以下关键技能理解 ROS2 核心概念包括节点、话题、服务、动作和工作空间。根据 Ubuntu 版本选择正确的 ROS2 发行版并完成环境部署。安装并配置 VSCode为 Python 和 C 开发提供智能提示和调试支持。分别使用 Python 和 C 编写发布者与订阅者节点并成功实现跨语言通信。掌握常见的环境问题和报错排查方法。下一步建议按照以下顺序继续学习深入学习 ROS2 话题、服务、动作的完整用法尝试自定义消息类型。学习launch文件的高级用法统一启动多个节点。学习 URDF 机器人建模在 Rviz2 中显示自己的机器人模型。学习 Gazebo 仿真给机器人添加传感器和物理属性。挑战一个综合项目例如基于 ROS2 的差速底盘小车建图、定位、导航一气呵成。ROS2 的入门曲线确实比普通软件开发陡峭一些但环境一旦打通后续学习会顺利很多。本文涉及的代码量不大关键在于每一步的执行顺序和环境细节。建议打开终端跟着做一遍遇到报错不要急着改先看错误信息再对照第 6 节排查思路逐项确认。如果你在配置过程中遇到了本文没有覆盖到的问题欢迎留言说明你的 Ubuntu 版本、ROS2 发行版和具体报错信息我可以继续补充对应的排查方案。