Ros2_通信机制
一、通信模型
1、话题通信
2、服务通信
3、动作通信
4、参数服务
二、话题通信
1、话题通信核心实现
1-1 、C++实现
发布方
1-1.创建发布方;
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
API:(创建发布方对象)
create_publisher ()
/*
模板: std_msgs::msg::String(被发布的消息类型 )
参数:
1、话题名称
2、Qos(消息队列长度)
返回值:
发布对象指针(rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_😉
*/
1-2.创建定时器;
timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));
API: 创建定时器
create_wall_timer ()
/*
参数:
1、时间间隔(此处可以使用using namespace std::chrono_literals)
2、CallBack(回调函数)
返回值:
定时器对象指针(rclcpp::TimeBase::SharedPtr timer_)
*/
1-3.组织消息并发布。
publisher_->publish(message);
API:发布消息
publish()
/*
参数:
1、发布的消息
*/
订阅者
1-1.创建订阅方;
subscription_ = this->create_subscription<std_msgs::msg::String>("topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
API:(创建订阅者)
create_subscription()
/*
模板:std_msgs::msg::String (消息类型)
参数:
1、话题名称
2、Qos(消息队列长度)
3、CallBack(回调函数(对订阅的消息进行处理))
返回值:订阅对象指针( rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;)
*/
1-2.处理订阅到的消息。
回调函数(void topic_callback(const std_msgs::msg::String & msg))
1-2、Python实现
发布方
1.创建发布方
self.publisher_ = rclpy.create_publiser(String, 'topic', 10)
API: (创建发布方)
create_publisher()
/*
参数:
1、消息类型
2、话题名称
3、Qos(消息队列长度)
返回值:self.publisher_ (发布对象)
*/
2.创建定时器;
self.timer = self.create_timer(timer_period, self.timer_callback)
API: (创建定时器)
create_timer ()
/*
参数:
1、时间间隔
2、回调函数(处理订阅到的消息)
*/
3.组织消息并发布。
API:回调函数(
self.publisher_.publish(message)
)
publish()
/*
参数:
1、订阅到的消息
*/
订阅者
1.创建订阅方;
self.subscription = self.create_subscription(String,'topic',self.listener_callback,10)
API:(创建订阅者)
create_subscripition ()
/*
参数:
1、消息类型
2、话题名称
3、CallBack(回调函数)
4、Qos(消息队列长度)
返回值:self.subscripition(订阅对象)
*/
2.处理订阅到的消息。
listener_callbak(self,msg)
/*
处理订阅到的消息
*/
1-3、自定义消息
1、新建msg文件夹
2、配置package.xml文件
编译依赖
<build_depend>rosidl_default_generators</build_depend>
执行依赖
<exec_depend>rosidl_default_runtime</exec_depend>
声明当前包所属的功能包组
<member_of_group>rosidl_interface_packages</member_of_group>
3、配置CMakeLists.txt文件
find_package(rosidl_default_generators REQUIRED)
为接口文件生成源码
rosidl_generate_interfaces(${PROJECT_NAME}
“msg/Student.msg”
)
C++文件包含自定义消息头文件时需要配置Vscode中俄c__cpp_properties.json文件,在文件中的includePath属性下添加一行:
“{workspaceFolder}/install/base_interfaces_demo/include/**”
三、服务通信
1、服务通信核心实现
适用于对实时性有要求,具有一定逻辑处理的应用场景中
1-1、C++服务端
1.包含头文件
#incldue “rclcpp/rclcpp.hpp”
#include “服务接口消息.hpp”
Tips:
using和using namespace的区别:
using用于引入某个命名空间中的特定名称,而using namespace则是用于引入整个命名空间的内容。
2.创建服务端
server = this->create_service<AddInts>("add_ints",std::bind(&MinimalService::add, this, _1, _2));
API:创建服务端
create_service()
/*
模板:服务接口类型
参数:
1、服务话题
2、回调函数
返回值:服务对象指针( rclcpp::Service::SharedPtr server;)
*/
3.处理请求数据并响应结果
void add(const AddInts::Request::SharedPtr req,const AddInts::Response::SharedPtr res)
回调函数中有两个内置参数,一个是AddInts::Request,一个是AddInts::Response。
*/
Tips:
可以使用命令测试服务端:
ros2 service call 服务话题(/add_ints) 声明提交数据的类型(base_interfaces_demo/srv/AddInts) 提交的数据(“{‘num1’:10,‘num2’:30}”)
1-2、C++客户端
1.包含头文件;
#include “rclcpp/rclcpp.hpp”
#include “base_interfaces_demo/srv/add_ints.hpp”
2、定义节点类
2-1.创建客户端;
rclcpp::Client<AddInts>::SharedPtr client;
client = this->create_client<AddInts>("add_ints");
API:创建客户端
create_client<服务接口>(服务话题名称)
/*
模板:服务接口消息类型
参数:服务话题名称
返回值:客户端对象指针
*/2-2.等待服务连接;(对于服务通信而言,如果客户端连接不到服务器,那么不能发送请求)
API:等待服务器连接
wait_for_service(1s)
/*
参数:
1、等待时间
*/2-3.组织请求数据并发送;
rclcpp::Client::FutureAndRequest send_request(int_32 num1,int_32 num2)
{
auto request = std::make_shared<AddInts::Request>();
request.num1 = num1;
request.num2 = num2;
return client->async_send_request(request);
}
API:异步发送数据
async_send_request()
/*
参数:
1、消息变量request
/*
3.创建对象指针调用其功能,并处理响应;
此时不需要使用spin函数(服务通信机制是一问一答的,当响应请求后,客户端不需要一直挂起,所以这里直接创建一个客户端对象即可)
auto client = std::make_shared<类名>();
3-1.需要调用连接服务的函数,然后根据链接结果做下一步处理
bool flag = client->connect_server()
3-2.连接服务后,调用请求提交函数并处理响应结果
auto future->send_request(atoi(argv[1]),atoi(argv[2]));
3-3.处理响应
rclcpp::spin_until_future_complete(client,response) == rclcpp::FutureReturnCode::SUCCESS
判断future的一个状态码
/*
参数:
1、当前节点指针
2、future指针
*/
Python服务端核心实现
“”"
1.导包;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建服务端;
3-2.处理请求数据并响应结果。
4.调用spin函数,并传入节点对象;
5.释放资源。
“”"
** 1.导包**
import rclpy
from rclpy.node import Node
from base_interfaces_demo.srv import AddInts
3.定义节点类
3-1.创建服务端
self.srv = self.create_service(AddInts, 'add_ints', self.add_two_ints_callback)
API:创建服务端
create_service()
/*
参数:
1、服务接口消息类型
2、服务话题名称
3、回调函数
*/
3-2.处理请求数据并响应结果
回调函数
def add(self,request,response}
该函数主要用于处理请求并产生响应
Python客户端核心实现
“”
1.导包;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建客户端;
3-2.等待服务连接;
3-3.组织请求数据并发送;
4.创建对象调用其功能,处理响应结果;
5.释放资源。
“”
1、导包
import sys import rclpy from rclpy.node import Node from base_interfaces_demo.srv import AddInts from rcpy.logging import get_logger//日志
3.定义节点类
3-1.创建客户端
self.client = self.create_client(AddInts,"add_ints")
API:创建客户端
create_client()
/*
参数:
1、服务接口类型
2、服务话题名称
*/
3-2.等待服务连接
self.client.wait_for_service(timeout_sec=1.0)
API:等待服务连接
wait_for_service()
/*
参数:
1、等待超时时间
/
3-3.组织请求数据并发送
自定义异步发送服务请求函数
def send_request(self):
self.req = AddInts.Request()
self.req.num1 = int(sys.argv[1])
self.req.num2 = int(sys.argv[2])
self.future = self.cli.call_async(self.req)
API:异步发送服务请求
call_async()
/
参数:
1、服务请求对象 req*/
4.创建对象调用其功能,处理响应结果
4-1.发送请求
client = AddIntsClient() clent.send_request()
4-2.处理响应
rclpy.spin_until_future_complete(minimal_client,minimal_client.future)
API:等待异步操作完成
spin_until_future_cmplete(client,future)
/*
参数:
1、当前客户端对象
2、future 异步操作的结果
*/rclpy.spin_until_future_complete(minimal_client,minimal_client.future) try: response = minimal_client.future.result()//返回响应对象 except Exception as e: minimal_client.get_logger().info('服务请求失败:%r' % (e,) else: minimal_client.get_logger().info( '响应结果:%d + %d = %d' % (minimal_client.req.num1, minimal_client.req.num2, response.sum)
四、动作通信
动作通信适用于长时间运行任务,结构上由目标、反馈、结果三部分构成。目标发送和结果获取都是对服务通信的封装,连续反馈是对话题通信的封装。
1、自定义动作通信接口消息
1-1.编辑配置文件
package.xml:
action构建工具依赖
<buildtool_depend>rosidl_default_generators</buildtool_depend>
这个是<build_depend>、<exec_depend>、<export_depend>三者的集成
<depend>action_msgs</depend>
声明当前包所属的功能包组
<member_of_group>rosidl_interface_packages</member_of_group>
CMakeList.txt:
find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME}"action/Progress.action")
2、动作通信服务端C++核心实现
/*
1.包含头文件;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建动作服务端;
3-2.处理提交的目标值(回调函数);
3-3.生成连续反馈与最终响应(回调函数);
3-4.处理取消任务请求(回调函数)。
4.调用spin函数,并传入节点对象指针;
5.释放资源。
*/
1、包含头文件
#include "rclcpp/rclcpp.hpp" #include "rclcpp_action/rclcpp_action.hpp"//动作通信需要用到的头文件 #include "base_interfaces_demo/action/progress.hpp"//消息类型头文件
3、定义节点类
3-1.创建动作服务端
rclcpp_action::create_server<Progress>( this, "get_sum", std::bind(&ActionServer::handle_goal,this,_1,_2), std::bind(&ActionServer::handle_cancel,this,_1), std::bind(&ActionServer::handle_accepted,this,_1) );
API:创建动作服务端
create_server()
/*
模板:动作服务接口消息类型
参数:
1、当前节点
2、动作服务话题名称
3、提交目标处理函数 (回调函数)
4、取消任务请求处理函数(回调函数)
5、生成连续反馈处理函数(回调函数)
返回值:
动作消息接口类型的指针对象
*/
3-2.处理提交的目标值rclcpp_action::GoalResponse handle_goal(const rclcpp_action::GoalUUID & uuid,std::shared_ptr<const Progress::Goal> goal)
参数:
1、rclcpp_action::GoalUUID :一个全局唯一标识符(UUID),用于标识特定的目标请求。这可以用于在需要时查找或识别目标。
2、Progress::Goal类型的智能指针 :一个指向目标请求的共享指针
返回值类型:rclcpp_action::GoalResponse类型
Tips:
1、没有用到的成员变量可以用void()描述,例:void(uuid)
2、一般在rclcpp_action::GoalResponse下存在三个选项:rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE rclcpp_action::GoalResponse::ACCEPT_AND_DEFER rclcpp_action::GoalResponse::REJECT
3-3.处理取消任务请求的函数
rclcpp_action::CancelResponse handle_cancel(const std::shared_ptr<rclcpp_action::ServerGoalHandle<Progress>> goal_handle)
参数:
1、const std::shared_ptr<ServerGoalHandle> :表示指向ServerGoalHandle对象的共享指针
返回值:rclcpp_action::CancelResponseTips:
1、一般在rclcpp_action::CancelResponse下存在两个选项:rclcpp_action::CancelResponse::ACCEPT rclcpp_action::CanceResponse::REJECT
3-4.生成连续反馈与最终响应
std::thread(std::bind(&ActionServer::execute,this,goal_handle)).detach()
一般处理主逻辑都比较耗时,所以此处用到了子线程
std::thread(执行体).detach()-------匿名线程
回调函数execute实现:3-4-1.初始化goal、feedback、result
//从handle_goal上获取目标信息 const auto goal = goal_handle->get_goal(); //创建一个进度反馈信息的共享指针 auto feedback = std::make_shared<Progress::Feedback>(); //创建一个结果信息的共享指针 auto result = std::make_shared<Progress::Result>();
3-4-1.生成连续反馈返回给客户端
feedback->progress = (double_t)i / goal->num; goal_handle->publish_feedback(feedback)
3-4-2.生成最终响应结果
result->sum = sum; goal_andle->succeed(result);
API(扩展):
1、goal_handle->get_goal()
;获取目标值
2、rclcpp::Rate rate(1.0);rate.sleep()
;设置休眠时间
3、goal_handle->is_canceling()
;判断是否正在取消请求
4、goal_handle->canceled()
;通知任务被取消并传递结果对象
3.动作通信客户端C++核心实现
/*
1.包含头文件;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建动作客户端;
3-2.发送请求;
3-3.处理目标发送后的反馈(回调函数);
3-4.处理连续反馈(回调函数);
3-5.处理最终响应(回调函数)。
4.调用spin函数,并传入节点对象指针;
5.释放资源。
*/
1.包含头文件
#include "rclcpp/rclcpp.hpp" #include "rclcpp_ation/rclcpp.hpp" #include "base_interfaces_demo/action/progress.hpp"
3.定义节点类
3-1.创建动作客户端
rclcpp_action::Client<Progress>::SharedPtr client_ptr_; this->client_ptr_ = rclcpp_action::create_client<Progress>(this,"get_sum");
API:创建动作客户端
create_client()
/*
模板:动作通信接口
参数:
1、当前节点对象
2、动作通信话题名称
返回值:动作通信客户端对象指针
*/
3-2.发送请求
主函数调用成员函数send_goal。
send_goal实现:3-2-1.连接服务器
this->clent_ptr_->wait_for_action_server(std::chrono::seconds(10))
API:等待服务器连接
wait_for_action_server()
/*
参数:
1、超时时间
返回值:bool值,连接成功返回True,失败则返回False。
*/
3-2-2.发送具体请求//设置goal_msg auto goal_msg = Progress::Goal(); goal_msg.num = num;//提交目标值 //设置options rclcpp_action::Client<Progress>::SendGoalOptions options; options.goal_response_callback =std::bind(&MinimalActionClient::goal_response_callback, this, _1); options.feedback_callback =std::bind(&MinimalActionClient::feedback_callback, this, _1, _2); options.result_callback =std::bind(&MinimalActionClient::result_callback, this, _1); //发送异步请求 client_->async_send_goal(goal_msg,options),
API:异步发送请求
asyn_send_goal(参数1,参数2)
/*
参数:
1、const base_interfaces-demo::action::Progress::Goal &goal;自定义的动作接口消息类型下的目标
2、const rclcpp_action::Client<base_interfaes_demo::action::Progress>::SendGoalOptions &options;发送目标选项,设置需要处理的回调函数(1、处理目标值的服务响应2、处理连续反馈3、处理最终响应)
*/1.处理目标发送后的反馈(回调函数)
/*
参数:
1、指向ClientGoalHandle类型的实例的智能指针,它管理目标的生命周期,并提供接口来查询目标状态、取消目标、获取结果等。
*/void goal_response_callback(rclcpp::action::ClientGoaHandle<Progress>::SharedPtr goal_handle) { //客户端首先发送一个目标值给服务端,服务端进行判断看目标值是否可以被接受,然后响应给客户端,如果可处理的话,goal_handle里面是由内容的,如果不可处理的话,goal_handle是个空指针。 if(!goal_handle)//goal_handle如果是个空指针 //目标请求被服务端拒绝,否则被接受 }
2.处理连续反馈(回调函数)
/*
参数:
1、这是个智能指针,用于管理特定目标(goal)的生命周期和状态。
2、指向反馈消息的智能指针。
*/void feedback_callback(rclcpp_action::ClientGoalHandle<Progress>::SharedPtr goal_handle,const std::shared_ptr<Progress::Feedback> feedbak) { //通过feedback获取反馈内容 int_32_t progress = (int_32_t)(feedback->progress * 100) }
3.处理最终响应(回调函数)
/*
参数:
1、通常包含一个布尔值表示目标是否成功完成,以及一个指向 Result 消息类型的共享指针,该消息包含了动作服务器返回的具体结果数据。
*/void result_callback(const rclcpp_action::ClientGoalHandle<Progress>::WrappedResult &result) { //result.code //通过状态码判断最终结果状态 if(result.code == rclcpp_action::ResultCode::SUCCEEDED) { //成功 } else if(result.code == rclcpp_action::ResultCode::ABORTED) { //被中断 } else if(result.code == rclcpp_action::ResultCode::CANCELED) { //被取消 }else { //未知异常 } }
3、动作通信服务端Python核心实现
“”"
1.导包;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建动作服务端;
3-2.生成连续反馈;
3-3.生成最终响应。
4.调用spin函数,并传入节点对象;
5.释放资源。
“”"
1.导包
import rclpy from rclpy.action import ActionServer from rclpy.node import Node from base_interfaces_demo.action import Progress
3.定义节点类
Tips:
其中处理请求数据实现和处理取消任务请求实现都有默认函数,可根据需求进行重写实现。
3-1.创建动作服务端
ActionServer(node,action_type,action_name,execute_callback)
API:创建动作通信服务端
ActionServer()
/*
参数:
1、ActionServer创建时所依赖的节点(node)
2、动作通信接口类型(action_type)
3、将服务端和客户端关联在一起的话题名称(action_name)
4、进行主逻辑的回调函数(execute_callback)
返回值:
*/
self._action_server = ActionServer(
self,
Progress,
‘get_sum’,
self.execute_callback
)
3-2.生成连续反馈并生成最终响应
goal_handle的类型是ServerGoalHandle类型的,里面包含了各种方法。def exeute_callback(self,goal_handle) { ***3-2-1.生成连续反馈*** feedback_msg = Progress.Feedback() feedback_msg.progress = i / goal_handle.request.num goal_handle.publish_feedback(feedback_msg) ***3-2-2.响应最终结果*** goal_handle.succeed() result = Progress.Result() return result }
4.动作通信客户端Python核心实现
“”"
1.导包;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.创建动作客户端;
3-2.发送请求;
3-3.处理目标发送后的反馈;
3-4.处理连续反馈;
3-5.处理最终响应。
4.调用spin函数,并传入节点对象;
5.释放资源。
“”"
1.导包
import rclpy from rclpy.action import ActionClient from rclpy.node import Node from base_interfaces_demo.action import Progress
3.定义节点类
3-1.创建动作客户端
self._action_client = ActionClient(self,Progress,'get_sum')
API:创建动作客户端
ActionClient(node,action_type,action_name)
/*
参数:
1、构建ActionClient时所依赖的节点
2、动作通信服务接口
3、动作服务端和客户端关联的话题
*/3-2.发送请求
goal = Progress.Goal() goal.num = num self.aaction_client.wait_for_server() self.future = self.action_client.send_goal_async(goal,self.fb_callback) self.future.add_done_callback(self.goal_response_callback)
3-2-1.等待连接服务器
self.action_client.wait_for_server()//一直等待服务端连接
API:等待服务端连接
wait_for_server(timeout)
/*
参数:
1、超时时间
*/3-2-2.发送具体请求
API:异步发送目标请求
send_goal_async(goal_msg,feed_callback)
/*
1、目标数据goal
2、回调函数feed_callback
返回值:返回一个ClientGoalHandle类型的Future
对象
/
API:添加一个回调函数
add_done_callback(callback)
/
这是future下的函数,也就是ClientGoalHandle类下的成员函数
参数:
1、回调函数
*/
3-3.处理目标发送后的反馈
def goal_response_callback(self,future)
3-3-1.获取目标句柄
API:用于异步获取并返回操作结果,有阻塞行为
future.result()
goal_handle = future.result()
3-3-2.判断目标是否被服务器正常接收
API:goal_handle对象的一个属性,用于检查目标是否被action server接受。
goal_handle.accepted()
API:用于异步获取目标的最终结果,没有阻塞行为,返回一个Future对象,判断结果是否可用
get_result_async()
if not goal_handle.accepted(): return self.result_future = goal_handle.get_result_async() self.result_future.add_done_callback(self.get_result_callback)
3-5.处理最终响应
def get_result_callback(self.future): result = future.result().result
3-4.处理连续反馈
连续反馈的回调函数:
//通过fb_msg获取到连续反馈回来的数据 def fb_callback(self,fb_msg): progress = fb_msg.feedback.progress
五、参数服务通信
参数服务是以共享的方式实现不同节点之间数据交互的一种通信模式
1.参数服务API实现
常用C++API:
1、创建参数服务对象
rclcpp::Parameter p1("car_name","Tiger");//参数值为字符串类型
rclcpp::Parameter p2("width",0.15); //参数值为浮点类型
2、获取参数值
as_string().c_str()
as_double()
as_int()…
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"car_name = %s", p1.as_string().c_str());
3、获取参数的键
get_name().c_str()
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"p1 name = %s", p1.get_name().c_str());
4、获取参数的数据类型
get_type_name().c_str()
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"p1 type_name = %s", p1.get_type_name().c_str());
5、将参数值转换成字符串类型
value_to_string().c_str()
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"p1 value_to_msg = %s", p1.value_to_string().c_str());
PythonAPI:
1、创建参数对象
p1 = rclpy.Parameter("car_name",value="Horse")
2、获取参数值
get_logger("rclpy").info("car_name = %s" % p1.value)
3、获取参数键
get_logger("rclpy").info("p1 name = %s" % p1.name)
2.参数服务端C++核心实现
/*
需求:编写参数服务端,设置并操作参数。
步骤:
1.包含头文件;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.声明参数;
3-2.查询参数;
3-3.修改参数;
3-4.删除参数。
4.创建节点对象指针,调用参数操作函数,并传递给spin函数;
5.释放资源。
*/
1、包含头文件
#include "rclcpp/rclcpp.hpp"
3、定义节点类
需要设置 rclcpp::NodeOptions().allow_undeclared_parameters(true),否则非法
class MinimalParameterServer : public rclcpp::Node{
public:
MinimalParameterServer():Node("minimal_parameter_server_node",rclcpp::NodeOptions().allow_undeclared_parameters(true)){
}
private:
}
3-1.声明参数
API:声明参数
declar_parameter()
/*
参数:
1、键
2、值
eg:
this->declar_parameter("car_name","Tiger");
*/
3-2.查询参数
API:查询参数
get_parameter("键").as_string.c_str()----获取参数的值
eg:
RCLCPP_INFO(this->get_logger(),"height:%.2f",this->get_parameter("height").as_double());
API:判断是否包含该参数
has_parameter("键")-
eg:
RCLCPP_INFO(this->get_logger(),"包含car_type? %d",this->has_parameter("car_type"));
3-3.修改参数
API:修改参数
set_parameter(需要修改的键值对)
eg:
this->set_parameter(rclcpp::Parameter("键","值"));
3-4.删除参数
API:删除参数
undeclar_parameter("键")
eg:
this->undeclar_parameter("键");
3.参数客户端C++核心实现
/*
需求:编写参数客户端,获取或修改服务端参数。
步骤:
1.包含头文件;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.查询参数;
3-2.修改参数;
4.创建节点对象指针,调用参数操作函数;
5.释放资源。
*/
1、包含头文件
#include "rclcpp/rclcpp.hpp";
3、定义节点类
3-0.等待服务器连接
API:等待服务器连接
wait_for_service(超时时间)
eg:
>paramClient = std::make_shared<rclcpp::SyncParametersClient>(this,"minimal_param_server");
>while(!paramClient->wait_for_service(1s))
>{
>if(!rclcpp::ok())
>{
> return false;
>}
>RCLCPP_INFO(this->get_logger(),"服务未连接!");
>}
3-1.查询参数
API:查询参数
get_parameter("键")
3-2.修改参数
API:修改参数
set_parameters(rclcpp::Parameter("键","值"))
4、参数服务端Python核心实现
“”"
需求:编写参数服务端,设置并操作参数。
步骤:
1.导包;
2.初始化 ROS2 客户端;
3.定义节点类;
3-1.声明参数;
3-2.查询参数;
3-3.修改参数;
3-4.删除参数。
4.创建节点对象,调用参数操作函数,并传递给spin函数;
5.释放资源。
“”"
1、导包
import rclpy from rclpy.node import Node
3、定义节点类
需要设置allow_undeclared_parameters = True
eg:def __int__(self): super().__init__("minimal_parameter_server_node",allow_undeclared_parameters = True)
3-1.声明参数
API:声明参数
declar_parameter("Key","Value")
self.declar_parameter("car_type","Tiger")
3-2.查询参数
API:判断是否包含参数键
has_parameter("Key")----判断是否包含参数键
self.get_logger().info("包含 car_type 吗?%d" % self.has_parameter("car_type"))
API:获取指定参数键的值
get_parameter("Key")----获取指定参数键的值
car_type = self.get_parameter("car_type") self.get_logger().info("%s = %s " % (car_type.name, car_type.value))
API:获取所有参数键的值
get_parameters(["Key1","Key2","Key3"])----获取所有参数键的值
params = self.get_parameters(["car_type","height","wheels"]) self.get_logger().info("解析所有参数:") for param in params: self.get_logger().info("%s ---> %s" % (param.name, param.value))
3-3.修改参数
API:修改参数
set_parameters(rclpy.Parameter("Key","Value"))
self.set_parameters([rclpy.Parameter("car_type",value = "horse")])
3-4.删除参数
API:删除参数
undeclar_parameter("Key")
self.undeclare_parameter("car_type")
4-5、参数客户端Python核心实现
暂无!
通信机制补充
1、分布式
1-1.概念:
分布式通信是指可以通过网络在不同主机之间实现数据交互的一种通信策略
1-2.实现:
多机通信时通过域ID对节点进行分组,每组的不同节点之间可以自由通信,不同组之间的节点不可以进行通信
export ROS_DOMAIN_ID = 2
如果为当前设备下所有节点设置统一的域ID,可以执行以下指令:
echo "export ROS_DOMAIN_ID = 2" >> ~.bashrc
注意:1、ROS_DOMAIN_ID的值在[0,101]之间
2、每个域ID内的节点总数需要小于等于120个
3、当域ID =101时,该域内的节点总数需要小于等于54个。
1-3.DDS域ID的计算规则:
1、DDS基于TCP/IP和UDP/IP网络通信协议,网络通信需要端口号,一个端口号占两个字节,取值范围为[0,65535]
2、DDS协议规定以7400为起始端口,默认每个域ID占250个端口,故域ID的个数为(65535-7400)/250 = 232(个),对应的其取值范围为[0,231]
3、操作系统会预留一些端口,不同操作系统预留端口有差异:Linux系统下,域ID为[0,101]和[215,231];MAC和Windows可用域ID为:[0,166]
4、每个域ID默认占用250个端口,每个ROS2节点需要占用两个端口,按照DDS协议每个域ID的端口段内,第1、2个端口是Discovery Multicast端口与User Multicast端口,从第11、12个端口开始是域内第一个节点的Discovery Unicast端口与User Unicast,后续节点所占用端口依次顺延,那么一个域ID中的最大节点个数为:(250-10)/2 = 120(个);
5、特殊情况:域ID值为101时,其后半段端口属于操作系统的预留端口,其节点最大个数为54个。
>域 ID 与节点所占用端口示意:
Domain ID: 0
Participant ID: 0
Discovery Multicast Port: 7400
User Multicast Port: 7401
Discovery Unicast Port: 7410
User Unicast Port: 7411
---
Domain ID: 1
Participant ID: 2
Discovery Multicast Port: 7650
User Multicast Port: 7651
Discovery Unicast Port: 7664
User Unicast Port: 7665
2、工作空间覆盖
1、概念
不同工作空间存在重名功能包时,重名功能包的调用会出现覆盖的情况。
这种情况需要极力避免的。
2、原因:
这与~/.bashrc中不同工作空间的setup.bash文件的加载顺序有关
1、ROS2首先解析 ~/.bashrc文件并生成全局变量AMENT_PREFIX_PATH 与 PYTHONPATH
2、.两个变量的值的设置与 ~/.bashrc 中的 setup.bash 的配置顺序有关
3、调用功能包时,会按照 AMENT_PREFIX_PATH 或 PYTHONPATH 中包配置顺序从前往后依次查找相关功能包
3、元功能包
将不同的功能包打包成一个功能包,安装某个功能模块时,直接调用打包后的功能包(MetaPackage)
1、实现
ros2 pkg create 功能包
ros2 pkg create tutorails_plumbing
2、配置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>tutorails_plumbing</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ros2@todo.todo">ros2</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>base_interfaces_demo</exec_depend>
<exec_depend>cpp01_topic</exec_depend>
<exec_depend>cpp02_service</exec_depend>
<exec_depend>cpp03_action</exec_depend>
<exec_depend>cpp04_param</exec_depend>
<exec_depend>py01_topic</exec_depend>
<exec_depend>py02_service</exec_depend>
<exec_depend>py03_action</exec_depend>
<exec_depend>py04_param</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
3、配置CmakeList.txt
cmake_minimum_required(VERSION 3.8)
project(tutorails_plumbing)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif