IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 【系统开发基础储备】ROS参数加载、数据类型创建、通讯机制、定时器中断 (使用ROS的通讯机制、定时器和C++相关线程技术)(第三篇) -> 正文阅读

[人工智能]【系统开发基础储备】ROS参数加载、数据类型创建、通讯机制、定时器中断 (使用ROS的通讯机制、定时器和C++相关线程技术)(第三篇)

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
TODO:写完再整理

文章目录


前言

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!

本文先对** ROS通讯机制及定时器中断(使用ROS的通讯机制、定时器和C++相关线程技术) **做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章


提示:以下是本篇文章正文内容

一、参数加载方法(rosparam)

1.【.h文件】

用结构体实现,结构体写在一个共用的里面
参数数据在.h中进行了初始化赋值的!!!【防盗标记--盒子君hzj】

.

2.【.yaml文件】

用.yaml文件加载参数的方法
(1)在.yaml文件里面编写变量名、变量值、数据类型
(2)ROS和其他系统都有API(LoadFile)读取文件里面的数据
		 YAML::Node config = YAML::LoadFile("/root/config/pure_pursuit.yaml");

(3)通过config指令赋值参数
		velocity_source_ = config["velocity_source"].as<int>();

.

3.【.launch文件】

在.launch文件用param指令

.
.

二、ROS可使用的数据类型

1.【std_msgs】std自带提供的基础消息结构类型

Std_msgs是ros基础的数据类型结构,Std_msgs是std库里面定义的数据类型都有,包括int/double等等

(1)std_msgs定义了常用的编程数据结构类型,本质上上也是用.msg编译成.h使用的

(2)std_msgs数据类型定义在opt/ros/melodic/share/std_msgs/msg

(3)std_msg一般包含bool、char、int、float、double【防盗标记--盒子君hzj】

.

2.【.h结构体数据类型】自定义结构体.h文件

自己建一个文件夹使用.h文件用结构体的方法定义接口

用结构体实现,结构体写在一个共用的.h文件面,可供多个功能包使用

技巧:
现在的规划部分接口是一层套一层的,一般写接口都是先写一个简单的接口,【防盗标记--盒子君hzj】
不够用了在用一个大一点的接口,大的接口把小弟额接口的内容包含进去

.

3.【xxx_msg】(.msg、.srv、.action)ROS自定义消息结构类型

(1)原理

.msg文件统一放在一个msg文件夹里,ros通过message_generation、message_runtime将.msg文件和.srv文件自动转换成.h文件。用户只需定义简单的.msg和.srv文件即可,.msg、.srv、.action最终都会编译成.h文件(Ros话题的msg,srv,action文件都被cmakelist\package.xml编译成为.h文件),在源文件中使用这些消息类型的时候在源文件中include对应的.h文件

(2)参考链接

https://www.cnblogs.com/birdBull/p/14889990.html
【防盗标记–盒子君hzj】

(3)步骤

在这里插入图片描述
(1)catkin_ws下创建srv,msg文件夹,编写.msg/.srv文件

(1).msg、.srv文件含义:.msg文件用于定义ros话题消息,.srv文件用于定义ros服务消息
(2)【编写方法】现在的规划部分接口是一层套一层的,一般写接口都是先写一个简单的接口,不够用了在用一个大一点的接口,大的接口把小弟额接口的内容包含进去
注意
【防盗标记--盒子君hzj】
(1)接口类型的编写和调用要对应上
(2)学会去写接口的转换函数,用于接口的转换

(2)打开package.xml文件添加编译和执行依赖message_generation及message_runtime

(1)<build_depend>message_generation</build_depend>【添加消息内容生成编译依赖】
(2)<exec_depend>message_runtime</exec_depend>【添加该依赖的额外依赖】

(3)打开CMakeLists.txt文件。添加编译包依赖message_generation,添加运行依赖message_generation,添加.srv/.msg文件,添加生成消息函数generate_messages

(1)、find_packge(......message_generation)【找到这个依赖】
(2)、catkin_package(roscpp rospy message_runtime)【编译这个依赖】
(3)、add_message_file(FILES,my.msg)【添加我自己写的.msg文件】
(4)、generation_message(DEPENDENCIES std_msgs)【生成消息】
【防盗标记--盒子君hzj】

(4)调用自己定义的数据结构的方法

(1)在源文件包含.h文件
#include "std_msgs/String.h"//常用的变量形式
#include "std_msgs/XXX.h"//常用的变量形式
注意,用到哪一种数据类型就包含哪种数据类型

