ROS2- 服务-cpp py实现 -09
1.服务
Client-Server
Request-Response
话题Pub/Sub是没有返回的,适用于单向或大量的数据传递。
服务是双向的,客户端发送请求,服务端响应请求。
同一个服务(名称相同)有且只能有一个节点来提供
同一个服务可以被多个客户端调用
ros2 run examples_rclpy_minimal_service service
ros2 service list
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
可以看到客户端请求两个数字5+10,服务端返回15。
service相关的命令
ros2 service list
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts
ros2 service type /add_two_ints # 查看服务接口类型
ros2 service find example_interfaces/srv/AddTwoInts # 查找使用某一接口的服务
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service list
/add_two_ints
/minimal_service/describe_parameters
/minimal_service/get_parameter_types
/minimal_service/get_parameters
/minimal_service/list_parameters
/minimal_service/set_parameters
/minimal_service/set_parameters_atomically
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=5, b=10)
response:
example_interfaces.srv.AddTwoInts_Response(sum=15)
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts
waiting for service to become available...
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=0, b=0)
response:
example_interfaces.srv.AddTwoInts_Response(sum=0)
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service type /add_two_ints
example_interfaces/srv/AddTwoInts
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service find example_interfaces/srv/AddTwoInts
/add_two_ints
2. 服务的cpp实现
cd chapt3/chapt3_ws/src
ros2 pkg create example_service_rclcpp --build-type ament_cmake --dependencies rclcpp
touch example_service_rclcpp/src/service_server_01.cpp
touch example_service_rclcpp/src/service_client_01.cpp
service_server_01.cpp
#include "rclcpp/rclcpp.hpp"
class ServiceServer01 : public rclcpp::Node {
public:
ServiceServer01(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
}
private:
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<ServiceServer01>("service_server_01");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
service_client_01.cpp
#include "rclcpp/rclcpp.hpp"
class ServiceClient01 : public rclcpp::Node {
public:
// 构造函数,有一个参数为节点名称
ServiceClient01(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
}
private:
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
/*创建对应节点的共享指针对象*/
auto node = std::make_shared<ServiceClient01>("service_client_01");
/* 运行节点,并检测退出信号*/
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
CMakeLists.txt
add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp)
add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp)
install(TARGETS
service_server_01
DESTINATION lib/${PROJECT_NAME}
)
install(TARGETS
service_client_01
DESTINATION lib/${PROJECT_NAME}
)
测试
cd chapt3/chapt3_ws/
colcon build --packages-select example_service_rclcpp
# 运行 service_server_01
source install/setup.bash
ros2 run example_service_rclcpp service_server_01
# 打开新终端运行 service_client_01
source install/setup.bash
ros2 run example_service_rclcpp service_client_01
3.服务端
如何利用自带的接口
ros2 interface show example_interfaces/srv/AddTwoInts
导入接口的三个步骤:
在CMakeLists.txt中导入,具体是先find_packages再ament_target_dependencies。
在packages.xml中导入,具体是添加depend标签并将消息接口写入。
在代码中导入,C++中是#include"消息功能包/xxx/xxx.hpp"。
具体:
CMakeLists.txt
find_package(example_interfaces REQUIRED)
add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp example_interfaces)
add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp example_interfaces)
packages.xml
<depend>example_interfaces</depend>
代码:
create_service,参考rclcpp API文档即可
ServiceT,消息接口example_interfaces::srv::AddTwoInts
service_name,服务名称
callback,回调函数,使用成员函数作为回调函数,std::bind进行转换
qos_profile,服务质量配置文件,默认rmw_qos_profile_services_default
group,调用服务的回调组,默认nullptr
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
class ServiceServer01 : public rclcpp::Node {
public:
ServiceServer01(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
// 创建服务
add_ints_server_ =
this->create_service<example_interfaces::srv::AddTwoInts>(
"add_two_ints_srv",
std::bind(&ServiceServer01::handle_add_two_ints, this,
std::placeholders::_1, std::placeholders::_2));
}
private:
// 声明一个服务
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr
add_ints_server_;
// 收到请求的处理函数
void handle_add_two_ints(
const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) {
RCLCPP_INFO(this->get_logger(), "收到a: %ld b: %ld", request->a,
request->b);
response->sum = request->a + request->b;
};
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<ServiceServer01>("service_server_01");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
测试
cd chapt3_ws/
colcon build --packages-select example_service_rclcpp
source install/setup.bash
ros2 run example_service_rclcpp service_server_01
可以看到我们声明的服务
ros2 service list
使用命令行进行调用
ros2 service call /add_two_ints_srv example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service list
/add_two_ints_srv
/service_server_01/describe_parameters
/service_server_01/get_parameter_types
/service_server_01/get_parameters
/service_server_01/list_parameters
/service_server_01/set_parameters
/service_server_01/set_parameters_atomically
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 service call /add_two_ints_srv example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
waiting for service to become available...
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=5, b=10)
response:
example_interfaces.srv.AddTwoInts_Response(sum=15)
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# vi src/example_service_rclcpp/CMakeLists.txt
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# vi src/example_service_rclcpp/package.xml
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# vi src/example_service_rclcpp/src/service_server_01.cpp
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# vi src/example_service_rclcpp/src/service_client_01.cpp
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# vi src/example_service_rclcpp/src/service_server_01.cpp
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# colcon build --packages-select example_service_rclcpp
Starting >>> example_service_rclcpp
Finished <<< example_service_rclcpp [5.50s]
Summary: 1 package finished [5.80s]
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# source install/setup.bash
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 run example_service_rclcpp service_server_01
[INFO] [1749515671.413449518] [service_server_01]: 节点已启动:service_server_01.
[INFO] [1749515695.230367691] [service_server_01]: 收到a: 5 b: 10
4. 客户端
写代码时看API文档,创建客户端 https://link.zhihu.com/?target=https%3A//docs.ros2.org/latest/api/rclcpp/classrclcpp_1_1Node.html%23aed42f345ae1de3a1979d5a8076127199
create_client:
service_name,服务名称
qos_profile,服务质量配置文件,默认rmw_qos_profile_services_default
group,调用服务的回调组,默认nullptr
函数async_send_request()同时传入两个参数
request,请求的消息,这里用于放a,b两个数。
CallBack,回调函数,异步接收服务器的返回的函数
wait_for_service
timeout
参数就一个,等待的时间,返回值是bool类型的,上线了就是true,不上线就是false。
之所以会用的这个函数的原因是,再发送请求之前保证服务端启动了,避免发送一个请求出去而无人响应的尴尬局面。
回调函数
void result_callback_(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture result_future)
函数的参数是客户端AddTwoInts类型的SharedFuture对象,
利用C++11的新特性std::shared_future创建的SharedResponse类模板,
类模板 std::shared_future 提供访问异步操作结果的机制,类似 std::future ,除了允许多个线程等候同一共享状态。
使用get函数即可获取结果。
最后还要修改下主函数:
//增加这一行,node->send_request(5, 6);,计算5+6结果
node->send_request(5, 6);
客户端最终代码:
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
class ServiceClient01 : public rclcpp::Node {
public:
// 构造函数,有一个参数为节点名称
ServiceClient01(std::string name) : Node(name) {
RCLCPP_INFO(this->get_logger(), "节点已启动:%s.", name.c_str());
// 创建客户端
client_ = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints_srv");
}
void send_request(int a, int b) {
RCLCPP_INFO(this->get_logger(), "计算%d+%d", a, b);
// 1.等待服务端上线
while (!client_->wait_for_service(std::chrono::seconds(1))) {
//等待时检测rclcpp的状态
if (!rclcpp::ok()) {
RCLCPP_ERROR(this->get_logger(), "等待服务的过程中被打断...");
return;
}
RCLCPP_INFO(this->get_logger(), "等待服务端上线中");
}
// 2.构造请求的
auto request =
std::make_shared<example_interfaces::srv::AddTwoInts_Request>();
request->a = a;
request->b = b;
// 3.发送异步请求,然后等待返回,返回时调用回调函数
client_->async_send_request(
request, std::bind(&ServiceClient01::result_callback_, this,
std::placeholders::_1));
};
private:
// 声明客户端
rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
void result_callback_(
rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture
result_future) {
auto response = result_future.get();
RCLCPP_INFO(this->get_logger(), "计算结果:%ld", response->sum);
}
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
/*创建对应节点的共享指针对象*/
auto node = std::make_shared<ServiceClient01>("service_client_01");
/* 运行节点,并检测退出信号*/
//增加这一行,node->send_request(5, 6);,计算5+6结果
node->send_request(5, 6);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
测试:
root@ubuntu-16:~/stephen/chapt3/chapt3_ws#
colcon build --packages-select example_service_rclcpp
source install/setup.bash
ros2 run example_service_rclcpp service_client_01
Summary: 1 package finished [4.92s]
[INFO] [1749516734.441409617] [service_client_01]: 节点已启动:service_client_01.
[INFO] [1749516734.442560823] [service_client_01]: 计算5+6
[INFO] [1749516735.442674784] [service_client_01]: 等待服务端上线中
[INFO] [1749516736.442809463] [service_client_01]: 等待服务端上线中
[INFO] [1749516768.349921900] [service_client_01]: 计算结果:11
另一端启动服务:
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 run example_service_rclcpp service_server_01
[INFO] [1749516767.244634848] [service_server_01]: 节点已启动:service_server_01.
[INFO] [1749516768.349500384] [service_server_01]: 收到a: 5 b: 6
python版
创建功能包和节点
cd chapt3/chapt3_ws/src
ros2 pkg create example_service_rclpy --build-type ament_python --dependencies rclpy example_interfaces --node-name service_server_02
发现,依赖,setup.py中的安装配置,ROS2加好了
--node-name service_server_02会帮你创建好节点文件和添加执行文件
cd example_service_rclpy/example_service_rclpy/
touch service_client_02.py
修改setup.py
entry_points={
'console_scripts': [
"service_client_02 = example_service_rclpy.service_client_02:main",
"service_server_02 = example_service_rclpy.service_server_02:main"
],
},
service_server_02
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
class ServiceServer02(Node):
def __init__(self,name):
super().__init__(name)
self.get_logger().info("节点已启动:%s!" % name)
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
node = ServiceServer02("service_server_02") # 新建一个节点
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
service_client_02
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceClient02(Node):
def __init__(self,name):
super().__init__(name)
self.get_logger().info("节点已启动:%s!" % name)
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
node = ServiceClient02("service_client_02") # 新建一个节点
node.send_request(3,4)
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
server端的最终代码
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceServer02(Node):
def __init__(self,name):
super().__init__(name)
self.get_logger().info("节点已启动:%s!" % name)
self.add_ints_server_ = self.create_service(AddTwoInts,"add_two_ints_srv", self.handle_add_two_ints)
def handle_add_two_ints(self,request, response):
self.get_logger().info(f"收到请求,计算{request.a} + {request.b}")
response.sum = request.a + request.b
return response
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
node = ServiceServer02("service_server_02") # 新建一个节点
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
测试
cd chapt3/chapt3_ws/src
colcon build --packages-select example_service_rclpy
source install/setup.bash
ros2 run example_service_rclpy service_server_02
Starting >>> example_service_rclpy
Finished <<< example_service_rclpy [1.16s]
Summary: 1 package finished [1.46s]
[INFO] [1749517444.683091522] [service_server_02]: 节点已启动:service_server_02!
[INFO] [1749517454.423798292] [service_server_02]: 收到请求,计算5 + 10
另一个终端
ros2 service call /add_two_ints_srv example_interfaces/srv/AddTwoInts "{a: 5,b: 10}"
waiting for service to become available...
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=5, b=10)
response:
example_interfaces.srv.AddTwoInts_Response(sum=15)
客户端实现
service_client_02.py
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceClient02(Node):
def __init__(self,name):
super().__init__(name)
self.get_logger().info("节点已启动:%s!" % name)
self.client_ = self.create_client(AddTwoInts,"add_two_ints_srv")
def result_callback_(self, result_future):
response = result_future.result()
self.get_logger().info(f"收到返回结果:{response.sum}")
def send_request(self, a, b):
while rclpy.ok() and self.client_.wait_for_service(1)==False:
self.get_logger().info(f"等待服务端上线....")
request = AddTwoInts.Request()
request.a = a
request.b = b
self.client_.call_async(request).add_done_callback(self.result_callback_)
def main(args=None):
rclpy.init(args=args) # 初始化rclpy
node = ServiceClient02("service_client_02") # 新建一个节点
node.send_request(3,4)
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # 关闭rclpy
root@ubuntu-16:~/stephen/chapt3/chapt3_ws/src# vi example_service_rclpy/example_service_rclpy/service_client_02.py
root@ubuntu-16:~/stephen/chapt3/chapt3_ws/src# cd -
/root/stephen/chapt3/chapt3_ws
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 run example_service_rclpy service_server_02
[INFO] [1749517751.376593714] [service_server_02]: 节点已启动:service_server_02!
[INFO] [1749517904.467982023] [service_server_02]: 收到请求,计算3 + 4
colcon build --packages-select example_service_rclpy
source install/setup.bash
root@ubuntu-16:~/stephen/chapt3/chapt3_ws# ros2 run example_service_rclpy service_client_02
[INFO] [1749517904.462961109] [service_client_02]: 节点已启动:service_client_02!
[INFO] [1749517904.469175061] [service_client_02]: 收到返回结果:7

浙公网安备 33010602011771号