**前情提要:上一篇,已经成功建立了工程,并编写了一个节点发布和订阅话题的功能 **
《教程 Re:Zero ROS (三) —— 新建工程、订阅发布话题》
https://blog.csdn.net/Lovely_him/article/details/107751902
教程 Re:Zero ROS (四)
—— Service/msg/srv/tf/urdf/launch/param
1. 编写与测试Service服务
《编写简单的Service和Client (Python)》
http://wiki.ros.org/cn/ROS/Tutorials/WritingServiceClient%28python%29
《测试简单的Service和Client》
http://wiki.ros.org/cn/ROS/Tutorials/ExaminingServiceClient
- 1)“Service服务”和“Topic话题”的编写差不多,使用区别就在于topic是一直发布与订阅,而service是请求后响应。具体编写可参考wikiros或是中科院的例程。
- 2)“Service服务”一般是用于反馈中。比如,如果我想得知当前小车运动情况,发送一个请求得到小车的响应,返回srv消息。ros一般一个节点一个功能,然后通过话题或服务等通讯方式连接起来实现目的。
- 3)我完成小车仿真的过程中并没有编写过“Service服务”,因为用不到。我的工程体量太小,内容简单,涉及的信息量不大。如果我想反馈信息,只需使用
rostopic echo
指令或rqt_plot
和rqt_graph
工具查看便可。调用的软件包虽然有提供了“Service服务”,但是我也没怎么用到。
2.了解msg与srv消息
- 1)msg和srv属于ros的一种数据类型,可以类比c语言的结构体。srv与msg不同的则是分有响应与请求两部分。
- 2)ros允许自定义msg和srv,定义完后修改工程的
CMakeLists.txt
文件便可使用。但是一般没必要自定义,因为ros库本身自带的msg和srv就已经完全够用且有多了。 - 3)需要关注的是
std_msgs/Header
msg消息类型,是关联TF树的重要知识点,在下一点说明。 - 4)在调试/编写ros工程时,时常需要查看msg或是srv数据类型的定义内容。在终端输入
rosmsg
和rossrv
相关指令便可快速查看,这两个指令不需要开启roscre
便可以使用。其中用得最多的便是rosmsg/srv show
。如果你使用的是”RoboWare“IDE的话,是可以直接查看系统内已安装的msg和srv。
3.编写TF树
tf/Tutorials
http://wiki.ros.org/tf/Tutorials
- 1)tf的功能就是接收许多坐标的相对消息,然后将其连接起来形成树状图,所以叫tf树。节点可以一直发布消息到tf,也可以一直订阅tf的消息。感觉上tf像是一个节点,因为它能处理接受到的数据并进行转换再发布给订阅的节点。但是在
rqt_graph
中可知tf属于类似话题类的方框标识。大家用多后自然有概念了,这个概念取决你涉及的深度,够自己用,不妨碍理解就好。 - 2)在小车仿真实验中,有一步要自己编写小车相对于原始起点的坐标,将此坐标发送至tf中,供其他节点调用。以下举个简单的例子,更多例子可参考wikiros或是中科院的例程。
#!/usr/bin/env python
#-*-coding:utf-8-*-
# 加载ros的Python基础包
import rospy
# 加载ros的tf2基础包
import tf2_ros
# 加载tf树 的 msg消息
from geometry_msgs.msg import TransformStamped
class main_class:
# 初始化函数
def __init__(self):
# 创建node节点 —— odom坐标计算
rospy.init_node('odom_calculate', anonymous=True)
# 创建tf对象,用于发布odom坐标
self.odom_tf = tf2_ros.TransformBroadcaster()
# 创建msg数据对象,用于存储数据于发布数据
self.msg_tf = TransformStamped()
# 填写当前时间
self.msg_tf.header.stamp = rospy.Time.now().to_sec()
# 填写参考原点
self.msg_tf.header.frame_id = odom
# 填写当前坐标
self.msg_tf.child_frame_id = base
# 设置延时频率
rate = rospy.Rate(1)
while not rospy.is_shutdown():
# 发送TF消息
self.broadcaster()
# 延时
rate.sleep()
# 回调函数
def broadcaster(self):
# 填写当前时间
self.msg_tf.header.stamp = rospy.Time.now().to_sec()
# 填写位姿(坐标)消息
self.msg_tf.transform.rotation.x = 0
self.msg_tf.transform.rotation.y = 1
self.msg_tf.transform.rotation.z = 2
self.msg_tf.transform.translation.x = 3
self.msg_tf.transform.translation.y = 4
self.msg_tf.transform.translation.z = 5
# 向tf树发送坐标消息
self.odom_tf.sendTransform(self.msg_tf)
if __name__ == '__main__':
try:
main_class()
except rospy.ROSInterruptException:
pass
- 3)
odom
代表起始原点,base
代表小车位置,即小车当前坐标相对于起始原点,处于什么位置。这个坐标关系是需要根据陀螺仪和编码器计算得到的,这里碍于篇幅就删掉赋予固定值。 - 4)在填写msg信息时,经常忘记有哪些项需要填写,可以使用
rosmsg show
指令查看。 - 5)如果忘记某msg信息每个值代表什么意思,可以尝试翻译变量的ID,或是查看wiki手册。
geometry_msgs
http://wiki.ros.org/geometry_msgs
geometry_msgs/TransformStamped Message
http://docs.ros.org/api/geometry_msgs/html/msg/TransformStamped.html
4.wiki-ros手册(http://wiki.ros.org)
- 0)假设你已经查看过很多次wiki了,或许应该差不多发现了wiki的网址命名规律。
- 1)查看msg包
http://wiki.ros.org
+/<msg 包名>
;进入页面后再找子msg消息数据类型。例如(srv
类似):
http://wiki.ros.org/std_msgs
- 2)查看pack包
http://wiki.ros.org
+/move_base
;进入页面后可查找子插件和节点功能等相关信息。
http://wiki.ros.org/move_base
- 3)总结,查看ros相关内容
http://wiki.ros.org
+/<相关内容>
;这个内容必须是大类的,索引查找当然应该从大类筛选到小类。 - 4)有时候在别的ros工程里看到有不懂的内容,不知道属于哪个大类或是有哪些小类,甚至属于什么类型的都不知道。这时就可以百度,关键字为
<不懂的内容>+ros wkik
。如果wiki内含有相关的标题内容,就会直接被搜索到。这个功能还是很方便的。(为什么不用wiki内的搜索框?不知道为什么每次搜索都失败,直接跳转加载超时页面)
5.编写URDF
urdf
http://wiki.ros.org/cn/urdf
- 1)urdf就是将tf的概念再扩展了一步,tf是宏观,urdf是微观
(个人目前使用范围内的理解)。urdf定义了各个部件之间的坐标关系与运动限制,如果对象是机械臂,则是不同关节的长度、可摆动角度等消息;如果对象是小车,则是不同轴承的长度,轮胎的转速等消息。 - 2)wiki上的图和例程就很经典,简单了解,直白易懂。根据图就能明白,
link
就是物块本身属性,joint
则是定义两个物块之间的属性。
urdf/Tutorials/Create your own urdf file
http://wiki.ros.org/urdf/Tutorials/Create%20your%20own%20urdf%20file
- 3)如果你没有给自己的设定物体准备模型,urdf还可以定义简易的物体形状与颜色。
编写一个关节链接另一个关节的程序并不难,先定个小目标,尝试拼个钢铁侠出来吧。比赛时小车的模型是不允许修改的,我也并没有多研究,有个概念知道是这么回事就好了,之后要用到再回来查。 - 4)urdf使用的是一种名为
xacro
的宏语言,优点是简单,缺点是简单……全是标签,只要缩进没有问题,查wiki手册知标签的意思,就是往里面填属性了。还有一点需要注意点是,urdf文件内不可以有中文,注释也不可以。不然运行就会报错。早期看程序学习时把我坑惨了。 - Ubuntu 20.04版本的noetic下,又许多指令和旧版的不一样,我在查找学习博客时就发现很多操作在noetic不能直接实现。
《ros初探xacro/xacro模型文件》
https://blog.csdn.net/qq_43786066/article/details/104420700
Using Xacro to Clean Up a URDF File
https://wiki.ros.org/urdf/Tutorials/Using%20Xacro%20to%20Clean%20Up%20a%20URDF%20File
6.配置文件CMake/package & 启动文件launch
roslaunch
http://wiki.ros.org/roslaunch/
- 1)之前编写的topic和service的python文件都是单个执行的,launch则是将调用各个topic和service的可执行文件,且不限于这两,ros几乎所有需要加载的东西都是由launch启动加载的。所以可以将launch理解为启动文件,其基础简单的作用便是启动roscore然后执行程序。
- 2)要想launch能顺利正常的启动还需要保证工程文件中
CMakeLists.txt
与package.xml
文件都配置好。因为launch会自动启动roscore,所以启动前还需要保证工程已经编译通过,环境变量已经刷新等操作。(注意,CMakeLists.txt
与package.xml
也不能存在中文) - 3)如果添加了msg或是srv,则需要修改:
package.xml
和CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS std_msgs message_generation)
解决ROS在编译中出现Unknown Cmake command“generate_messages”的问题
https://blog.csdn.net/csdnpen/article/details/107317421
- 3)如果添加了自定义的msg和srv,则需要修改:
package.xml
和CMakeLists.txt
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
generate_messages(DEPENDENCIES std_msgs <自定义msg的名字> <自定义srv文件的名字>)
add_message_files(FILES <自定义msg文件的名字>)
add_service_files(FILES <自定义srv文件的名字>)
创建ROS消息和ROS服务
http://wiki.ros.org/cn/ROS/Tutorials/CreatingMsgAndSrv
Creating a workspace for catkin
http://wiki.ros.org/catkin/Tutorials/create_a_workspace
- 4)如果添加了
.py
文件,则需要修改:CMakeLists.txt
catkin_install_python(
PROGRAMS
<py文件名含路径>
DESTINATION ${
CATKIN_PACKAGE_BIN_DESTINATION}
)
- 4)如果
.py
导入的其他软件包,则需要修改:package.xml
和CMakeLists.txt
<build_depend><添加的软件包></build_depend>
<exec_depend><添加的软件包></exec_depend>
find_package(catkin REQUIRED COMPONENTS <添加的软件包>)
- 5)这里面是重点,配置了
CATKIN_DEPENDS
,则package.xml
内也要添加对应的内容。不然就会报错。
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES himpack_name
CATKIN_DEPENDS roscpp rospy std_msgs <包名1>
# DEPENDS system_lib
)
<build_depend><包名1></build_depend>
<exec_depend><包名1></exec_depend>
- 5)一切准备无误后,就开始编写launch文件,尝试启动之前写好的topic话题
.py
文件。启动之前确定package.xml
和CMakeLists.txt
无误,确定.py
文件给予了执行权限,确定launch内pkg
、type
填写无误。任何一步出错都有导致编译失败。(launch的编写格式、标签请上wiki查,具有绝对准确性与详细性)
<?xml version="1.0"?>
<launch>
<node pkg="himpack_name" type="output_control.py" name="motor_control"/>
</launch>
- 6)编写完成后,
catkin_mate
编译,然后source ~/.bashrc
刷新环境变量,最后roslaunch
执行launch文件,然后启动rqt_graph
工具查看节点。
catkin_mate
source ~/.bashrc
roslaunch output_control.launch
- 7)可以看到,启动launch文件时,ros也会被启动,相对于执行力一次
roscore
。同时节点图内查看的节点名是没有后缀的。不同于单个文件启动。 - 8)大多数时候launch是分功能块写的,部分节点归为一类,一起启动会相对于启动某种功能。这样便会用launch打包在一起,然后被其他launch文件引用/调用。同时,如果某些launch被打包后不能单独启动,只能被其他launch调用的话,会在
.launch
后缀名后加上.xml
,一起组成.launch.xml
。这样便只能用于被调用了,使用起来有点像载入参数。
7.参数param/yaml
rosparam
http://wiki.ros.org/rosparam
- 1)大多数时候,为了增加程序的适用性与移植性,都不会把程序的参数“写死”。比如之前写的topic发送程序,我们已经把"发布的topic话题名"和“订阅的topic话题名”,我们日后调用该程序时,若对应的topic名不一致,我们还需要打开程序的源代码修改。这样就极其不方便了。
- 2)如果需要把
.py
的变量转为可变参数,只需要作如下修改。rospy.get_param
内的第一个参数即为参数名,第二个参数为默认参数值,可有可无。这个函数就是把该参数的值返回给node_name
变量,如果没有默认值也没有设置值的话就会报错。
# 创建node节点 —— 电机控制
node_name = rospy.get_param('~node_name',"motor_control")
rospy.init_node(node_name, anonymous=True)
- 3)
.py
内修改好后,在launch内就可以设置参数了。name
为参数名,value
为参数值,type
为参数类型,可为实数或是字符。字符为"string"
,实数为"double"
。需要注意多就是,如果参数是实数变量,则.py
内的默认参数不需要加双引号,但是launch内还是要加。
<?xml version="1.0"?>
<launch>
<node pkg="himpack_name" type="output_control.py" name="motor_control">
<param name="node_name" type="string" value="motor_control"/>
</node>
</launch>
# .py内
max_angle = rospy.get_param('~max_angle',0.32)
# .launch内
<param name="max_angle" type="double" value="0.32"/>
- 4)除了可以在
.py
和.cpp
内设置参数,launch内也可以设置参数。注意格式$(arg node_name)
。参数default
是指默认值,该launch文件被其他launch调用时如果没设置这个参数,这个参数必须有个默认值。同时,<param …… value……>
内一定为value
不能为default
,切记。
<?xml version="1.0"?>
<launch>
<arg name="node_name" default="motor_control"/>
<node pkg="himpack_name" type="output_control.py" name="motor_control">
<param name="node_name" type="string" value="$(arg node_name)"/>
</node>
</launch>
- 5)同时,如果参数实在过多,在launch文件内排布的不美观的话,还可以另外再打包为参数文件
.param
。这种情况是确定好了部分参数的值,不需要再被外层调用修改时使用。说白了就是,一般在调用ros官方库时才用,因为官方软件包已经是成品,有完善的功能,但是需要配置很多参数。所以就有了,param
参数打包文件。编写也很简单,只需要"参数名:参数值
"即可。
i: 1.0
d: 1.0
- 6)如果参数是msg或是srv类型的,需要配置多层参数,则需要按缩进分层。
8. 总结
- 1)现在已经把ros工程的大致结构介绍完了,掌握这些知识,对于搭建仿真小车来说已经绰绰有余了。
- 2)下一篇教程将开始一步步创建仿真小车的ros工程。同时简单介绍
map_server
/move_base
/amcl
/slam
等与小车导航、定位有关的软件包。 - 3)
待补。感谢你能看到这里,这是一个完整的系列,如果还没看前后篇,开去看吧。
《教程 Re:Zero ROS (五)—— 导入模型,关节控制器 》
https://blog.csdn.net/Lovely_him/article/details/107806662