(2)类成员、接口是可以通过“.”进行跳转的
(1)用msg的数据类型定义相关对象:visualization_msgs XXX xxx
(2)对msg对象进行赋值:
		xxx.header = a;
		xxx.stamp = b;
		xxx.对象子元素 = c;

.
.

4.【common_msgs】ROS提供的组件通用消息结构类型

(1)具体用法参考wiki

【查看数据类型的元素强烈建议再wiki上查找–关系到使用方法】【防盗标记–盒子君hzj】
http://wiki.ros.org/visualization_msgs
.
.

(2)通用消息结构类型

header的数据类型:header的数据类型包括stamp、frame_id、seq

(1)【常用】geometry_msgs

(1)介绍
geometry_msgs为常见几何图元(如点、向量和姿势)提供消息【防盗标记–盒子君hzj】
https://www.cxyzjd.com/article/RainVictor1/79674863

.
(2)消息类型


(1)point【点集】
(1)Point
float64 x  ,float64 y,float64 z

(2)Point32
float32 x,float32 y,float32 z  一般使用Point,大规模点云使用Point32

(3)PointStamped
std_msgs/Header header 包含坐标系和时间戳信息  
geometry_msgs/Point point 点

(2)poses【几何–位姿集】
(1)Pose2D
	float64 x,float64 y,float64 theta

(2)Pose【防盗标记--盒子君hzj】
	(1)geometry_msgs/Point position 位置 
	(2)geometry_msgs/Quaternion orientation 姿态,即方向

(3)PoseStamped
	(1)std_msgs/Header header  
	(2)geometry_msgs/Pose pose

(4)PoseArray
	(1)std_msgs/Header header
	(2)geometry_msgs/Pose[] poses

(5)PoseWithCovariance【带有协方差的位姿】
	(1)geometry_msgs/Pose pose   即(x, y, z,  X 旋转, Y旋转,  Z旋转)
	(2)float64[36] covariance协方差

(6)PoseWithCovarianceStamped【带有协方差和时间戳的位姿】
	(1)std_msgs/Header header     
	(2)geometry_msgs/PoseWithCovariance pose
(3)Quaternion【四元素–位姿集】
(1)Quaternion
	float64 x,float64 y,float64 z,float64 w

(2)QuaternionStamped【防盗标记--盒子君hzj】
(4)Vector3【几何–矢量集】
(1)Vector3
	float64 x,float64 y,float64 z

(2)Vector3Stamped
(5)Twist【速度–矢量集】
(1)Twist 速度
	(1)geometry_msgs/Vector3 linear 线速度
	(2)geometry_msgs/Vector3 angular角速度

(2)TwistStamped
(3)TwistWithCovariance
(4)TwistWithCovarianceStamped
(6)Wrench【力–矢量集】
(1)Wrench
	(1)geometry_msgs/Vector3 force 力【防盗标记--盒子君hzj】
	(2)geometry_msgs/Vector3 torque 扭矩

(2)WrenchStamped
(7)Transform【坐标系之间的变换】
(1)Transform
	(1)geometry_msgs/Vector3 translation 平移向量
	(2)geometry_msgs/Quaternionrotation 旋转向量
(2)TransformStamped

.
.
.

(2)【常用】visualization_msgs

(1)介绍

    visualization_msgs是处理可视化特定数据的高级包(如rviz)使用的一组消息。visualization\u msgs中的主要消息是visualization\u msgs/Marker。标记消息用于将可视化“标记”(如框、球体、箭头、线等)发送到可视化环境(如rviz)
    rviz可视化的话题数据也是有一定的数据格式的,可以参考链接:http://www.ros.org/wiki/rviz/DisplayTypes

Visualization_msgs的数据类型可以直接赋值进行rviz可视化,其他的格式的数据类型要转换成Visualization_msgs的数据类型才能进行可视化

visualization_msgs的path数据类型在rviz中会默认连接起来,point数据类型在rviz中默认仅仅显示一个点【防盗标记--盒子君hzj】

(2)消息类型

(1)Grid

沿平面显示 2D 或 3D 网格

(2)Grid Cells

从网格中绘制单元格,通常是从导航堆栈中的代价地图中绘制障碍物
nav_msgs/GridCells

(3)Laser Scan

显示来自激光扫描的数据,具有渲染模式、累积等的不同选项【防盗标记–盒子君hzj】
sensor_msgs/LaserScan

(4)Point Cloud(2)

显示来自点云的数据,具有不同的渲染模式、累积等选项
sensor_msgs/PointCloud , sensor_msgs/PointCloud2

(5)Map

在地平面上显示地图
nav_msgs/OccupancyGrid

