新建会话
Ctrl
K
  • 网站
  • 文档
  • PPT
  • 表格
  • 深度研究
Kimi Code Kimi Claw
历史会话
  • 查看手机应用
  • 关于我们
    • 探索月之暗面
    • 开放平台
    • 社会招聘
    • 校园招聘
    • 用户协议
    • 隐私协议
  • Language
  • 用户反馈

ROS2 启动问题及解决方案

把代码修改成python版本: #include <chrono> #include <functional> #include <memory> #include <string> #include <rclcpp/rclcpp.hpp> #include <geometry_msgs/msg/twist.hpp> using namespace std::chrono_literals; class TurtlePublisher : public rclcpp::Node { public: TurtlePublisher() : Node("turtle_publisher") { // 创建 Publisher:发布到 "/turtle1/cmd_vel",队列大小10 publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10); // 创建定时器:每500ms调用一次回调函数 timer_ = this->create_wall_timer( 500ms, std::bind(&TurtlePublisher::timer_callback, this)); } private: void timer_callback() { auto message = geometry_msgs::msg::Twist(); message.linear.x = 2.0; // 前进速度 message.angular.z = 1.0; // 旋转速度 → 画圆! RCLCPP_INFO(this->get_logger(), "Publishing: linear=%.2f, angular=%.2f", message.linear.x, message.angular.z); publisher_->publish(message); } rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); // 初始化 ROS 2 rclcpp::spin(std::make_shared<TurtlePublisher>()); // 进入事件循环 rclcpp::shutdown(); // 关闭 return 0; }
下面给出与 C++ 版本 行为完全一致 的 Python 实现(ROS 2 rclpy)。
保存为 turtle_publisher.py,放到 src/ 目录即可,CMakeLists.txt 里把 turtle_publisher 也改成 Python 安装即可使用。
Python
复制
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist   # 等价于 C++ 的 geometry_msgs/msg/twist.hpp

class TurtlePublisher(Node):
    def __init__(self):
        super().__init__('turtle_publisher')          # 节点名

        # 创建 Publisher:话题 /turtle1/cmd_vel,队列 10
        self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)

        # 创建定时器:每 500 ms 触发一次 timer_callback
        self.timer = self.create_timer(0.5, self.timer_callback)

    def timer_callback(self):
        msg = Twist()
        msg.linear.x = 2.0   # 前进速度
        msg.angular.z = 1.0  # 旋转速度 → 画圆!

        self.get_logger().info(f'Publishing: linear={msg.linear.x:.2f}, angular={msg.angular.z:.2f}')
        self.publisher_.publish(msg)

def main(args=None):
    rclpy.init(args=args)                    # 初始化 ROS 2
    node = TurtlePublisher()
    rclpy.spin(node)                         # 保持节点运行,直到 Ctrl-C
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

与 C++ 关键差异对照(帮你快速迁移)

表格
C++Python
rclcpp::Noderclpy.node.Node
create_publisher<T>(...)self.create_publisher(T, ...)
create_wall_timer(500ms, ...)self.create_timer(0.5, ...) 单位秒
RCLCPP_INFO(...)self.get_logger().info(...)
rclcpp::spin(node)rclpy.spin(node)

使用步骤(Python 版)

  1. 给脚本加可执行权限
    bash
    复制
    chmod +x ~/ros2_ws/src/pub_sub/src/turtle_publisher.py
  2. 在 CMakeLists.txt 里再加一行(或把原来的 C++ 二进制换成这个)
    cmake
    复制
    install(PROGRAMS
      src/turtle_publisher.py
      DESTINATION lib/${PROJECT_NAME}
      RENAME turtle_publisher)
  3. 重新构建
    bash
    复制
    cd ~/ros2_ws
    colcon build --packages-select pub_sub
    source install/setup.bash
  4. 运行(launch 文件不用改,可执行名仍是 turtle_publisher)
    bash
    复制
    ros2 launch pub_sub turtle_circle.launch.py
