自学内容网 自学内容网

25/1/21 算法笔记<ROS2> 服务通信,参数

我们将构建一个完整的项目来讲解ROS2中的服务,通信和参数

  1. 服务通信:通过服务控制海龟的运动。

  2. 参数通信:动态修改海龟的背景颜色。

  3. 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服务请求对象request
        auto 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函数中:

    1. 检查服务是否可用

      • 使用set_pen_client_->wait_for_service(std::chrono::seconds(1))等待服务可用。

      • 如果服务不可用,打印警告信息并退出回调函数。

    1. 随机生成颜色值

      • 使用rand() % 256随机生成RGB颜色值(范围为0到255)。

      • 设置画笔宽度为5,启用画笔(off = 0)。

    2. 调用set_pen服务

      • 创建服务请求request,并设置随机颜色值。

      • 使用set_pen_client_->async_send_request(request)异步发送请求。

      • 使用rclcpp::spin_until_future_complete等待服务响应完成。

      • 如果服务调用成功,打印成功信息,显示当前设置的颜色。

      • 如果服务调用失败,打印错误信息。

4. 主函数

  • main函数中:

    1. 初始化ROS 2环境。

    2. 创建BackgroundChanger节点实例。

    3. 启动节点的事件循环rclcpp::spin(node),使节点能够处理定时器回调。

    4. 程序退出时,调用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(TARGETS
  turtle_controller
  background_changer
  DESTINATION 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 项目通常依赖于其他包(如 rclcppgeometry_msgsturtlesim 等)。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(TARGETS
  turtle_controller
  background_changer
  DESTINATION 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

  1. 定义项目结构

    • CMakeLists.txt 定义了项目的源代码文件、依赖项、可执行文件等,确保项目能够正确编译和运行。

  2. 管理依赖项

    • ROS 2 项目通常依赖于多个包(如 rclcppgeometry_msgs 等),CMakeLists.txt 中需要明确声明这些依赖项。

  3. 控制编译过程

    • 通过 CMakeLists.txt,可以设置编译器选项、链接库文件、安装路径等,控制项目的编译过程。

  4. 生成包配置

    • CMakeLists.txt 中的 ament_package() 会生成 ROS 2 包的配置文件,使 ROS 2 能够正确加载和使用该包。

  5. 支持跨平台构建

    • 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 项目通常依赖于其他包(如 rclcppgeometry_msgsturtlesim 等),需要在 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

  1. 描述包的基本信息

    • package.xml 提供了包的名称、版本、描述、维护者和许可证等基本信息,方便其他开发者了解和使用该包。

  2. 管理依赖项

    • package.xml 中声明了包的依赖项,ROS 2 会在构建和运行时自动加载这些依赖项,确保项目能够正确运行。

  3. 支持包的管理和分发

    • package.xml 是 ROS 2 包的重要组成部分,ROS 2 工具(如 colcon)会根据 package.xml 中的信息管理包的构建、安装和分发。

  4. 支持测试和代码风格检查

    • 如果项目包含测试代码,可以在 package.xml 中声明测试依赖项,例如代码风格检查工具。

  5. 支持跨平台构建

    • 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> 文件的作用是同时启动三个节点:

  1. turtlesim_node:启动 turtlesim 模拟器,显示一个小乌龟。

  2. turtle_controller:控制乌龟的移动。

  3. background_changer:修改 turtlesim 窗口的背景颜色。

通过这个配置文件,你可以一次性启动多个相关的节点,而不需要手动逐个启动。这在复杂的 ROS 2 系统中非常有用,可以简化启动过程并确保所有相关节点都能正确启动。

总结

我们构建了一个完整的项目来讲解ROS2中的服务,通信和参数

  1. 服务通信:通过服务控制海龟的运动。

  2. 参数通信:动态修改海龟的背景颜色。

  3. Launch 文件:启动多个节点并传递参数。


原文地址:https://blog.csdn.net/yyyy2711/article/details/145291480

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!