ROS2 入门第二章

机器人

Posted by LXG on May 23, 2026

ROS2 教程

编写简单的服务和客户端

创建新的包


lxg@lxg:~/code/ROS2/workspace/demo/src$ ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp example_interfaces
going to create a new package
package name: cpp_srvcli
destination directory: /home/lxg/code/ROS2/workspace/demo/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['lxg <xiaogang.li>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: ['rclcpp', 'example_interfaces']
creating folder ./cpp_srvcli
creating ./cpp_srvcli/package.xml
creating source and include folder
creating folder ./cpp_srvcli/src
creating folder ./cpp_srvcli/include/cpp_srvcli
creating ./cpp_srvcli/CMakeLists.txt

lxg@lxg:~/code/ROS2/workspace/demo/src$ ls /opt/ros/humble/include/example_interfaces/example_interfaces/srv/
add_two_ints.h  add_two_ints.hpp  detail  set_bool.h  set_bool.hpp  trigger.h  trigger.hpp

编写服务端代码


#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"

#include <memory>

// 文件说明:
// 这是一个最小的 ROS2 服务端示例,提供名为 `add_two_ints` 的服务。
// 服务类型为 `example_interfaces::srv::AddTwoInts`,接收两个整数 `a` 和 `b`,
// 返回它们的和 `sum`。示例展示了如何定义服务回调、创建服务并进入事件循环。

// 服务回调函数:处理来自客户端的请求并填充响应
// 参数:
// - request: 指向请求消息的共享指针,包含请求字段 `a` 和 `b`(要相加的两个整数)
// - response: 指向响应消息的共享指针,回调应设置 `response->sum` 字段
void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
         std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response)
{
  // 计算和并写入响应对象
  response->sum = request->a + request->b;

  // 打印接收到的请求到日志,便于调试
  // 注意:使用 `%ld` 打印 long int,取决于平台上整数类型的大小
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
              request->a, request->b);

  // 打印返回的响应值
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}

int main(int argc, char **argv)
{
  // 初始化 ROS 2 客户端库(必须在程序开始时调用)
  rclcpp::init(argc, argv);

  // 创建一个名为 "add_two_ints_server" 的节点实例
  // `make_shared` 返回节点的共享指针,便于与 `spin` 一起使用
  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");

  // 创建服务:
  // - 模板参数:服务的类型 `example_interfaces::srv::AddTwoInts`
  // - 第一个参数:服务名 "add_two_ints",客户端将使用该名称进行调用
  // - 第二个参数:回调函数指针 `&add`,用于处理每次到来的请求
  rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service =
    node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);

  // 日志:表示服务已经准备就绪,可以接受请求
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");

  // 进入事件循环,处理传入的请求和回调,直到节点退出
  rclcpp::spin(node);

  // 清理并关闭 ROS 2 客户端库
  rclcpp::shutdown();
}

写入客户端节点


#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"

#include <chrono>
#include <cstdlib>
#include <memory>

using namespace std::chrono_literals;

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);
  // 参数检查:期望两个整数参数 X 和 Y
  if (argc != 3) {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y");
    return 1;
  }

  // 创建客户端节点
  // 节点名为 "add_two_ints_client"
  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");

  // 创建服务客户端:
  // - 模板参数为服务类型 `example_interfaces::srv::AddTwoInts`
  // - 参数为服务名 "add_two_ints",应与服务端提供的名称一致
  rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =
    node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

  // 构造请求消息并填充请求字段 a 和 b
  auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
  // 将命令行参数转换为 long(使用 atoll)并赋值
  request->a = atoll(argv[1]);
  request->b = atoll(argv[2]);

  // 等待服务可用,超时为 1 秒,每次超时后会再次检查
  // 如果用户按下 Ctrl+C 或 ROS 被中断(rclcpp::ok() 变为 false),程序将退出
  while (!client->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
      return 0;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  }

  // 异步发送请求,返回一个 future 对象用于获取响应
  auto result = client->async_send_request(request);

  // 等待 future 完成(即等待服务端响应)并处理结果
  if (rclcpp::spin_until_future_complete(node, result) ==
    rclcpp::FutureReturnCode::SUCCESS)
  {
    // 如果成功,打印响应中的 sum 字段
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  } else {
    // 否则打印错误信息
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
  }

  // 关闭 ROS 2 客户端库并退出
  rclcpp::shutdown();
  return 0;
}

Cmake 配置


cmake_minimum_required(VERSION 3.8)
project(cpp_srvcli)

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

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(example_interfaces REQUIRED)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
rclcpp example_interfaces)

add_executable(client src/add_two_ints_client.cpp)
ament_target_dependencies(client
  rclcpp example_interfaces)

