【服务通信/参数通信】
【服务通信/参数通信】
服务通信:请求/响应 双向通信机制
参数通信:管理节点
关于服务和参数【概述】
服务通信 service
ros2 service list -t
查询服务列表 -t:显示服务接口类型
/spawn [turtlesim/srv/Spawn]
spawn:服务名称 产生一只新海龟
[ ]:接口类型->(1)请求接口 (2)响应接口
ros2 interface show turtlesim/srv/Spawn
# 请求接口定义
float32 x
float32 y
float32 theta
string name # Optional. A unique name will be created and returned if this is empty
---
# 响应接口定义
string name
调用接口
ros2 service call /spawn turtlesim/srv/Spawn "{x: 1, y: 1}"
基于服务的参数通信 param
参数:节点的设置
ros2 service list -t | grep parameter
查看和参数有关的服务
/turtlesim/describe_parameters [rcl_interfaces/srv/DescribeParameters]
/turtlesim/get_parameter_types [rcl_interfaces/srv/GetParameterTypes]
/turtlesim/get_parameters [rcl_interfaces/srv/GetParameters]
/turtlesim/list_parameters [rcl_interfaces/srv/ListParameters]
/turtlesim/set_parameters [rcl_interfaces/srv/SetParameters]
/turtlesim/set_parameters_atomically [rcl_interfaces/srv/SetParametersAtomically]
关于ros2 param
使用
delete Delete parameter
describe Show descriptive information about declared parameters
dump Show all of the parameters of a node in a YAML file format
get Get parameter
list Output a list of available parameters
load Load parameter file for a node
set Set parameter
ros2 param list
查看参数列表
/turtlesim:
background_b
background_g
background_r
qos_overrides./parameter_events.publisher.depth
qos_overrides./parameter_events.publisher.durability
qos_overrides./parameter_events.publisher.history
qos_overrides./parameter_events.publisher.reliability
use_sim_time
ros2 param describe /turtlesim background_r
查看某个参数的具体描述
Parameter name: background_r
Type: integer
Description: Red channel of the background color
Constraints:
Min value: 0
Max value: 255
Step: 1
ros2 param get /turtlesim background_r
获取节点参数值 get
ros2 param set /turtlesim background_r 255
修改参数值 set
使用文件配置参数
将某节点配置导出yaml格式文件
ros2 param dump /turtlesim > turtlesim_param.yaml
修改yaml文件
重新打开终端
ros2 run turtlesim turtlesim_node --ros-args --params-file turtlesim_param.yaml
自定义服务接口:海龟寻迹项目为例
创建一个功能包来存服务接口
srv/Patrol.srv
# Request 请求部分
float32 target_x # 目标x值
float32 target_y # 目标y值
---
# Response 应答部分
int8 SUCCESS = 1 # 定义常量,表示成功
int8 FAIL = 0 # 定义常量,表示失败
int8 result # 处理结果 -> SUCCESS/FAIL取其一
CMakeList.txt注册
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)
find_package(sensor_msgs REQUIRED)
# 在rosidl_generate_interfaces中添加sensor_msgs依赖
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/Patrol.srv"
DEPENDENCIES sensor_msgs
)
package.xml声明:同消息接口
测试
$ ros2 interface show chapt4_interfaces/srv/Patrol //注意这里文件长度不超过3个
服务端:控制海龟到目标点
创建功能包:与srv同级目录
ros2 pkg create demo_cpp_service...
--dependencies ... chapt4_interfaces ... //注意把chapt4_interfaces包含到依赖里
在闭环控制海龟基础上写服务端
【添加服务头文件】
//添加服务头文件并创建别名
#include "chapt4_interfaces/srv/patrol.hpp"
using Patrol=chapt4_interfaces::srv::Patrol;
【TurtleController类里】
public:
//创建服务
patrol_server_ = this->create_service<Patrol>(
"patrol",
//lambda函数作为回调函数:参数为请求和响应对象的共享指针
[&](const std::shared_ptr<Patrol::Request> request,
std::shared_ptr<Patrol::Response> response) -> void{
//判断巡逻点是否在模拟器边界内
if((0 < request->target_x && request->target_x < 12.0f)
&& (0 < request->target_y && request->target_y < 12.0f)){
//设置目标点
target_x_ = request -> target_x;
target_y_ = request -> target_y;
response -> result = Patrol::Response::SUCCESS;
}
else{
response -> result = Patrol::Response::FAIL;
}
});
private:
//服务:Patrol类型服务共享指针
rclcpp::Service<Patrol>::SharedPtr patrol_server_;
CMakeList.txt内注册:不要忘记install
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(chapt4_interfaces REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)
add_executable(turtle_control_two src/turtle_control.cpp)
ament_target_dependencies(turtle_control_two rclcpp geometry_msgs turtlesim chapt4_interfaces)
install(TARGETS turtle_control_two
DESTINATION lib/${PROJECT_NAME})
运行
【先打开海龟模拟器】
->【运行代码】
$ ros2 run demo_cpp_service turtle_control_two
【启动服务】
查看服务
ros2 service list
->source找消息接口
【请求服务】
ros2 service call /patrol chapt4_interfaces/srv/Patrol "{target_x: 10.0,target_y: 10.0}"
客户端:随机生成目标点,并请求服务端处理
#include<cstdlib>
#include<ctime>
#include"rclcpp/rclcpp.hpp"
#include"chapt4_interfaces/srv/patrol.hpp"
#include<chrono>
using namespace std::chrono_literals;
using Patrol=chapt4_interfaces::srv::Patrol;
class PatrolClient : public rclcpp::Node{
public:
PatrolClient() : Node("patrol_cilent"){
patrol_client_ = this->create_client<Patrol>("patrol");
timer_=this->create_wall_timer(10s,std::bind(&PatrolClient::timer_callback,this));
//定时请求服务端->每10秒一次
srand(time(NULL));
}
//实现函数
void timer_callback(){
//TODO 生成随机目标点,请求服务端
//1.等待服务端上线 👇超过指定时间
while(!patrol_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<Patrol::Request>();
request->target_x = rand() % 15;
request->target_y = rand() % 15;
RCLCPP_INFO(this->get_logger(),"请求巡逻:(%f,%f)",request->target_x,request->target_y);
//3.发送异步请求,然后等待返回,返回时调用回调函数(用lambda写)
patrol_client_->async_send_request(
request, // ↓ 获取异步处理的结果
[&](rclcpp::Client<Patrol>::SharedFuture result_future) -> void{
auto response = result_future.get();//获取Response共享指针
if(response -> result == Patrol::Response::SUCCESS){
RCLCPP_INFO(this->get_logger(),"目标点处理成功");
}
else if(response -> result == Patrol::Response::FAIL){
RCLCPP_INFO(this->get_logger(),"目标点处理失败");
}
});
}
private:
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Client<Patrol>::SharedPtr patrol_client_;
};
int main(int argc,char **argv){
rclcpp::init(argc,argv);
auto node=std::make_shared<PatrolClient>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
【参数通信】
声明和设置参数
TurtleController() : Node("turtle_controller"){
//声明和获取参数初始值
this->declare_parameter("k",1.0);//声明参数
this->declare_parameter("max_speed",1.0);
this->get_parameter("k",k_);//获取当前参数值
this->get_parameter("max_speed",max_speed_);
}
接受参数事件:服务端
//导入消息接口:返回参数设置结果
#include "rcl_interfaces/msg/set_parameters_result.hpp"
using SetParametersResult=rcl_interfaces::msg::SetParametersResult;
TurtleController() : Node("turtle_controller"){
...
//添加参数设置回调
parameters_callback_handle_=this->add_on_set_parameters_callback(
//添加回调函数即可
[&](const std::vector<rclcpp::Parameter> ¶ms)->SetParametersResult{//注意这里传入数组->静态引入
for(auto param : params){
RCLCPP_INFO(this->get_logger(),"更新参数%s值为:%f",param.get_name().c_str(),
param.as_double());
if(param.get_name() == "k"){
k_ = param.as_double();
}
else if(param.get_name() == "max_speed"){
max_speed_ = param.as_double();
}
}
auto result = SetParametersResult();
result.successful=true;//都是内置参数
return result;
});
}
【代码改变参数】
this->set_parameter(rclcpp::Parameter("k",2.0));
修改其他节点的参数:客户端
【请求服务】
#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"
using SetP=rcl_interfaces::srv::SetParameters;//消息与服务相关接口
【发送参数请求并接收响应】
std::shared_ptr<SetP::Response> call_set_parameters(rcl_interfaces::msg::Parameter ¶meter){
//创建客户端等待服务上线
auto param_client=this->create_client<SetP>("turtle_controller/set_parameters");
while(!param_client->wait_for_service(std::chrono::seconds(1))){
if(!rclcpp::ok()){
RCLCPP_ERROR(this->get_logger(),"等待服务的过程中被打断...");
return nullptr;
}
RCLCPP_INFO(this->get_logger(),"等待参数设置服务端上线中");
}
//创建请求对象->等待服务上线
auto request=std::make_shared<SetP::Request>();
//parameters是内置数组
request->parameters.push_back(parameter);
//异步调用,等待并返回响应结果
auto future=param_client->async_send_request(request);
rclcpp::spin_until_future_complete(this->get_node_base_interface(),future);
auto response=future.get();
return response;
}
【发送请求更新参数k】
void update_server_param_k(double k){
//创建参数对象
auto param=rcl_interfaces::msg::Parameter();
param.name="k";
//创建参数值对象并赋值
auto param_value=rcl_interfaces::msg::ParameterValue();
param_value.type=rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
param_value.double_value = k;
param.value=param_value; //更新到原param里
//请求更新参数并处理
auto response=call_set_parameters(param);
if(response == nullptr){
RCLCPP_WARN(this->get_logger(),"参数修改失败");
return;
}
else{
for(auto result : response->results){
if(result.successful){
RCLCPP_INFO(this->get_logger(),"参数k已修改为:%f",k);
}
else{
RCLCPP_WARN(this->get_logger(),"参数k 失败 原因:%s",result.reason.c_str());
}
}
}
}
在main函数内调用
node->update_server_param_k(1.5);