1、准备
1)安装ROS2
【ROS】Ubuntu22.04安装ROS2(Humble Hawksbill)
2)ROS2命令
【ROS】ROS2命令行工具详解
3)配置工作空间
【ROS】ROS2中的概念和名词解释中第一节:工作空间 workspace
4)ROS2源码及官方示例:
https://github.com/ros2
https://github.com/ros2/examples
2、编辑
2.1 创建功能包
1)ros2 pkg create
cd ~/ros/src
mkdir laoerExample
cd laoerExample
ros2 pkg create --build-type ament_cmake cSrvCli --dependencies rclcpp example_interfaces
注意:创建功能包时一定要先创建好目录,进入相应的目录后创建,不要使用下面的方法,否则会在编译时报错
cd ~/ros/src
ros2 pkg create --build-type ament_cmake laoerExample/cSrvCli
2)–dependencies
–dependencies将会自动在package.xml和CMakeLists.txt中添加必要的依赖项
例如:–dependencies rclcpp example_interfaces,将会在package.xml中自动添加:
<depend>rclcpp</depend>
<depend>example_interfaces</depend>
CMakeLists.txt中自动添加:
find_package(rclcpp REQUIRED)
find_package(example_interfaces REQUIRED)
3)example_interfaces
example_interfaces功能包是ROS2中自带的功能包,不需要自己创建
功能包目录:/opt/ros/humble/share/example_interfaces
这个博文的示例中将会用到其中的一个服务接口,两个数相加后,将结果返回,接口文件路径及内容如下:
/opt/ros/humble/share/example_interfaces/srv$ cat AddTwoInts.srv
int64 a
int64 b
---
int64 sum
2.2 编辑源码
2.2.1 服务端
~/ros/src/laoerExample/cSrvCli$ vi srv.cpp
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"#include <memory>/* 求和函数 */
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;/* RCLCPP_INFO:ROS2自带的log输出,分等级,带颜色,输出格式与printf相同,需要标明数据的类型。*/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)
{/* 初始化ROS2 */rclcpp::init(argc, argv);/* 定义服务端节点add_two_ints_server */std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");/* 创建服务名为add_two_ints,服务函数为add的service服务端 */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);/* 退出ROS2 */rclcpp::shutdown();
}
2.2.2 客户端
~/ros/src/laoerExample/cSrvCli$ vi cli.cpp
#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)
{/* 初始化ROS2 */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");/* 创建服务名为add_two_ints的client客户端 */rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");/* 创建请求request */auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();request->a = atoll(argv[1]);request->b = atoll(argv[2]);/* 搜索服务节点,间隔1s */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...");}/* 获得应答并显示其状态 */auto result = client->async_send_request(request);// Wait for the result.if (rclcpp::spin_until_future_complete(node, result) ==rclcpp::FutureReturnCode::SUCCESS){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");}/* 退出ROS2 */rclcpp::shutdown();return 0;
}
2.3 添加依赖配置 package.xml
1)自动添加依赖
在创建功能包时,已经通过参数–dependencies指定了依赖
ros2 pkg create --build-type ament_cmake cSrvCli --dependencies rclcpp example_interfaces
2)手动添加依赖
依赖已自动添加在package.xml中,如果没有则需要手动修改添加即可
<depend>rclcpp</depend>
<depend>example_interfaces</depend>
3)完整的依赖 package.xml 如下:
~/ros/src/laoerExample/cSrvCli$ cat 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>cSrvCli</name><version>0.0.0</version><description>TODO: Package description</description><maintainer email="lesen@todo.todo">lesen</maintainer><license>TODO: License declaration</license><buildtool_depend>ament_cmake</buildtool_depend><depend>rclcpp</depend><depend>example_interfaces</depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export>
</package>
2.4 修改编译配置
1)依赖相关的编译配置
在创建功能包时,已经通过参数–dependencies指定了依赖,CMakeLists.txt中也会自动添加依赖相关的编译配置,如果没有则手动添加
find_package(rclcpp REQUIRED)
find_package(example_interfaces REQUIRED)
2)添加需要编译的源码文件
add_executable(server src/srv.cpp)
ament_target_dependencies(server rclcpp example_interfaces)add_executable(client src/cli.cpp)
ament_target_dependencies(client rclcpp example_interfaces)
3)生成安装规则
install(TARGETSserverclientDESTINATION lib/${PROJECT_NAME})
4)完整 CMakeLists.txt 内容如下:
~/ros/src/laoerExample/cSrvCli$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(cSrvCli)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 filesset(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 filesset(ament_cmake_cpplint_FOUND TRUE)ament_lint_auto_find_test_dependencies()
endif()add_executable(server src/srv.cpp)
ament_target_dependencies(server rclcpp example_interfaces)add_executable(client src/cli.cpp)
ament_target_dependencies(client rclcpp example_interfaces)install(TARGETSserverclientDESTINATION lib/${PROJECT_NAME})ament_package()
3、编译
在工作空间根目录中编译:
cd ~/ros
~/ros$ colcon build
4、运行
在终端1中运行服务端:
~/ros$ source install/setup.sh
~/ros$ ros2 run cSrvCli server
在终端2中运行客户端:
~/ros$ source install/setup.sh
~/ros$ ros2 run cSrvCli client 123 456
[INFO] [1685671664.664459306] [rclcpp]: Sum: 579
注意:运行客户端时,需要传输两个参数值,否则会报错
[INFO] [1685671646.305281649] [rclcpp]: usage: add_two_ints_client X Y
[ros2run]: Process exited with failure 1
终端2输出打印信息:
[INFO] [1685671664.664459306] [rclcpp]: Sum: 579
终端1输出打印信息:
[INFO] [1685671604.946255679] [rclcpp]: Ready to add two ints.
[INFO] [1685671664.664030229] [rclcpp]: Incoming request
a: 123 b: 456
[INFO] [1685671664.664096508] [rclcpp]: sending back response: [579]
5、测试
1)查看节点信息
在终端3中查看节点信息,运行命令:ros2 node list
~$ ros2 node list
/add_two_ints_server
对应服务端源码中定义服务节点时:
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");
2)查看服务信息
在终端3中查看话题信息,运行命令:ros2 service list
~$ ros2 service list
/add_two_ints
/add_two_ints_server/describe_parameters
……
/add_two_ints_server/set_parameters_atomically
对应接口定义:
/opt/ros/humble/share/example_interfaces/srv/AddTwoInts.srv
以及服务端和客户端中定义节点时,使用的接口:
node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);
node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");