install(TARGETS
  server
  client
  DESTINATION lib/${PROJECT_NAME})

ament_package()

编译


lxg@lxg:~/code/ROS2/workspace/demo$ colcon build --packages-select cpp_srvcli
Starting >>> cpp_srvcli
Finished <<< cpp_srvcli [3.22s]                     

Summary: 1 package finished [3.43s]

运行


# 服务端
lxg@lxg:~/code/ROS2/workspace/demo$ ros2 run cpp_srvcli server
[INFO] [1779517707.444431515] [rclcpp]: Ready to add two ints.
[INFO] [1779517722.915908316] [rclcpp]: Incoming request
a: 2 b: 3
[INFO] [1779517722.915932832] [rclcpp]: sending back response: [5]

# 客户端
lxg@lxg:~/code/ROS2/workspace/demo$ ros2 run cpp_srvcli client 2 3
[INFO] [1779517722.916145334] [rclcpp]: Sum: 5

创建自定义ROS2 msg 和 srv 文件

创建新包tutorial_interfaces


lxg@lxg:~/code/ROS2/workspace/demo/src$ ros2 pkg create --build-type ament_cmake tutorial_interfaces
going to create a new package
package name: tutorial_interfaces
destination directory: /home/lxg/code/ROS2/workspace/demo/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['lxg <xiaogang.li>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: []
creating folder ./tutorial_interfaces
creating ./tutorial_interfaces/package.xml
creating source and include folder
creating folder ./tutorial_interfaces/src
creating folder ./tutorial_interfaces/include/tutorial_interfaces
creating ./tutorial_interfaces/CMakeLists.txt

编写msg 和 srv


lxg@lxg:~/code/ROS2/workspace/demo/src/tutorial_interfaces$ tree  -L 2
.
├── build
│   ├── COLCON_IGNORE
│   └── tutorial_interfaces
├── CMakeLists.txt
├── include
│   └── tutorial_interfaces
├── install
│   ├── COLCON_IGNORE
│   ├── local_setup.bash
│   ├── local_setup.ps1
│   ├── local_setup.sh
│   ├── _local_setup_util_ps1.py
│   ├── _local_setup_util_sh.py
│   ├── local_setup.zsh
│   ├── setup.bash
│   ├── setup.ps1
│   ├── setup.sh
│   ├── setup.zsh
│   └── tutorial_interfaces
├── log
│   ├── build_2026-05-23_14-39-29
│   ├── COLCON_IGNORE
│   ├── latest -> latest_build
│   └── latest_build -> build_2026-05-23_14-39-29
├── msg
│   └── Num.msg
├── package.xml
├── src
└── srv
    └── AddThreeInts.srv

13 directories, 17 files

编译自动生成的num.hpp

./build/tutorial_interfaces/rosidl_generator_cpp/tutorial_interfaces/msg/num.hpp

./install/tutorial_interfaces/include/tutorial_interfaces/tutorial_interfaces/msg/num.hpp


// generated from rosidl_generator_cpp/resource/idl.hpp.em
// generated code does not contain a copyright notice

#ifndef TUTORIAL_INTERFACES__MSG__NUM_HPP_
#define TUTORIAL_INTERFACES__MSG__NUM_HPP_

#include "tutorial_interfaces/msg/detail/num__struct.hpp"
#include "tutorial_interfaces/msg/detail/num__builder.hpp"
#include "tutorial_interfaces/msg/detail/num__traits.hpp"
#include "tutorial_interfaces/msg/detail/num__type_support.hpp"

#endif  // TUTORIAL_INTERFACES__MSG__NUM_HPP_

lxg@lxg:~/code/ROS2/workspace/demo/src/tutorial_interfaces$ find . -name “add_three_ints.hpp”

./build/tutorial_interfaces/rosidl_generator_cpp/tutorial_interfaces/srv/add_three_ints.hpp

./install/tutorial_interfaces/include/tutorial_interfaces/tutorial_interfaces/srv/add_three_ints.hpp


// generated from rosidl_generator_cpp/resource/idl.hpp.em
// generated code does not contain a copyright notice

#ifndef TUTORIAL_INTERFACES__SRV__ADD_THREE_INTS_HPP_
#define TUTORIAL_INTERFACES__SRV__ADD_THREE_INTS_HPP_

#include "tutorial_interfaces/srv/detail/add_three_ints__struct.hpp"
#include "tutorial_interfaces/srv/detail/add_three_ints__builder.hpp"
#include "tutorial_interfaces/srv/detail/add_three_ints__traits.hpp"
#include "tutorial_interfaces/srv/detail/add_three_ints__type_support.hpp"

#endif  // TUTORIAL_INTERFACES__SRV__ADD_THREE_INTS_HPP_