(6)Markers

允许程序员通过主题显示任意原始形状
visualization_msgs/Marker, visualization_msgs/MarkerArray

(7)Path

显示导航中的路径
nav_msgs/Path

(8)Point

将一个点绘制为一个小球体
geometry_msgs/PointStamped

(9)Pose

将姿势绘制为箭头或轴
geometry_msgs/PoseStamped

(10)Pose Array

绘制一个“云”箭头,一个用于姿势数组中的每个姿势
geometry_msgs/PoseArray

(11)Polygon

将多边形的轮廓绘制为线。
geometry_msgs/Polygon

(12)Odometry

随着时间的推移积累里程计姿势【防盗标记–盒子君hzj】
nav_msgs/Odometry

(13)Range

显示代表声纳或红外距离传感器距离测量值的锥体
sensor_msgs/Range

(14)RobotModel

显示处于正确姿势(由当前 TF 变换定义)的机器人的视觉表示

(15)TF

显示tf转换层次结构

(16)Axes

显示一组轴

(17)Effort

显示投入到机器人每个旋转关节上的力【防盗标记–盒子君hzj】
sensor_msgs/JointStates

(18)Camera

从相机的角度创建一个新的渲染窗口,并将图像叠加在它上面
sensor_msgs/Image , sensor_msgs/CameraInfo

(19)Image

创建一个带有图像的新渲染窗口
sensor_msgs/Image

(20)InteractiveMarker

显示来自一个或多个交互式标记服务器的 3D 对象,并允许鼠标??与它们交互
visualization_msgs/InteractiveMarker

(21)Wrench

将扳手绘制为箭头(力)和箭头 + 圆圈(扭矩)
geometry_msgs/WrenchStamped
.
.

(3)【常用】nav_msgs

(1)介绍
Nav_msgs是专门定义给navigation_stuck导航包使用的

(2)消息类型

(1)GridCells【网格阵列】
二维网格中的单元阵列
(1)float32 cell_width
(2)float32 cell_height
(3)geometry_msgs/Point[] cells
(2)MapMetaData【占据网格数据】
记录占据网格数据
(1)time map_load_time
(2)float32 resolution
(3)uint32 width
(4)uint32 height
(5)geometry_msgs/Pose origin
(3)OccupancyGrid【二维栅格地图】
表示二维网格图,其中每个单元表示占用的概率,在占据网格数据的基础上,添加概率信息
(1)std_msgs/Header header
(2)nav_msgs/MapMetaData info
(3)int8[] data
【防盗标记--盒子君hzj】
(4)Odometry【里程计】
对自由空间中的位置和速度的估计
(1)【时间戳】std_msgs/Header header
(2)【坐标系】string child_frame_id
(3)【位置】geometry_msgs/PoseWithCovariance pose
(4)【速度】geometry_msgs/TwistWithCovariance twist
(5)Path【路径】
机器人要跟随的路径的一组姿势
(1)std_msgs/Header header
(2)geometry_msgs/PoseStamped[] poses

.
.

(4)【常用】sensor_msgs

参考资源
http://wiki.ros.org/sensor_msgs?distro=melodic

(1)Joy相关
Joy
JoyFeedback
JoyFeedbackArray【防盗标记--盒子君hzj】
(2)IMU相关
Imu	
(3)Laser相关
LaserEcho
LaserScan
MultiEchoLaserScan
(4)Camera相关
Image
CameraInfo
CompressedImage
ChannelFloat32
(5)PointCloud相关
PointCloud
PointCloud2
PointField【防盗标记--盒子君hzj】
(6)Joint相关
JointState
MultiDOFJointState
(7)其他传感器
BatteryState
FluidPressure
Illuminance
MagneticField
NavSatFix
NavSatStatus
Range
RegionOfInterest
RelativeHumidity【防盗标记--盒子君hzj】
Temperature
TimeReference

.
.

(5)trajectory_msgs【机械臂轨迹】

(1)介绍
【常常用于机械臂的轨迹规划】此包定义用于定义机器人轨迹的消息

(2)消息类型

JointTrajectory
std_msgs/Header header
string[] joint_names
trajectory_msgs/JointTrajectoryPoint[] points
JointTrajectoryPoint
float64[] positions
float64[] velocities
float64[] accelerations【防盗标记--盒子君hzj】
float64[] effort
duration time_from_start
MultiDOFJointTrajectory
作用:
多自由度关节轨迹的表示(每个点是一个变换)沿轨迹的每个点将包括一个位置/速度/加速度数组,
该数组与关节名称数组具有相同的长度,
并且与关节名称数组具有相同的关节顺序。
std_msgs/Header header
string[] joint_names
trajectory_msgs/MultiDOFJointTrajectoryPoint[] points
MultiDOFJointTrajectoryPoint
geometry_msgs/Transform[] transforms
geometry_msgs/Twist[] velocities
geometry_msgs/Twist[] accelerations【防盗标记--盒子君hzj】
duration time_from_start