现在你就拥有 纯 Python 实现 的“画圆”节点了!
把这个改成c++版本: #!/usr/bin/env python3 import rclpy from rclpy.node import Node from turtlesim.msg import Pose # 注意:turtlesim 自定义消息 class TurtleSubscriber(Node): def __init__(self): super().__init__('turtle_subscriber') # 创建 Subscriber:订阅 "/turtle1/pose" self.subscription = self.create_subscription( Pose, # 消息类型 '/turtle1/pose', # 话题名 self.listener_callback, # 回调函数 10) # 队列大小 self.subscription # 防止被 Python 垃圾回收 def listener_callback(self, msg): # msg 是 Pose 类型,包含 x, y, theta self.get_logger().info(f'Received pose: x={msg.x:.2f}, y={msg.y:.2f}, theta={msg.theta:.2f}') def main(args=None): rclpy.init(args=args) node = TurtleSubscriber() rclpy.spin(node) # 进入事件循环 node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
下面给出与 Python 版本行为完全一致的 C++ 实现(ROS 2 rclcpp)。
保存为 turtle_subscriber.cpp,放到 src/ 目录即可。
cpp
复制
#include <rclcpp/rclcpp.hpp>
#include <turtlesim/msg/pose.hpp>  // 对应 Python 的 turtlesim.msg.Pose

class TurtleSubscriber : public rclcpp::Node
{
public:
  TurtleSubscriber()
  : Node("turtle_subscriber")
  {
    // 创建 Subscriber:话题 /turtle1/pose,队列 10
    subscription_ = this->create_subscription<turtlesim::msg::Pose>(
      "/turtle1/pose",
      10,
      std::bind(&TurtleSubscriber::listener_callback, this, std::placeholders::_1));
  }

private:
  // 回调函数,参数是 Pose 消息的智能指针
  void listener_callback(const turtlesim::msg::Pose::SharedPtr msg)
  {
    RCLCPP_INFO(this->get_logger(),
                "Received pose: x=%.2f, y=%.2f, theta=%.2f",
                msg->x, msg->y, msg->theta);
  }

  rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);                                // 初始化 ROS 2
  rclcpp::spin(std::make_shared<TurtleSubscriber>());     // 进入事件循环
  rclcpp::shutdown();                                      // 清理
  return 0;
}

与 Python 关键差异对照

表格
PythonC++
create_subscription(Pose, ..., callback, 10)create_subscription<turtlesim::msg::Pose>(..., 绑定回调, 10)
self.get_logger().info(f"...")RCLCPP_INFO(this->get_logger(), "...")
rclpy.spin(node)rclcpp::spin(std::make_shared<Node>())

使用步骤(C++ 版)

  1. 把文件放进 ~/ros2_ws/src/pub_sub/src/turtle_subscriber.cpp
  2. 在 CMakeLists.txt 里加可执行节点与安装规则(已存在可跳过):
cmake
复制
# 找到 turtlesim 消息包
find_package(turtlesim REQUIRED)

add_executable(turtle_subscriber src/turtle_subscriber.cpp)
ament_target_dependencies(turtle_subscriber rclcpp turtlesim)

install(TARGETS turtle_subscriber
        DESTINATION lib/${PROJECT_NAME})
  1. 构建 & 运行
bash
复制
cd ~/ros2_ws
colcon build --packages-select pub_sub
source install/setup.bash
ros2 run pub_sub turtle_subscriber
现在你就拥有 纯 C++ 实现 的“打印海龟位姿”节点了!
我现在有四个源文件了,分别是publisher和subscriber的c++和python实现。cmakelists和构建运行会不会混乱?
不会乱,只要告诉 CMakeLists.txt “谁是谁、怎么装” 就行。
下面给你一份可直接套用的模板,把 4 个节点全部装进同一个 pub_sub 包,互不冲突。

1. 文件清单(放对位置即可)

plain
复制
pub_sub/
├── CMakeLists.txt
├── package.xml
├── launch/
│   └── turtle_circle.launch.py   # 可四选一或全开
├── src/
│   ├── turtle_publisher.cpp      # C++ 画圆
│   ├── turtle_subscriber.cpp     # C++ 打印位姿
│   ├── turtle_publisher.py       # Python 画圆
│   └── turtle_subscriber.py      # Python 打印位姿

2. CMakeLists.txt(一份管4个节点)

cmake
复制
cmake_minimum_required(VERSION 3.8)
project(pub_sub)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# ---------- 依赖 ----------
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)   # 为了 Pose 消息

