本节沿用之前4.3 节小海龟控制例子。
4.5.1 参数声明与设置
打开src/demo_cpp_service/src/turtle_control.cpp文件
添加测试代码
cpp
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_);
重新构建后,运行节点
ros2 run demo_cpp_service turtle_control
查看参数列表
bohu@bohu-TM1701:~/chapt4/chapt4_ws$ ros2 param list
/turtle_controller:
k
max_speed
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
可以看到节点声明的参数了。还可以使用rqt软件直接修改
4.5.2 接受参数事件
上面设置参数只是修改ros2参数,节点的参数值没有修改,需要增加参数设置回调函数。
修改之前的turtle_control.cpp.
cpp
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "turtlesim/msg/pose.hpp"
#include "chapt4_interfaces/srv/patrol.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
using Patrol = chapt4_interfaces::srv::Patrol;
using SetParametersResult = rcl_interfaces::msg::SetParametersResult;
using namespace std;
class TurtleControlNode : public rclcpp::Node
{
public:
TurtleControlNode() : 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_);
paramter_callback_handle_ = this->add_on_set_parameters_callback(
[&](const 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;
});
patrol_service_ = this->create_service<Patrol>(
"patrol",[&](const shared_ptr<Patrol::Request> request,
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;
}
});
//调用继承来父类来创建发布者
publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel",10);
//订阅者
subscriber_ = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose",10,std::bind(&
TurtleControlNode::on_pose_received_,this,placeholders::_1));
}
private:
void on_pose_received_(const turtlesim::msg::Pose::SharedPtr pose) {
//1获取当前位置
auto current_x = pose->x;
auto current_y = pose->y;
RCLCPP_INFO(get_logger(),"当前:x=%f,y=%f",current_x,current_y);
//2计算与目标距离,以及当前海龟的角度差
auto distance = sqrt(
(target_x_-current_x)*(target_x_-current_x)+
(target_y_-current_y)*(target_y_-current_y)
);
auto angle = atan2((target_y_-current_y),(target_x_-current_x))-pose->theta;
//3控制策略:
auto msg = geometry_msgs::msg::Twist();
if(distance>0.1){
if(fabs(angle)>0.2)
{
msg.angular.z = fabs(angle);
}else{
msg.linear.x = k_*distance;
}
}
//4 限制最大速度
if(msg.linear.x>max_speed_){
msg.linear.x = max_speed_;
}
publisher_->publish(msg);
}
private:
OnSetParametersCallbackHandle::SharedPtr paramter_callback_handle_;
rclcpp::Service<Patrol>::SharedPtr patrol_service_;
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscriber_; //订阅者智能指针
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_; //发布者智能指针
double target_x_{1.0}; //目标位置X,
double target_y_{1.0}; 目标位置Y
double k_{1.0};//比例系数
double max_speed_{2.0}; //最大速度
};
int main(int argc,char *argv[])
{
rclcpp::init(argc,argv);
auto node = std::make_shared<TurtleControlNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
主要是增加回调函数add_on_set_parameters_callback
重新构建后运行节点,打开rqt 修改属性值
bohu@bohu-TM1701:~/chapt4/chapt4_ws$ ros2 run demo_cpp_service turtle_control
[INFO] [1736411168.111954645] [turtle_controller]: 更新参数的值k=2.000000
[INFO] [1736411195.539882142] [turtle_controller]: 更新参数的值k=3.000000
[INFO] [1736411207.634454220] [turtle_controller]: 更新参数的值max_speed=2.000000
4.5.3 修改其他节点的参数
参数机制是基于服务通信实现的,之前python节点验证过。先看下涉及SetParameters数据类型:
bohu@bohu-TM1701:~/chapt4/chapt4_ws$ ros2 interface show rcl_interfaces/srv/SetParameters
A list of parameters to set.
Parameter[] parameters
string name
ParameterValue value
uint8 type
bool bool_value
int64 integer_value
float64 double_value
string string_value
byte[] byte_array_value
bool[] bool_array_value
int64[] integer_array_value
float64[] double_array_value
string[] string_array_value
Indicates whether setting each parameter succeeded or not and why.
SetParametersResult[] results
bool successful
string reason
这也是为什么要引入相关头文件,修改后的patrol_client.cpp代码
cpp
#include <chrono>
#include <cstdlib>
#include <ctime>
#include "rclcpp/rclcpp.hpp"
#include "chapt4_interfaces/srv/patrol.hpp"
#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;
using Patrol = chapt4_interfaces::srv::Patrol;
using namespace std;
using namespace std::chrono_literals;//可以使用10s
class PatrolClient : public rclcpp::Node
{
public:
PatrolClient() : Node("patrol_client")
{
patrol_client_ = this->create_client<Patrol>("patrol");
timer_ = this->create_wall_timer(10s,[&]()->void{
//1检测服务端是否上线
while(!patrol_client_->wait_for_service(1s)){
RCLCPP_ERROR(this->get_logger(),"等待服务上线过程中,rclcpp异常,退出");
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 发送请求
this->patrol_client_->async_send_request(request,[&](rclcpp::Client<Patrol>::SharedFuture result_future)->void{
auto response = result_future.get();
if(response->result==Patrol::Response::SUCCESS){
RCLCPP_INFO(this->get_logger(),"请求目标点处理成功");
}else if(response->result==Patrol::Response::FAIL){
RCLCPP_INFO(this->get_logger(),"请求目标点处理失败");
}
});
});
}
/**发送请求 */
SetP::Response::SharedPtr call_set_parameters(const rcl_interfaces::msg::Parameter ¶m)
{
auto param_client_ = this->create_client<SetP>("/turtle_controller/set_parameters");
//1检测服务端是否上线
while(!patrol_client_->wait_for_service(1s)){
RCLCPP_ERROR(this->get_logger(),"等待服务上线过程中,rclcpp异常,退出");
return nullptr;
}
RCLCPP_INFO(this->get_logger(),"等待服务上线....");
//2 构造请求对象
auto request = std::make_shared<SetP::Request>();
request->parameters.push_back(param);//添加数据
//3 发送请求
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;
}
/**更新参数 */
void update_param_k(double k)
{
//1 创建请求参数
auto param = rcl_interfaces::msg::Parameter();
param.name = "k";
//2 创建请求参数值
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;
//3. 请求更新参数并处理
auto response = this->call_set_parameters(param);
if(response == NULL){
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(), "参数修改失败,reason:%s",result.reason.c_str());
}
}
}
}
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>();
node->update_param_k(4.0);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
增加了call_set_parameters、update_param_k 两个方法,在main增加update_param_k调用。
构建后启动服务端、与客户端。日志分别如下:
bohu@bohu-TM1701:~/chapt4/chapt4_ws$ ros2 run demo_cpp_service turtle_control
[INFO] [1736431217.835118418] [turtle_controller]: 更新参数的值k=4.000000
客户端:
bohu@bohu-TM1701:~/chapt4/chapt4_ws$ ros2 run demo_cpp_service patrol_client
[INFO] [1736431217.834569546] [patrol_client]: 等待服务上线....
[INFO] [1736431217.836297268] [patrol_client]: 参数K已经修改为:4.000000