(6)actionlib_msgs

用于支持动作服务器的

(7)shape_msgs

(8)diagnostic_msgs

(9)diagnostic_msgs

(10)stereo_msgs

(3)组件通用消息结构类型用法

ROS已经集成实现了,学会调用就行
步骤:
(1)先include进来
(2)编程的时候直接定义,调用就行
(3)消息结构包含什么东西wiki查一查,或者直接通过指令rosmsg查一查【防盗标记--盒子君hzj】

5.【protobuf】使用谷歌推荐的protobuf定义数据接口

.
.
.

三、ROS通讯机制【话题通讯、服务通讯、动作通讯】

(话题时群聊,服务时私聊,动作是奴隶,编程时话题和服务的套路时一样的)hahaha

1.话题通讯编程步骤

(1)图例

在这里插入图片描述

(2)步骤

1、创建一个发布者

1、在功能包的src文件夹下新建一个空白文档如talker.cpp 
2、编写程序内容
(1)、初始化ROS节点
(2)、向ROS Master注册节点信息,包括发布的话题名字和话题中的消息类型
(3)、按照一定频率循环发布信息【防盗标记--盒子君hzj】

2、创建一个订阅者

1、在功能包的src文件夹下新建一个空白文档如listener.cpp 
2、编写程序内容
(1)初始化ROS节点
(2)订阅需要的话题和消息类型
(3)循环等待话题消息,接收到消息后进入回调函数【防盗标记--盒子君hzj】
(4)在回调函数中完成消息处理

3、添加编译选项并编译

(1)、打开在功能包的src文件夹下的Cmakelist.txt,Cmakelist.txt是配置具体的编译选项的
(2)、在文件中添加编译选项【很多代码在文件中是有的,但是被注释的了,通过删除注释,改掉里面的内容即可】
		1、add_executable(talker src/talker.cpp)【格式:add_executable(exe_name src/1.cpp 2.cpp .....n.cpp)】【【通过talker.cpp生成一个talker的可执行文件】】
		2、target_link_libraries(talker ${catkin_LIBRARIES})【【把可执行文件和第三方库做链接】】
		3、add_dependencies(exe_name$(PROJECT_NAME)_generate_messages_cpp)【【添加第三方依赖】】
(3)、回到工作空间的文件夹进行编译,编译完成后,在工作空间下devel文件夹里面有对应编译生成的可执行文件
注意:
1、【cpp代码要,py代码不用】
2、编译完成后会有两个对应的可执行文件生成,并存放在devel文件夹下【防盗标记--盒子君hzj】

4、运行可执行文件

1、启动roscore
2、分别执行可执行文件【rosrun 功能包名称 可执行文件名称】(rosrun learning_communication listener)
3、另外开一个终端,分别执行可执行文件rosrun learning_communication talker
到这里已经完成了话题通讯了

5、通过rqt查看运行的节点和内容$rqt_graph$rqt_plot【防盗标记–盒子君hzj】

.

(3)API

0.创建句柄【命名空间的目录】

(1)nodehandle句柄的作用
	句柄(Handle)这个概念可以理解为一个“把手”,你握住了门把手,就可以很容易把整扇门拉开,而不必关心门是什么样子。
	作用一:NodeHandle就是对节点资源的描述,有了它你就可以操作这个节点了,节点的句柄可以用来创建Publisher、Subscriber、访问和修改param以及做其他事情
	作用二:它提供了一个额外的层命名空间解决方案,可以使组件更容易写【防盗标记--盒子君hzj】

1.发布对象

(1)C++版本(推荐)

1)对象注册函数

(1)功能:在 ROS master 注册并返回一个发布者对象,该对象可以发布消息
(2)函数原型
		Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false)【防盗标记--盒子君hzj】
		* \param topic 发布消息使用的话题
		* \param queue_size 等待发送给订阅者的最大消息数量
		* \param latch (optional) 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
		* \return 调用成功时,会返回一个发布对象

(3)函数的调用
		ros::Publisher pub = handle.advertise<std_msgs::Empty>("my_topic", 1);
		
		pub.publish(msg):主函数中使用自定义或ROS的标准消息类型(message-type)定义具体的消息(message content),通过pub.publish(msg)发布话题

