我们将构建一个完整的项目来讲解ROS2中的服务,通信和参数
-
服务通信:通过服务控制海龟的运动。
-
参数通信:动态修改海龟的背景颜色。
-
Launch 文件:启动多个节点并传递参数。
项目结构
turtlesim_demo/
├── CMakeLists.txt
├── package.xml
├── src/
│ ├── turtle_controller.cpp
│ ├── background_changer.cpp
├── launch/
│ └── turtlesim_demo.launch.xml
1. 创建 ROS 2 包
创建包:
ros2 pkg create turtlesim_demo --build-type ament_cmake
进入包目录:
cd turtlesim_demo
2. 编写代码
(1)Turtle 控制器(服务通信)
src/turtle_controller.cpp
:
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "turtlesim/srv/spawn.hpp"class TurtleController : public rclcpp::Node
{
public:TurtleController() : Node("turtle_controller"){// 创建发布者,控制海龟运动twist_pub_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10);// 创建客户端,调用 spawn 服务生成新海龟spawn_client_ = this->create_client<turtlesim::srv::Spawn>("spawn");// 定时器,控制海龟运动timer_ = this->create_wall_timer(std::chrono::milliseconds(100),std::bind(&TurtleController::timer_callback, this));}private:void timer_callback(){// 发布运动指令auto twist_msg = geometry_msgs::msg::Twist();twist_msg.linear.x = 2.0;twist_msg.angular.z = 1.0;twist_pub_->publish(twist_msg);// 调用 spawn 服务生成新海龟if (!spawn_client_->wait_for_service(std::chrono::seconds(1))) {RCLCPP_WARN(this->get_logger(), "Waiting for spawn service...");return;}#创建一个spawn服务请求对象requestauto request = std::make_shared<turtlesim::srv::Spawn::Request>();#设置新海龟的初始参数request->x = 5.0;request->y = 5.0;request->theta = 0.0;request->name = "turtle2";auto future = spawn_client_->async_send_request(request);if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), future) ==rclcpp::FutureReturnCode::SUCCESS) {RCLCPP_INFO(this->get_logger(), "Spawned turtle: %s", future.get()->name.c_str());} else {RCLCPP_ERROR(this->get_logger(), "Failed to call spawn service");}}rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_pub_;rclcpp::Client<turtlesim::srv::Spawn>::SharedPtr spawn_client_;rclcpp::TimerBase::SharedPtr timer_;
};int main(int argc, char* argv[])
{rclcpp::init(argc, argv);auto node = std::make_shared<TurtleController>();rclcpp::spin(node);rclcpp::shutdown();return 0;
}
(2)背景颜色修改器(参数通信)
src/background_changer.cpp
:
#include "rclcpp/rclcpp.hpp"
#include "turtlesim/srv/set_pen.hpp"class BackgroundChanger : public rclcpp::Node
{
public:BackgroundChanger() : Node("background_changer"){// 创建客户端,调用 set_pen 服务修改背景颜色set_pen_client_ = this->create_client<turtlesim::srv::SetPen>("turtle1/set_pen");// 定时器,动态修改背景颜色timer_ = this->create_wall_timer(std::chrono::seconds(2),std::bind(&BackgroundChanger::timer_callback, this));}private:void timer_callback(){if (!set_pen_client_->wait_for_service(std::chrono::seconds(1))) {RCLCPP_WARN(this->get_logger(), "Waiting for set_pen service...");return;}// 随机生成颜色值auto request = std::make_shared<turtlesim::srv::SetPen::Request>();request->r = rand() % 256;request->g = rand() % 256;request->b = rand() % 256;request->width = 5;request->off = 0;// 调用 set_pen 服务auto future = set_pen_client_->async_send_request(request);if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), future) ==rclcpp::FutureReturnCode::SUCCESS) {RCLCPP_INFO(this->get_logger(), "Changed background color to (%d, %d, %d)",request->r, request->g, request->b);} else {RCLCPP_ERROR(this->get_logger(), "Failed to call set_pen service");}}rclcpp::Client<turtlesim::srv::SetPen>::SharedPtr set_pen_client_;rclcpp::TimerBase::SharedPtr timer_;
};int main(int argc, char* argv[])
{rclcpp::init(argc, argv);auto node = std::make_shared<BackgroundChanger>();rclcpp::spin(node);rclcpp::shutdown();return 0;
}
以下是这段代码的基本步骤总结:
1. 初始化ROS 2环境
使用
rclcpp::init(argc, argv)
初始化ROS 2节点,这是运行ROS 2程序的必要步骤。2. 创建节点
创建一个名为
BackgroundChanger
的节点类,继承自rclcpp::Node
。在构造函数中:
初始化节点名称为
"background_changer"
。创建一个服务客户端
set_pen_client_
,用于调用turtle1/set_pen
服务,修改海龟的画笔颜色。创建一个定时器
timer_
,每隔2秒触发一次回调函数timer_callback
。3. 定时器回调函数
在
timer_callback
函数中:
检查服务是否可用:
使用
set_pen_client_->wait_for_service(std::chrono::seconds(1))
等待服务可用。如果服务不可用,打印警告信息并退出回调函数。
随机生成颜色值:
使用
rand() % 256
随机生成RGB颜色值(范围为0到255)。设置画笔宽度为5,启用画笔(
off = 0
)。调用
set_pen
服务:
创建服务请求
request
,并设置随机颜色值。使用
set_pen_client_->async_send_request(request)
异步发送请求。使用
rclcpp::spin_until_future_complete
等待服务响应完成。如果服务调用成功,打印成功信息,显示当前设置的颜色。
如果服务调用失败,打印错误信息。
4. 主函数
在
main
函数中:
初始化ROS 2环境。
创建
BackgroundChanger
节点实例。启动节点的事件循环
rclcpp::spin(node)
,使节点能够处理定时器回调。程序退出时,调用
rclcpp::shutdown()
关闭ROS 2节点。5. 程序运行逻辑
程序运行后,定时器每隔2秒触发一次回调函数。
每次回调函数都会随机生成颜色值,并通过
set_pen
服务设置海龟的画笔颜色。由于画笔宽度较大(5),海龟会绘制整个窗口,从而间接改变背景颜色。
3. 配置 CMakeLists.txt
在 ROS 2 中,CMakeLists.txt
文件是用于配置和构建项目的核心文件。它定义了项目的编译规则、依赖项、可执行文件、库文件等。
cmake_minimum_required(VERSION 3.8)
project(turtlesim_demo)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)add_executable(turtle_controller src/turtle_controller.cpp)
ament_target_dependencies(turtle_controller rclcpp geometry_msgs turtlesim)add_executable(background_changer src/background_changer.cpp)
ament_target_dependencies(background_changer rclcpp turtlesim)install(TARGETSturtle_controllerbackground_changerDESTINATION lib/${PROJECT_NAME}
)ament_package()
1. 定义项目的基本信息
CMakeLists.txt
文件的开头通常包含项目的基本信息,例如项目名称和最低 CMake 版本要求:
cmake_minimum_required(VERSION 3.8)
project(turtlesim_demo)
2. 设置编译器选项
在 CMakeLists.txt
中,可以为项目设置编译器选项,例如启用警告信息:
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic)
endif()
3. 查找依赖项
ROS 2 项目通常依赖于其他包(如 rclcpp
、geometry_msgs
、turtlesim
等)。CMakeLists.txt
中需要明确声明这些依赖项:
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)
4. 添加可执行文件
在 CMakeLists.txt
中,需要定义如何将源代码编译为可执行文件:
add_executable(turtle_controller src/turtle_controller.cpp)
ament_target_dependencies(turtle_controller rclcpp geometry_msgs turtlesim)
5. 安装可执行文件
ROS 2 项目通常需要将生成的可执行文件安装到指定目录,以便在运行时能够找到它们:
install(TARGETSturtle_controllerbackground_changerDESTINATION lib/${PROJECT_NAME}
)
6. 生成包配置
CMakeLists.txt
文件的末尾需要调用 ament_package()
,用于生成 ROS 2 包的配置:
7. 测试配置(可选)
如果项目包含测试代码,可以在 CMakeLists.txt
中配置测试:
if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)ament_lint_auto_find_test_dependencies()
endif()
8. 为什么需要配置 CMakeLists.txt
?
定义项目结构:
CMakeLists.txt
定义了项目的源代码文件、依赖项、可执行文件等,确保项目能够正确编译和运行。管理依赖项:
ROS 2 项目通常依赖于多个包(如
rclcpp
、geometry_msgs
等),CMakeLists.txt
中需要明确声明这些依赖项。控制编译过程:
通过
CMakeLists.txt
,可以设置编译器选项、链接库文件、安装路径等,控制项目的编译过程。生成包配置:
CMakeLists.txt
中的ament_package()
会生成 ROS 2 包的配置文件,使 ROS 2 能够正确加载和使用该包。支持跨平台构建:
CMake 是一个跨平台的构建工具,
CMakeLists.txt
可以确保项目在不同平台上(如 Linux、Windows、macOS)都能正确编译。
4. 配置 package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3"><name>turtlesim_demo</name><version>0.0.0</version><description>A demo package for turtlesim with services, parameters, and launch files.</description><maintainer email="your-email@example.com">Your Name</maintainer><license>Apache-2.0</license><buildtool_depend>ament_cmake</buildtool_depend><depend>rclcpp</depend><depend>geometry_msgs</depend><depend>turtlesim</depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export>
</package>
在 ROS 2 中,package.xml
文件是包的元数据文件,用于描述包的基本信息、依赖项、作者、许可证等。
1. 定义包的基本信息
package.xml
文件的开头通常包含包的基本信息,例如包名称、版本、描述、维护者和许可证:
<package format="3"><name>turtlesim_demo</name><version>0.0.0</version><description>A demo package for turtlesim with services, parameters, and launch files.</description><maintainer email="your-email@example.com">Your Name</maintainer><license>Apache-2.0</license>
</package>
2. 声明构建工具依赖
ROS 2 使用 ament_cmake
作为构建工具,因此需要在 package.xml
中声明构建工具依赖:
<buildtool_depend>ament_cmake</buildtool_depend>
3. 声明运行时依赖
ROS 2 项目通常依赖于其他包(如 rclcpp
、geometry_msgs
、turtlesim
等),需要在 package.xml
中声明这些依赖项:
<depend>rclcpp</depend>
<depend>geometry_msgs</depend>
<depend>turtlesim</depend>
4. 声明测试依赖(可选)
如果项目包含测试代码,可以在 package.xml
中声明测试依赖项:
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
5. 导出包配置
package.xml
文件的末尾需要导出包的配置信息,以便 ROS 2 能够正确加载和使用该包:
<export><build_type>ament_cmake</build_type>
</export>
6. 为什么需要配置 package.xml
?
描述包的基本信息:
package.xml
提供了包的名称、版本、描述、维护者和许可证等基本信息,方便其他开发者了解和使用该包。管理依赖项:
package.xml
中声明了包的依赖项,ROS 2 会在构建和运行时自动加载这些依赖项,确保项目能够正确运行。支持包的管理和分发:
package.xml
是 ROS 2 包的重要组成部分,ROS 2 工具(如colcon
)会根据package.xml
中的信息管理包的构建、安装和分发。支持测试和代码风格检查:
如果项目包含测试代码,可以在
package.xml
中声明测试依赖项,例如代码风格检查工具。支持跨平台构建:
package.xml
中的依赖项和配置信息可以确保项目在不同平台上(如 Linux、Windows、macOS)都能正确编译和运行。
5. 编写 Launch 文件
launch/turtlesim_demo.launch.xml
:
<launch><!-- 启动 turtlesim 节点 --><node pkg="turtlesim" exec="turtlesim_node" name="turtlesim" /><!-- 启动 Turtle 控制器 --><node pkg="turtlesim_demo" exec="turtle_controller" name="turtle_controller" /><!-- 启动背景颜色修改器 --><node pkg="turtlesim_demo" exec="background_changer" name="background_changer" />
</launch>
这个 <launch>
文件的作用是同时启动三个节点:
-
turtlesim_node
:启动turtlesim
模拟器,显示一个小乌龟。 -
turtle_controller
:控制乌龟的移动。 -
background_changer
:修改turtlesim
窗口的背景颜色。
通过这个配置文件,你可以一次性启动多个相关的节点,而不需要手动逐个启动。这在复杂的 ROS 2 系统中非常有用,可以简化启动过程并确保所有相关节点都能正确启动。
总结
我们构建了一个完整的项目来讲解ROS2中的服务,通信和参数
-
服务通信:通过服务控制海龟的运动。
-
参数通信:动态修改海龟的背景颜色。
-
Launch 文件:启动多个节点并传递参数。