# ros2学习 **Repository Path**: linzhenghan/ros2-learning ## Basic Information - **Project Name**: ros2学习 - **Description**: ros2 学习文档和代码记录 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-08 - **Last Updated**: 2026-03-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ros2 学习 ## 1 - ros2 安装 - 使用鱼香ros一键安装 ```shell wget http://fishros.com/install -O fishros && . fishros ``` ## 2 - ros2 基础 ### 使用功能包构筑一个简单的 C++ 节点 - 使用功能包组织 C++ 节点 ```shell ros2 pkg create demo_cpp_pkg --build-type ament_cmake --license Apache-2.0 ``` demo_cpp_pkg 是功能包的名称 --build-type ament_cmake 表示使用 cmake 构建 --license Apache-2.0 表示使用 Apache-2.0 许可证 - 添加 c++ 节点 ```shell cd demo_cpp_pkg/src touch cpp_node.cpp ``` - 编写最简单的c++节点 ```cpp #include "rclcpp/rclcpp.hpp" int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared("cpp_node"); RCLCPP_INFO(node->get_logger(), "Hello ROS2!"); rclcpp::spin(node); rclcpp::shutdown(); return 0; } ``` - 编译节点 修改CMakeLists.txt ```cmake find_package(rclcpp REQUIRED) add_executable(cpp_node src/cpp_node.cpp) ament_target_dependencies(cpp_node rclcpp) install(TARGETS cpp_node DESTINATION lib/${PROJECT_NAME}) ``` 修改package.xml 添加依赖 ```xml rclcpp ``` - 运行节点 ```shell source install/setup.bash ros2 run demo_cpp_pkg cpp_node ``` 在 ros2 中 src 目录可以约定为工作空间,其中可以放不同的功能包,每个功能包可以有多个节点,在运行的过程中可以在工作空间同级目录下使用 colcon build 编译所有功能包。同时可以使用colcon build --packages-select demo_cpp_pkg 编译指定的功能包。 然后可以通过在 package.xml 中添加依赖来优先构筑某个功能包。 ### 面向对象编程基础 在 ros2 开发中往往使用面对对象的编程方式,将节点封装为一个类,类中包含节点的属性和方法。 ```cpp #include "rclcpp/rclcpp.hpp" #include class PersonNode : public rclcpp::Node { private: std::string name_; int age_; public: PersonNode(const std::string &node_name, const std::string &name, const int &age) : Node(node_name) { this->name_ = name; this->age_ = age; } void eat(const std::string &food_name) { RCLCPP_INFO(this->get_logger(), "我是%s,今年%d岁,我正在吃%s", this->name_.c_str(), this->age_, food_name.c_str()); } }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared("cpp_node", "张三", 18); node->eat("鱼香肉丝"); rclcpp::spin(node); rclcpp::shutdown(); return 0; } ``` 在编译的参考上一章节的方法,将节点的名称改为 person_node 即可。 ## 3 - 话题-订阅和发布 ROS2 中的话题机制有四个关键点,分别是发布者、订阅者、话题名称和话题类型。 在本次案例中,利用ROS2中的海龟模拟器,先通过话题发布速度,控制海龟花园,然后通过话题订阅海龟的当前位置,根据当前位置和目标位置的差距来修正控制命令,实现对海龟位置的闭环控制。 运行海龟模拟器 ```shell ros2 run turtlesim turtlesim_node ``` 查看海龟模拟器节点信息 ```shell ros2 node info /turtlesim ``` 输出海龟当前的位姿 ```shell ros2 topic echo /turtle1/pose ``` 查看某个话题的具体信息 ```shell ros2 topic info /turtle1/cmd_vel -v ``` 查看消息接口的详细定义 ```shell ros2 interface show geometry_msgs/msg/Twist ``` ### 发布速度控制海龟 先使用命令创建功能包 ```shell ros2 pkg create demo_cpp_topic --build-type ament_cmake --dependencies rclcpp geometry_msgs turtlesim --license Apache-2.0 ``` - `demo_cpp_topic` 是功能包的名称 - `--build-type ament_cmake` 表示使用 cmake 构建系统 - `--dependencies rclcpp geometry_msgs turtlesim` 指定了功能包的依赖项: - `rclcpp`: ROS2的C++客户端库 - `geometry_msgs`: 几何消息类型,用于传递位置、速度等信息 - `turtlesim`: 海龟模拟器包 - `--license Apache-2.0` 表示使用 Apache-2.0 许可证 然后在功能包的 src 目录下创建 turtle_circle.cpp 文件,编写发布速度控制海龟花园的节点。 ```cpp #include "rclcpp/rclcpp.hpp" #include "geometry_msgs/msg/twist.hpp" #include using namespace std::chrono_literals; class TurtleCircle : public rclcpp::Node { private: rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisher::SharedPtr publisher_; public: explicit TurtleCircle(const std::string & node_name) : Node(node_name) { publisher_ = this->create_publisher("turtle1/cmd_vel", 10); timer_ = this->create_wall_timer(500ms, std::bind(&TurtleCircle::timer_callback, this)); } private: void timer_callback() { auto message = geometry_msgs::msg::Twist(); message.linear.x = 0.5; message.angular.z = 0.5; publisher_->publish(message); } }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared("turtle_circle"); rclcpp::spin(node); rclcpp::shutdown(); return 0; } ``` 修改 CMakeLists.txt 文件,添加编译节点的代码 ```cmake cmake_minimum_required(VERSION 3.8) project(demo_cpp_topic) # 设置C++编译选项,开启常见警告 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(geometry_msgs REQUIRED) find_package(turtlesim REQUIRED) # 虽然代码中没直接用,但保留你的依赖声明 # 构建可执行文件(turtle_circle节点) add_executable(turtle_circle src/turtle_circle.cpp) # 链接所有依赖包的头文件和库 ament_target_dependencies(turtle_circle rclcpp geometry_msgs turtlesim # 补充到依赖中,避免潜在的链接问题 ) # 安装可执行文件(核心修正:TARGET → TARGETS 复数形式) install(TARGETS turtle_circle DESTINATION lib/${PROJECT_NAME} ) # 测试相关配置(保留你的原有设置) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # 跳过版权检查(新手可保留) set(ament_cmake_copyright_FOUND TRUE) # 跳过cpplint检查(新手可保留) set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() # 声明ament包(ROS2必需) ament_package() ``` 然后使用编译命令编译节点 ```shell colcon build --packages-select demo_cpp_topic ``` 首先运行海龟模拟器 ```shell ros2 run turtlesim turtlesim_node ``` 然后运行发布速度的节点 ```shell ros2 run demo_cpp_topic turtle_circle ``` 可以看到海龟在花园中以圆形运动。 ### 订阅Pose实现闭环控制 完整实现代码 ```cpp #include "geometry_msgs/msg/twist.hpp" #include "rclcpp/rclcpp.hpp" #include "turtlesim/msg/pose.hpp" class TurtleController : public rclcpp::Node { public: TurtleController() : Node("turtle_controller") { velocity_publisher_ = this->create_publisher( "/turtle1/cmd_vel", 10); pose_subscription_ = this->create_subscription( "/turtle1/pose", 10, std::bind(&TurtleController::on_pose_received_, this, std::placeholders::_1)); } private: void on_pose_received_(const turtlesim::msg::Pose::SharedPtr pose) { auto message = geometry_msgs::msg::Twist(); // 1.记录当前位置 double current_x = pose->x; double current_y = pose->y; RCLCPP_INFO(this->get_logger(), "当前位置:(x=%f,y=%f)", current_x, current_y); // 2.计算距离目标的距离,与当前海龟朝向的角度差 double distance = std::sqrt((target_x_ - current_x) * (target_x_ - current_x) + (target_y_ - current_y) * (target_y_ - current_y)); double angle = std::atan2(target_y_ - current_y, target_x_ - current_x) - pose->theta; // 3.控制策略:距离大于0.1继续运动,角度差大于0.2则原地旋转,否则直行 if (distance > 0.1) { if(fabs(angle)>0.2) { message.angular.z = fabs(angle); }else{ // 通过比例控制器计算输出速度 message.linear.x = k_ * distance; } } // 4.限制最大值并发布消息 if (message.linear.x > max_speed_) { message.linear.x = max_speed_; } velocity_publisher_->publish(message); } private: rclcpp::Subscription::SharedPtr pose_subscription_; rclcpp::Publisher::SharedPtr velocity_publisher_; double target_x_{1.0}; // 目标位置X,设置默认值1.0 double target_y_{1.0}; // 目标位置Y,设置默认值1.0 double k_{1.0}; // 比例系数,控制输出=误差*比例系数 double max_speed_{3.0}; // 最大线速度,设置默认值3.0 }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared(); rclcpp::spin(node); rclcpp::shutdown(); return 0; } ``` ### 话题通讯实践 明确需求 1、创建一个小工具监看系统的实时状态信息,包括记录信息的实现、主机名称、cpu使用率、内存使用率、内存总大小、剩余内存、网络接受数据量和网络发送数据两 2、有一个简单的界面,可以将系统信息显示出来 3、可以在其他主机上查看数据 实际就是编写一个python节点获取系统信息并通过话题发布出来,接着使用c++编写一个显示节点,订阅这个话题并进行显示 首先在 topic_practice_ws/src 目录下输入下面代码创建功能包 ```shell ros2 pkg create status_interfaces --build-type ament_cmake --dependencies rosidl_default_generators builtin_interfaces --license Apache-2.0 ``` 上面的指令用于创建一个名为 status_interfaces 的功能包,并为其添加 builtin_interfaces 和 rosidl_default_generators 这两个依赖。builtin_interfaces 是 ROS2 中用于定义基本数据类型的包,而 rosidl_default_generators 是用于生成 ROS2 接口代码的工具包。 在 ROS2 中,话题信息定义文件需要放置到功能包的msg目录下,文件名必须以大写字母开头且只能由大写字母、小写字母、数字组成。新建 SystemStatus.msg 文件,内容如下 ```shell builtin_interfaces/Time stamp # 时间戳 string host_name # 主机名称 float32 cpu_percent # cpu使用率 float32 memory_percent # 内存使用率 float32 memory_total # 内存总大小 float32 memory_available # 剩余内存 float32 net_sent # 网络发送数据量 float32 net_rev # 网络接受数据量 ``` ros2 消息接口支持的9种数据类型 ```shell bool byte char float32 float64 int8 int16 int32 int64 ```