(2)消息发布函数

void publish(const M& message) const
(2)python版本

(1)对象获取函数

(1)功能:在 ROS master 注册并返回一个发布者对象,该对象可以发布消息【防盗标记--盒子君hzj】

(2)函数原型
		def __init__(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None, queue_size=None)
		        @param name: 话题名称 
		        @type  name: str
		        @param data_class: 消息类型
		
		        @param latch: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
		        @type  latch: bool
		
		        @param queue_size: 等待发送给订阅者的最大消息数量【防盗标记--盒子君hzj】
		        @type  queue_size: int

(2)消息发布函数

	def publish(self, *args, **kwds):
	        """
	        发布消息
	        """

2.订阅对象

(1)C++版本(推荐)

(1)对象获取函数

(1)功能:
该函数将根据给定的话题在ROS master 注册,并自动连接相同主题的发布方,每接收到一条消息,都会调用回调函数,并且传入该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使用该消息

(2)函数原型
		Subscriber subscribe(const std::string& topic, uint32_t queue_size, void(*fp)【防盗标记--盒子君hzj】
		* \param topic 订阅的话题
		* \param queue_size 消息队列长度,超出长度时,头部的消息将被弃用
		* \param fp 当订阅到一条消息时,需要执行的回调函数
		* \return 调用成功时,返回一个订阅者对象,失败时,返回空对象

(3)函数调用
ros::Subscriber sub = nodeHandle.subscribe("my_topic", 1, callback);

(2)订阅回调函数

void callback(const std_msgs::Empty::ConstPtr& message)
{
}
(2)python版本

(1)对象获取函数

(1)功能
该函数将根据给定的话题在ROS master 注册,并自动连接相同主题的发布方,每接收到一条消息,都会调用回调函数,并且传入该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使用该消息【防盗标记--盒子君hzj】

(2)函数原型
def __init__(self, name, data_class, callback=None, callback_args=None,queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False)
        @param name: 话题名称
        @type  name: str

        @param data_class: 消息类型
        @type  data_class: L{Message} class

        @param callback: 处理订阅到的消息的回调函数
        @type  callback: fn(msg, cb_args)

        @param queue_size: 消息队列长度,超出长度时,头部的消息将被弃用【防盗标记--盒子君hzj】

(2)订阅回调函数

(4)注意

发布器创建后,我想什么时候发布信息都可以,订阅器创建后,若订阅到话题后必须要进行处理

注意:话题通讯是有缓存的,若没有进行话题的发布和订阅,数据也会存在缓存里,但是触发不了订阅的回调函数用不起来而已

2.服务通讯编程步骤

(1)步骤

1、创建服务器

1、在功能包的src文件夹下新建一个空白文档如server.cpp【防盗标记--盒子君hzj】
 2、编写程序内容
(1)初始化ROS节点
(2)创建server的实例
(3)循环等待服务请求,接收到消息后进入回调函数
(4)在回调函数中完成消息处理

2、创建客户端

1、在功能包的src文件夹下新建一个空白文档如client.cpp 
2、编写程序内容
(1)初始化ROS节点
(2)创建client实例
(3)发布服务请求数据
(4)等待server处理后返回的结果

3、添加编译选项并编译

【在cmakellist.txt】
1、编译代码生成可执行程序【add_executable(server src/server.cpp)】
2、设置链接库【target_link_libraries(talker ${catkin_LIBRARIES})】
3、设置依赖【add_dependencies(exe_name (PROJECT_NAME)_gencpp)】【防盗标记--盒子君hzj】
4、回到工作空间进行编译【编译完成后会有两个对应的可执行文件生成,并存放在devel文件夹下】

4、运行可执行文件

(1)启动roscore
(2)启动服务端【如rosrun learning communication server】【roscore 元功能包 可执行文件】
(3)启动客户端【如rosrun learning communication client】【roscore 元功能包 可执行文件】【并在客户端终端上输入参数】

(2)API

1.创建句柄【命名空间的目录】

(1)nodehandle句柄的作用
	句柄(Handle)这个概念可以理解为一个“把手”,你握住了门把手,就可以很容易把整扇门拉开,而不必关心门是什么样子。
	作用一:NodeHandle就是对节点资源的描述,有了它你就可以操作这个节点了,节点的句柄可以用来创建Publisher、Subscriber、访问和修改param以及做其他事情
	作用二:它提供了一个额外的层命名空间解决方案,可以使组件更容易写

2.服务对象

(1)C++版本(推荐)

(1)对象创建函数

(1)功能
该函数可以连接到 ROS master,并提供一个具有给定名称的服务对象

(2)函数原型
		ServiceServer advertiseService(const std::string& service, bool(*srv_func)【防盗标记--盒子君hzj】
		* \param service 服务的主题名称
		* \param srv_func 接收到请求时,需要处理请求的回调函数
		
(3)函数调用
		ros::ServiceServer service = handle.advertiseService("my_service", callback);

(2)服务回调函数

bool callback(std_srvs::Empty& request, std_srvs::Empty& response)
{
return true;
}
(2)python版本

(1)对象获取函数

(1)功能
该函数可以连接到 ROS master,并提供一个具有给定名称的服务对象【防盗标记--盒子君hzj】

(2)函数原型
		def __init__(self, name, service_class,handler,buff_size=DEFAULT_BUFF_SIZE, error_handler=None)
		        @param name: 服务主题名称 ``str``
		        @param service_class:服务消息类型
		
		        @param handler: 回调函数,处理请求数据,并返回响应数据
		
		        @type  handler: fn(req)->resp

(3)函数调用		       
		s = Service('getmapservice', GetMap, get_map_handler)

(2)服务回调函数

~

3.客户端对象

(1)C++版本(推荐)

(1)对象获取函数

(1)功能:创建一个服务客户端对象
(2)函数原型
ServiceClient serviceClient(const std::string& service_name, bool persistent = false, const M_string& header_values = M_string())
*param service_name 服务主题名称

(2)请求发送函数

bool call(Service& service)
* 返回值为 bool 类型,true,请求处理成功,false,处理失败。【防盗标记--盒子君hzj】

(3)等待服务函数1

bool waitForService(const std::string& service_name, ros::Duration timeout = ros::Duration(-1));
* ros::service::waitForService("addInts");
 * \brief 等待服务可用,否则一致处于阻塞状态
 * \param service_name 被"等待"的服务的话题名称
 * \param timeout 等待最大时常,默认为 -1,可以永久等待直至节点关闭
 * \return 成功返回 true,否则返回 false。

(4)等待服务函数2

bool waitForExistence(ros::Duration timeout = ros::Duration(-1));
* client.waitForExistence();
* \brief 等待服务可用,否则一致处于阻塞状态
* \param timeout 等待最大时常,默认为 -1,可以永久等待直至节点关闭【防盗标记--盒子君hzj】
* \return 成功返回 true,否则返回 false。
(2)python版本

(1)对象获取函数

(1)功能:创建一个服务客户端对象
(2)函数原型
		def __init__(self, name, service_class, persistent=False, headers=None):
		        """
		        ctor.
		        @param name: 服务主题名称
		        @type  name: str
		        @param service_class: 服务消息类型
		        @type  service_class: Service class【防盗标记--盒子君hzj】
		        """
(3)函数调用		
	add_two_ints = ServiceProxy('add_two_ints', AddTwoInts)

(2)请求发送函数

def call(self, *args, **kwds):
        """
        发送请求,返回值为响应数据
        """

.
.
.

3.动作通讯编程步骤

(1)动作通讯知识

(1)动作通讯使用场景

ROS中常用的通讯机制是topic和service,但是在很多场景下,这两种通讯机制往往满足不了我们的需求,比如上一篇博客我们讲到的机械臂控制,如果用topic发布一个运动目标,由于topic没有反馈,还需要另外订阅一个机械臂状态的topic,来获得运动过程的状态。如果用service来发布运动目标,【防盗标记–盒子君hzj】虽然可以获得反馈,但是反馈只有一次,对于我们的控制来讲数据太少了,而且如果反馈迟迟没收到,也只能傻傻等待,干不了其他事情。那么有没有一种更加适合的通讯机制,来满足类似这样场景的需求呢?当然是有的,就是我们这篇要讲到的action通讯机制。【防盗标记–盒子君hzj】

(2)什么是action

ROS中有一个actinlib的功能包集,实现了action的通讯机制。那么什么是action呢?action也是一种类似于service的问答通讯机制,不一样的地方是action还带有一个反馈机制,可以不断反馈任务的实施进度,而且可以在任务实施过程中,中止运行。

1、是一种应答机制
2、带有连续反馈
3、可以在任务过程中终止运行
4、是基于ROS的消息机制实现的

(3)动作编程的接口

--goal(发布任务目标)
--cancel(请求取消任务)
--status(通知客户端当前状态)
--feedback(周期反馈任务运行的额检测数据)
--result(向客户端发送任务的执行结果,只发布一次的)

(2)步骤

1、定义动作消息

(在功能包目录下)
(1)定义action文件
在功能包的功能包文件夹下新建一个空白文档如action文件夹,在该文件夹里创建一个.action文件 ,编写程序(包括三个定义内容变量:goal、result、feedback message等)
(2)在package.xml文件中添加功能包依赖(与上述两个通讯类似)
(3)在cmakelist.txt中添加编译选项(与上述两个通讯类似)   【防盗标记--盒子君hzj】
(4)预编译一次
会生成.action文件对应的.h文件

2、创建一个服务器

1、在功能包的src文件夹下新建一个空白文档如action_server.cpp 
2、编写程序内容
(1)初始化ROS节点
(2)创建server的实例
(3)启动服务器,等待动作请求
(4)接收到消息后进入回调函数,完成动作服务功能的处理并反馈进度信息
(5)动作完成,发送借宿信息

3、创建客户端

1、在功能包的src文件夹下新建一个空白文档如action_client.cpp
2、编写程序
(1)初始化ROS节点
(2)创建client实例
(3)连接动作服务端
(4)创建并发送动作目标goal
(5)设置多个功能不同的回调函数

4、添加编译选项并编译

【在cmakellist.txt】
编译代码生成可执行程序【add_executable(server src/server.cpp)】【防盗标记--盒子君hzj】
设置链接库【target_link_libraries(talker ${catkin_LIBRARIES})】
设置依赖【add_dependencies(exe_name (PROJECT_NAME)_gen_cpp)】
回到工作空间进行编译

5、运行可执行文件

(1)启动roscore
(2)启动服务端【如rosrun learning communication action_server】【roscore 元功能包 可执行文件】
(3)启动客户端【如rosrun learning communication action_client】【roscore 元功能包 可执行文件】【并在客户端终端上输入参数】

四、ROS定时器中断

1.Ros_time定时器实现步骤

1).h声明定时器变量
2).cpp创建定时器API【防盗标记--盒子君hzj】
3)在.cpp中编写回调函数,并在.h中声明回调函数
【注意在回调函数的形参上要加上(const ros::Timeevent&event)】