# ---------- C++ 节点 ----------
add_executable(turtle_publisher_cpp   src/turtle_publisher.cpp)
add_executable(turtle_subscriber_cpp  src/turtle_subscriber.cpp)
ament_target_dependencies(turtle_publisher_cpp   rclcpp geometry_msgs)
ament_target_dependencies(turtle_subscriber_cpp  rclcpp turtlesim)

# ---------- Python 节点 ----------
find_package(ament_cmake_python REQUIRED)
install(PROGRAMS
  src/turtle_publisher.py
  src/turtle_subscriber.py
  DESTINATION lib/${PROJECT_NAME}
  RENAME turtle_publisher_py turtle_subscriber_py   # 去掉 .py 并区分名字
)

# ---------- 安装 ----------
install(TARGETS
  turtle_publisher_cpp
  turtle_subscriber_cpp
  DESTINATION lib/${PROJECT_NAME}
)

install(DIRECTORY launch/
  DESTINATION share/${PROJECT_NAME})

ament_package()

3. 可执行名对照表(ros2 run 时用)

表格
语言节点名运行命令
C++turtle_publisher_cppros2 run pub_sub turtle_publisher_cpp
C++turtle_subscriber_cppros2 run pub_sub turtle_subscriber_cpp
Pythonturtle_publisher_pyros2 run pub_sub turtle_publisher_py
Pythonturtle_subscriber_pyros2 run pub_sub turtle_subscriber_py

4. launch 文件示例(四选一,想全开就再写 4 个 Node)

Python
复制
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(package='turtlesim', executable='turtlesim_node', name='sim'),
        # 下面只启动 C++ 版,想换 Python就把 executable 换成 *_py
        Node(package='pub_sub', executable='turtle_publisher_cpp', name='cpp_pub'),
        Node(package='pub_sub', executable='turtle_subscriber_cpp', name='cpp_sub'),
    ])

5. 构建 & 运行(和原来完全一样)

bash
复制
cd ~/ros2_ws
colcon build --packages-select pub_sub
source install/setup.bash

# 任意挑一个组合
ros2 run pub_sub turtle_publisher_cpp      # C++ 画圆
ros2 run pub_sub turtle_subscriber_py      # Python 打印

6. 不会乱的核心原因

  • 可执行名不同:*_cpp vs *_py。
  • 安装路径相同都没关系,因为文件名唯一。
  • colcon 只是把它们分别拷到 install/pub_sub/lib/pub_sub/;ROS 2 按名字找,互不影响。

一句话总结

把名字区分好,一份 CMakeLists.txt 就能同时管 C++ 和 Python;构建、运行、launch 全部零冲突。
讲解图片中turtle这些量各自代表的含义
下面把截图里出现的 所有带 turtle 的量 逐个拆开说明:
(按“你在终端/代码里看到的名字 → 它到底是什么” 的顺序讲,保证一眼能对上号)

1. turtle_publisher_cpp

  • 本质:一个可执行文件的名字,编译后出现在
    install/pub_sub/lib/pub_sub/turtle_publisher_cpp
  • 来源:CMakeLists.txt 里
    add_executable(turtle_publisher_cpp src/turtle_publisher.cpp)
    把源文件 turtle_publisher.cpp 编译成二进制并取了这个名字。
  • 用途:ros2 run pub_sub turtle_publisher_cpp 时,系统就去上面路径找这个二进制并启动。

2. turtle_subscriber_cpp

  • 同上,只是源文件换成 turtle_subscriber.cpp,功能变成打印海龟位姿。

3. turtle_publisher_py / turtle_subscriber_py

  • 本质:同样是可执行文件的名字,但它们是Python 脚本。
  • 来源:CMakeLists.txt 里
    cmake
    复制
    install(PROGRAMS src/turtle_publisher.py src/turtle_subscriber.py
           DESTINATION lib/${PROJECT_NAME}
           RENAME turtle_publisher_py turtle_subscriber_py)
    把 .py 文件拷过去并顺手去掉后缀,再统一加 _py 后缀以便和 C++ 版区分。
  • 用途:ros2 run pub_sub turtle_publisher_py 时,系统去同名路径找脚本并用 python3 解释执行。