4)ROS的time定时器的形参设置
ros的time定时器设置的是运行持续时间也是并运行的频率,设置方式不同而已,效果是一样的
运行持续时间使用duration(XXX)进行设置,XXX秒是定时器运行的最长时间内,
time定时器原则上运行不能超过XXX秒,所以不要在定时器内用延时(duration())或者阻塞函数(rate.sleep)或者while。
若是提前运行完了定时器的程序,定时器会提前退出,响应下一次再进来
duration(0.1)--10hz
duration(0.5)--2hz
duration(1)--1hz
duration(10)--0.1hz

2.定时器函数API

(1)C++版本(推荐)

(1)功能
创建一个定时器,按照指定频率调用回调函数

(2)函数原型
		Timer createTimer(Duration period, const TimerCallback& callback, bool oneshot = false, bool autostart = true) const;
		* \param period 时间间隔
		* \param callback 回调函数
		* \param oneshot 如果设置为 true,只执行一次回调函数,设置为 false,就循环执行。【防盗标记--盒子君hzj】
		* \param autostart 如果为true,返回已经启动的定时器,设置为 false,需要手动启动
		
(3)函数调用
		1)只执行一次调用
		ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,true)
		
		2)执行多次调用
		ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing);

(4)定时器回调函数
		void doSomeThing(const ros::TimerEvent &event)
		{
		    ROS_INFO("-------------");
		    ROS_INFO("event:%s",std::to_string(event.current_real.toSec()).c_str());
		}

(2)python版本

(1)功能
		创建一个定时器,按照指定频率调用回调函数【防盗标记--盒子君hzj】

(2)函数原型
		def __init__(self, period, callback, oneshot=False, reset=False):
		    Constructor.
		    @param period: 回调函数的时间间隔
		    @type  period: rospy.Duration
		    @param callback: 回调函数
		    @type  callback: function taking rospy.TimerEvent
		    @param oneshot: 设置为True,就只执行一次,否则循环执行
		    @type  oneshot: bool
		    @param reset: if True, timer is reset when rostime moved backward. [default: False]
		    @type  reset: bool
		"""

(3)函数调用
		1)只执行一次调用
		rospy.Timer(rospy.Duration(1),doMsg,True) # 只执行一次
		
		2)执行多次调用
		rospy.Timer(rospy.Duration(1),doMsg)

(4)定时器回调函数
		def doMsg(event):
		    rospy.loginfo("+++++++++++")
		    rospy.loginfo("当前时刻:%s",str(event.current_real))【防盗标记--盒子君hzj】

五、Boost库线程API

1.Boost库介绍

(1)介绍

Boost库使用thread线程,使用单独线程运行一个函数,去完成不可影响的实时任务,如发布话题任务或者计算任务

允许使用可移植C++代码中的共享数据执行多个线程。它提供用于管理线程本身的类和函数

(2)Boost.Thread库

(1)boost ::thread的定义
它在boost/thread.hpp中定义,所以在源文件中要包含该头文件按【防盗标记--盒子君hzj】
#include <boost/thread.hpp>

(2)boost::thread的功能
类用于创建新线程

(3)boost ::thread的使用方法

	#include <boost/thread.hpp>
	
	int main()
	{
	 boost::thread t{thread};
	 t.join();
	}
	创建句柄t之后,函数thread()立即开始在其自己的线程中执行。 此时,thread()与main()函数并发执行。

(3)boost.bind库

(1)boost::bind的定义
它在boost/bind.hpp中定义,所以在源文件中要包含该头文件按#include <boost/bind.hpp>

(2)boost::bind的功能
绑定任意参数到某个指定值上或者将输入参数传入任意位置,同时支持函数对象、函数、函数指针、成员函数指针的绑定

(3)boost ::bind的使用方法【防盗标记--盒子君hzj】
		例子一
		int f(int a, int b)
		{
		    return a + b;
		}
		bind(f, 1, 2)等价于f(1, 2);
		
		例子二
		int g(int a, int b, int c)
		{
		    return a + b + c;
		}
		bind(g, 1, 2, 3)等价于g(1, 2, 3);

(4)参考资料

Boost库有很多功能,也有很多类的实现,用到什么学什么!【防盗标记–盒子君hzj】
https://cloud.tencent.com/developer/article/1629794
.
.

2.boost可以理解成和mian()的while()或者定时器一样,有异同点

(1)共同点

(1)在main()和boost库都要使用while(),也要使用ros_sleep()与ros_loop_rate()的API,不然执行的太快了

(2)在main()和boost库中不要真正的return出去,出去了就进不来了

在main()和boost库中控制运行频率的方法(步骤)
1)在main()的while()外使用ros::Rate的API设置运行频率【ros_Rate loop_rate(10)--以10hz运行】
2)在main()的while()内使用ros::sleep的API控制系统运行完休眠【loop_rate sleep()】

(2)异同点

(1)用boost库比在main()中更整洁,单独开一个线程安全性也更高

(2)若在boost中使用while(),boost库可以看成一个定时器,若不在boost库中使用while()可以看成仅仅执行一次的初始化函数

.
.

3.Boost库使用注意

(1)Boost库是一个线程库,一般boost的thread与bind一起使用。【防盗标记--盒子君hzj】

(2)在boost的线程库中一般要加上while()语句进行使用,boost库不会定时调用,
是单独开一个线程像mian()函数这样连续运行,main的节点挂掉了boost线程循行的东西也不会挂掉

(3)要持续运行的程序除了boost库的实现方法之外,还可以在main函数中使用while()
【当然能用boost的while,最好不要用main()的while,这样节点cpp和实现的cpp就更规范了,
连续运行的boost的稳定性比main()强,因为单独开了一个线程给它运行】

(4)可以把ros_time定时器理解成离散时间运行的,boost库理解成连续时间运行的

4.Boost库的优势

(1)boost库的最有用的地方是让工程架构更整洁
(2)运行的安全性更高,节点挂了它依然能够运行!
(3)要连续运行耗时很长的程序,最好用boost库而不是time定时器,time容易出现时间溢出的潜在bug

六、Ros使用话题通讯、服务通讯、动作通讯、定时器、boost库的选择

(1)用服务通讯:仅仅做一次,且要得到结果的【防盗标记–盒子君hzj】

(2)用话题通讯:要一直执行,连续的处理

(3)用定时器实现:按照一定频率进行处理

(4)用动作实现:用于控制的处理

(5)尽量不要用run()实现,耦合太多,不独立执行,宁愿用定时器

(6)C++的boost库和ROS的定时器API的对比

(1)ROS的线程处理:boost:thread XXX(boostbind(XXX,this))
(2)ROS的定时器处理:pathUpdate Time = nh.creatTimer(ros:Duration(1.0),XXX,this)
(3)两者的功能是差不多的,都是定时实现某一个功能,但是可是底层实现方法不太一样【防盗标记--盒子君hzj】
(4)boost库作为C++的线程库,适用与移植到各种平台,但是在ROS中最好还是用自身的time定时器实现

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-11-10 12:23:57  更:2021-11-10 12:25:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 6:14:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码