4. turtle_publisher(Node 名)

  • 出现位置:C++ 构造函数 / Python 父类初始化里
    cpp
    复制
    Node("turtle_publisher")
    Python
    复制
    super().__init__('turtle_publisher')
  • 本质:节点名(node name),不是文件名!
    它决定了:
    • ros2 node list 里看到的名字;
    • 日志里的 [turtle_publisher] 前缀;
    • 参数、命名空间、重映射时的默认前缀。
  • 注意:节点名可以相同,因为 ROS 2 会自动加数字后缀(如 turtle_publisher_1)来避免冲突。

5. turtle1(命名空间 / 话题前缀)

  • 出现位置:话题字符串 /turtle1/cmd_vel、/turtle1/pose
  • 本质:turtlesim 里默认的海龟命名空间。
    如果你再 spawn 一只海龟,话题会变成 /turtle2/cmd_vel 等。
  • 作用:让同一条 ROS 网络里可以有多只海龟,互不干扰。

6. /turtle1/cmd_vel、/turtle1/pose

  • 本质:话题(Topic)全名,不是代码变量,而是 ROS 2 网络里的“信道”。
  • 对应关系:
    • cmd_vel:你发速度 → 海龟动;
    • pose:海龟广播自己当前坐标 → 你订阅后就能打印。

7. 小结速查表(看到哪个词就查哪行)

表格
你看到的名字到底是啥出现地方
turtle_publisher_cpp可执行文件名(二进制)ros2 run / install/.../lib
turtle_publisher_py可执行文件名(脚本)ros2 run / install/.../lib
turtle_publisher节点名(node name)代码里 Node("turtle_publisher")
turtle1默认海龟命名空间话题 /turtle1/...
/turtle1/cmd_vel话题(发速度)ros2 topic pub / rostopic list
/turtle1/pose话题(收位姿)ros2 topic echo
记住一句话:
*_cpp / *_py 是给“操作系统”找文件的;turtle_publisher 是给“ROS 2 网络”认节点的;turtle1 是 turtlesim 自带的前缀,方便多海龟共存。
当前模型仅对图片中的文字进行识别,切换 K2.5 获取更好的视觉理解能力
好的,谢谢。 你刚才说的:4. launch 文件示例(四选一,想全开就再写 4 个 Node) 是什么意思?全写4个node不是冲突了吗?
不会冲突,因为:
  1. 节点名字(name=)可以任意取,只要在同一命名空间内不重复即可;
  2. 话题完全一样反而正是 ROS 2 的设计——多个节点同时订阅或发布同一话题是允许的,消息会被所有订阅者各自收到,发布者则按到达顺序被订阅者接收(队列大小以内)。

举个例子:把 4 个节点全部启动

Python
复制
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        # 1. 海龟仿真器
        Node(package='turtlesim', executable='turtlesim_node', name='sim'),

        # 2. C++ 画圆
        Node(package='pub_sub', executable='turtle_publisher_cpp', name='cpp_pub'),

        # 3. C++ 打印位姿
        Node(package='pub_sub', executable='turtle_subscriber_cpp', name='cpp_sub'),

        # 4. Python 画圆(同样发 /turtle1/cmd_vel)
        Node(package='pub_sub', executable='turtle_publisher_py', name='py_pub'),

        # 5. Python 打印位姿(同样订阅 /turtle1/pose)
        Node(package='pub_sub', executable='turtle_subscriber_py', name='py_sub'),
    ])

实际效果

表格
场景结果
两个 publisher 同时发 Twist 到 /turtle1/cmd_vel海龟会交替执行两条指令,速度叠加/覆盖,看上去“抖动”或“更快”——不报错,只是逻辑奇怪。
两个 subscriber 同时订阅 /turtle1/pose两条日志并行打印,互不影响;你能看到 C++ 和 Python 版本各自输出一次。

结论

  • ROS 2 允许“多发布者、多订阅者”同话题;
  • 是否冲突取决于业务逻辑而非系统限制;
  • 如果想纯观察,全开 4 个节点完全没问题;
  • 如果想只让一只海龟听话,launch 里只保留一个 publisher 即可(这就是前面说的“四选一”)。