ROS MEMO
预备
所有ros的github仓库集合 , 链接:http://wiki.ros.org/RecommendedRepositoryUsage/CommonGitHubOrganizations, 包括ros核心源码, 可视化库, 仿真库, 规划库。 , 如果要查找ros核心源码的接口函数定义, 可以通过以下链接查找https://docs.ros.org/en/api/roscpp/html/
0. catkin依赖的CMakeLists.txt文件说明网站: http://wiki.ros.org/catkin/CMakeLists.txt
- catkin 中cmake宏定义的说明网站:https://docs.ros.org/en/api/catkin/html/
catkin 源码网站:https://github.com/ros/catkin/blob/noetic-devel/cmake/ - ROS入门教程:http://wiki.ros.org/cn/ROS/Tutorials
- ROS发展历史: Mhttps://wiki.ros.org/Distributions
- ROS中package.xml说明文档: http://wiki.ros.org/catkin/package.xml
- ROS中launch文件的xml格式: http://wiki.ros.org/roslaunch/XML
- ROS支持的所有功能包列表: https://index.ros.org/packages/page/1/time/#galactic ,包括ROS1和ROS2。
- ROS的开发介绍文档:
https://docs.ros.org/
https://docs.ros.org/en/
http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/MultiEchoLaserScan.html
http://wiki.ros.org/sensor_msgs
分别包含了入门学习教程以及ROS源码接口说明, 开源功能包的接口说明, 标准定义的传感器数据类型。
例如, dwa规划器件的源码接口说明: https://docs.ros.org/en/melodic/api/dwa_local_planner/html/classdwa__local__planner_1_1DWAPlannerROS.html
例如: ros中官方的代码仓库,包含了ros中各类的工具, 网址: https://github.com/orgs/ros-planning
例如: moveit机械臂官方教程, https://moveit.picknik.ai/humble/index.html
例如: 比rqt_plot更好用的数据监控软件, 下次出一个使用教程, 即plotJuggler
,支持描绘机器人的坐标位置, 官方的学习教程:https://slides.com/davidefaconti/introduction-to-plotjuggler - 常见传感器的驱动代码连接:http://wiki.ros.org/Sensors
- ROS中几个重要的机器人坐标系:
- 机器人导航的栅格地图原点在整张栅格地图的左下角, 横轴为x,纵轴为y。
- 栅格地图生成的yaml文件中的
origin点
是在机器人的世界坐标系下,地图原点的位置。 - 机器人的自身坐标系一般向前运动的方向为x轴,旋转角为机器人与x轴的逆时针夹角。
混淆点
- 利用catkin创建ros工作空间
mkdir -p catkin_ws/src # 只需要在工作空间放置相应的ros源码即可。
cd catkin_ws && catkin_make # catkin_ws 继承了cmake 源码与编译输出相分离的原则,会自动创建build以及devel工作文件夹
source ~/devel.setup.bash # 将当前工作空间的信息加载到环境变量中,方便进行rosrun等等命令找到相应的功能包以及可执行文件。
注意: py文件是可执行脚本,不需要编译,直接创建在scripts文件夹即可,
但是要通过 chmod +x foo.py 命令给脚本加上可执行权限,否则无法通过rosrun进行加载。
- 利用catkin_create_pkg 创建对应的功能包框架
cd catkin_ws
# 其实该命令只是帮忙快速编写了CMakeLists.txt以及package.xml中的一些比较有限的重复内容,
# 原则上要学会自己编写,重点放在几个cmake 函数模块上。
catkin_create_pkg pkg_name roscpp rospy
# 即使dependencies写错了, catkin_crate_pkg也完全不会报错, 因为该工具未作依赖库的检查。
# catkin_create_pkg pkg_name dependencies
#####
sino@chinaSouth:~/tf_ws$ catkin_create_pkg
usage: catkin_create_pkg [-h] [--meta] [-s [SYS_DEPS [SYS_DEPS ...]]]
[-b [BOOST_COMPS [BOOST_COMPS ...]]] [-V PKG_VERSION]
[-D DESCRIPTION] [-l LICENSE] [-a AUTHOR]
[-m MAINTAINER] [--rosdistro ROSDISTRO]
name [dependencies [dependencies ...]]
- 在使用rviz的时候,有时候某个话题一直显示不出来, 重新勾选话题可能可以解决,
有时候,某些点无法显示, 要注意是不是显示模型grid map的坐标系或者size 尺寸没设置好,size过小,有点距离超出了地图范围,也有导致无法正常显示
有时候,某些点没有显示出来, 要注意是不是设置了显示模型的Alpha值(透明度),或者是不是设置了消息的history length, 如果话题的历史长度设置太长, 就会导致话题显示模型没办法立即显示。 - catkin_create_pkg命令可以节省精力避免错误,但ros package相对于普通c++ package的不同, 不过就是小部分CMakelist.txt文件中的宏操作以及一个XML文件。
命令的例子:$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
//命令 功能包名 依赖 - ros在解析图结构的时候,主要分为 base, relative, global, and private四种情形,具体举例如下:
Node | Relative (default) | Global | Private |
---|---|---|---|
/node1 | bar -> /bar | /bar -> /bar | ~bar -> /node1/bar |
/wg/node2 | bar -> /wg/bar | /bar -> /bar | ~bar -> /wg/node2/bar |
注意: 节点名node继承于命名空间名, 而各类服务,参数继承方式取决于ros::nodehandle()函数形参的定义与选择。 | |||
可以选择继承于根命名空间,继承于节点初始化所在的命名空间以及继承于节点名命名空间之后。 | |||
一般情况下,图结构解析形式一般采取relative的形式去继承节点所在的命名空间字段。 |
- ros_init() 用于初始化节点,并设置节点名,即初始化了node功能
(节点是ros中构建功能的基础, 一个节点下可以通过ros::nodehandle()函数创建多个不同功能的命名空间用于功能开发, 命名空间的继承方式有如上三种,如私有空间, 全局空间, 相对空间。注意该节点名可以通过roslaunch中node标签修改,并且roslaunch修改的优先级更高,会直接覆盖代码中的命名, node标签还具有“ns”参数,用于设置命名空间, 如果有设置,节点名将会继承于该命名空间。注意:命名空间在节点启动的时候, 是可以通过外部传参修改的, 话题也是通过这种方式去改变, 通过修改节点命名空间的名字的方法可以让多个服务运行相同的节点代码。 <http://wiki.ros.org/Nodes> )
- ros::nodehandle 函数用于初始化个人服务的功能命名空间(my_node_namespace),之后的订阅和发布话题名字或者服务名字, 参数名都继承于该个人功能命名空间,注意,如果函数形参为空或者为“/”的话, 则默认为相对继承, 如果为“~”, 表示私有继承。
个人功能命名空间默认为相对继承的形式, 默认继承于ns参数设置的命名空间,即 /ns/topic, 如果没有ns参数设置的命名空间的话, 则默认继承于根目录, 即/ns/topic。
如果功能命名空间通过"ros::advertise_service(/service_name)等功能函数“
设置为/
路径的话, 则相关的话题,服务继承于根服务空间,为全局命名域,即/topic, /service。
如果在设置功能命名空间的时候,如果使用private的形式时(即通过ros::nodehandle(~name)函数命名个人节点命名空间的时候),此时个人功能命名空间名字将会继承节点名的字段来组成命名空间(/node_name/my_node_namespace)
,因而通过使用private的形式命名的话, 有利于参数服务器传递参数值到指定的节点上。**即/ns/node_name/topic。
为了更好的理解私有继承, 我们可以将ros::nodehandle(name)函数所传递的形参所携带的“”符号理解为节点名的通配符。 - 当我们在roslaunch中使用<param>标签添加参数的时候, 如果添加在<node></node>标签中的话, 则这些参数将会变成node节点的私有参数, 也就是说参数将会继承于节点名命名空间, 为私有继承。如下将会得到参数名为
/robot_pose_ekf/freq
, 因此, 在节点代码中, 要通过ros::nodehandle(“~”)将功能命名空间设置为私有,这样节点代码中, 就可以通过参数名直接访问了,而不用写出冗长的参数路径。<node pkg="robot_pose_ekf" type="robot_pose_ekf" name="robot_pose_ekf" output="screen"> <param name="freq" value="30.0"/> </node>
如下小乌龟的图结果:
catkin_package(CATKIN_DEPENDS pkg_name)
用于指定catkin编译时的一些动态依赖库等信息来生成编译所需要的cmakeFIle等 相关文件, 此时添加的依赖库的名同样要添加到 package.xml中的 exec_depend标签中。
INCLUDE_DIRS
表示本功能包要导出的头文件路径
LIBRARIES
表示本功能包要导出的库文件
DEPEND
表示catkin编译时需要依赖的一些非catkin功能包的依赖库。- 在ROS新创建功能包,编译完之后, 要
source devel/setup.bash
文件, 退到功能包的根目录下, 才能够Tab键出来。 - rospack的工作依赖文件就是 package.xml文件, 比如说
rospack find
或者rospack depend
命令操作,都是依赖于该文件来操作。
除此之外,还要注意一点在于当我们执行
catkin_make
进行编译操作的时候,它会去检查package.xml文件是否规范。
CMakeLists.txt文件中如果说执行了find_package( COMPONENTS pkg_name )
去查找某些功能包的时候, 那么其中的功能包名要在package.xml中的<build_depend> pkg_name </build_depend>
体现,
如果说在CMakeLists.txt中添加catkin_package(CATKIN_DEPENDS pkg_name)
或者说当你需要依赖某功能包的动态库的时候,那么功能包要在<exec_depend>pkg_name</exec_depend>
标签中体现。
-
当我们在使用现有的消息或者服务的时候,需要在编写cmakelist的时候,在find_package 的操作中添加相应查找 msg, srv的功能包, 如 tf,
-
在使用geometry.angular进行角速度控制的时候,角速度的正负值使用右手螺旋定则来判定,
也就是说大拇指是旋转轴的朝向, 其他拇指按逆时针握紧,
此时的值为正值, 如果按照旋转轴发生顺时针运动则为负值。 -
当我们自定义消息或者服务时,
5.1 先通过 find_package 查找 message_generation的包。
5.2 然后在CMakeList.txt中进行add_message_file(file a.msg), generation_message()
生成消息的操作(该操作通过python实现消息的生成,目前没去找源码的实现, 如何在cmake中嵌套 python操作? ) 。在使用generate_message的操作的时候, 如果说, 你在消息中依赖了其他数据结构, 比如 依赖了std_msgs的话, 那么就需要添加如下操作:generation_message(DEPENDENCIES std_msgs)
5.3 最后, 调用cakin_package宏添加运行依赖库message_runtime到该cmake宏命令中的CATKIN_DEPEND选项上来生成编译文件以及链接运行时需要的库文件。(注意
:在编译构建时,其实只需要message_generation,而在运行时,我们只需要message_runtime。) -
在srv和msg文件夹中去自定义消息和服务的数据结构的时候, 可以使用ROS系统当前已定义的现有数据格式,如
std_msgs, nagavitation_msgs
,也可以使用自定义的msg格式, 比如说, sensor_msgs。 ROS中还有一个特殊的数据类型:Header
,它含有ROS中广泛使用的时间戳和坐标系的命名, 在msg文件的第一行经常可以看到Header header。
详细的参考链接:
http://wiki.ros.org/msg
http://wiki.ros.org/srv
目前的消息支持以下类型的转换: Serialization(序列化) ,C++, Python2, Python3。
注意:
关于数据类型如何映射转换的, 可以查看以上的网址, 也可以查看生成消息的代码, 在 devel/include的文件夹下。
目前支持的数据类型如下所示:
int8, int16, int32, int64
uint8,uint16, uint32, uint64
float32, float64
string, bool
time(ros::Time), duration(ros::Duration)# 这两种数据结构是ros内置的,要配合括号中的方法使用。
other msg files
variable-length array[] and fixed-length array[C],# 在c++中 映射为vector数据类型。
- 注意别在main函数或者普通函数外进行赋值操作,有时候一眼看出来有问题, 有时候会不注意,
函数外可以定义初始化变量, 但是不能进行赋值操作,编译器报错也有时候不太直观,
在使用自定义消息和服务的数据结构时, 对结构体内的变量进行赋值之后, 不太明白为何编译器报错显示为
error: ‘tT’ does not name a type.
#这种问题 一般是无法找到结构体的数据结构定义的时候会报这种错。
- 如何理解定义的文件内的消息结构:
# 消息格式
int32 x
int32 y
>第一列表示以上支持的数据类型, 第二列表示定义的变量名。
# 服务格式
string str
---
string str
>对于 c++而言, 第一行表示request结构体内的数据变量, 第二行表示response结构体内的数据变量。
-
针对 c++编程而言,自定义的消息或者服务 会生成在
devel/include
的文件夹下, 要注意, 此时我们创建的catkin功能包名字会被作为命名空间, 在msg文件夹下的消息文件名以及在srv文件夹的服务文件名会被作为结构体的名字,比如:pkg_name::msg_file_name
,要注意的是该数据类型中包含request和response的结构体以及 Request和Response的类,不要像msg那样直接就调用自定义的数据类型,在调用类里面的数据变量的时候,有时候经常会犯这个错误, 因为srv文件的内容格式定义分为 response和request两部分内容,如果只是需要request的数据类型, 通过pkg_name::msg_file_name::Request
来声明变量就可以实现。
头文件的位置路径也是类似于这样这样来包含, 比如pkg_name/msg.h
或者pkg_name/srv.h
。
在使用定义的数据结构的时候,定义的变量名下可能还有多层数据结构嵌套,有时候可能会看乱掉,可以借助rosmsg show msg_name
来展开显示。 -
在调用他人已定义的数据结构的时候, 一般通过以下命令查询其数据结构, rosmsg和rossrv主要用于显示数据结构的框架图。
#! 以下代码用于查询服务的数据结构
rosservice list # 当前运行的服务列表
rosservice type service_name
ros
rossrv show srv_struct_name
#! 以下代码用于查询话题的数据结构
rostopic list
rostopic type topic_name
rosmsg show msg_struct_name
- 注意launch文件中的xml 语法,在加载赋值的时候, 会产生后面覆盖前面的行为。正常来说, xml必须开启和关闭, 两个动作中间填写描述内容,比如说, 可以插入其他的标签。 每一个标签都必须关闭,这是XML语法规定的。 如:
<a> hello </a>
如果标签中间没有内容存在,则为空标签<a></a>
,这可以简写成<a/>
, 例如: ros中设置参数服务器的参数时, 添加<param name="foo" value="$(arg my_foo)" />
,注意该标签是空标签,此时标签中设置了标签属性name和value,而简化的关闭动作/
挪到空标签的后面。 - 注意在launch不要随便添加xml注释,否则非常容易导致gazebo和rviz以及lanunch工作异常, 比如,rviz和gazebo 出现黑屏现象。
- 在node标签中,如果node节点代码需要传入参数到代码内的指针数组argv的时候, 可以通过通过赋值设置node标签的 args 属性。
注意: 执行 rosrun a b <args>命令时,<args>的尖括号内的参数会作为 argv参数(指针数组)传入到代码内, 相当于直接执行某个命令,命令尾部传入相应的命令参数。
一般在node标签的内容
中,添加param标签传递参数到参数服务器, 添加remap标签来修改话题名,
<node pkg="a" type="b" name="a" args="$(arg va)">
<!--通过roslaunch a b.launch name:=value命令传递到内部,会产生相应的name的arg标签到roslaunch中。
launch文件内可通过$(arg name)置换符去读取数据。 -->
<param name="va" type="double" value="$(arg va)">
</node>
- 使用
roslaunch
的时候,通过执行roslaunch a.launch a:=val b:=b
命令时, 会在执行时产生<arg name="a" value = "val"/>
的标签, 那么在launch文件内部, 我们就可以通过置换符$(arg a)
来获取参数值, 这样使得我们可以从roslaunch命令传递参数值到launch文件内部。 - roslaunch 中xml格式参考http://wiki.ros.org/roslaunch/XML, 目前常用的标签如下:
<launch> #整个文件的主标签
<node> #启动节点的设置标签
<machine> # 该标签用于声明你想运行ros节点的机器信息,一般用于远程机器的操控, 主要是声明远程机器的ssh和ROS环境, 如果本地运行,则不太需要这个标签。
<include> #包含某一个launch文件并执行。
<remap> #一般嵌套于node标签, 用于修改话题名,话题名会被从from的值改成to的值, 被修改的话题名, 包括代码中publish与subscribe的话题,都可被修改,只要搜索匹配到即可。
<arg> #用于获取arg标签的值,
记住一个小技巧:
在需要include 某个foo文件的时候,如果该foo文件内部需要某些的arg变量,可将arg标签写在include标签的内容之中,
如 `<include > <arg name="var" value="96"/> </include> `
<env> #用于设置环境变量。
<param> # 用于设置服务器参数, 如果嵌套在某node标签中,表示属于该节点命名空间下的参数,参数名会继承节点命名空间
# 如果未嵌套在node标签下的话, 则表示全局参数, 无继承任何节点命名空间。
<rosparam> # 用于加载和删除相应的参数文件。如:<rosparam command="load" file="$(find rosparam)/example.yaml" /> 参数文件要遵循yaml语法, 最终的变量在终端用 rosparam 命令查看。
<group> # 这个标签类似于上层标签<launch>, 为命令提供一个容器空间,其中包含了ns属性,以及 clear_params="true|false"。
<test> # 该标签类似node标签, 用于测试节点运行。
- param支持从文件中读值, 比如读取urdf的描述到 robot_description 的变量中, 也支持通过执行命令去获得计算的结果值。
<param name="robot_description" textfile="robot.urdf"> `
<!-- 从命令中去获取值 -->
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_model_simu)/urdf/robot.xacro'" />
<param name="robot_description" command="$(arg model)">
<>
- rosparam 支持从yaml文件中,加载一堆变量, 一般用于写参数配置。
- roslaunch 有一个十分常用并且方便的功能, 就是 substitution args (替换符),通过命令符,我们可以调用变量,简化操作, 避免hard-code。
目前支持的替换符如下:
$(env ENVIRONMENT_VARIABLE) #用于获取环境变量,如HOME, 如果变量不存在,会直接运行报错
$(find pkg) #等价于调用 rospack find 命令, 在找不到的时候,会直接报错停止launch 的执行。
$(anon name) # 启动节点的时候, 产生一个匿名节点名, 避免冲突, 目前还没怎么用到。
$(arg foo) # 用于读取launch文件中的 arg标签的值。
$(dirname) #New in Lunar版本, 用于获取指定文件的全路径, 比如说: $(dirname)/other.launch"
$(eval ) #New in Kinetic版本, 用于表达式的计算, 如数学计算。 $(eval 2.* 3.1415 *arg('radius')
)注意此时arg替换符的使用方法不一样。
- ros在使用 stamped类型的数据进行开发的时候, 比如tf中的坐标变换某一个点数据的时候, 需要用到的 pointStamped 数据结构,其中的header结构体中的stamp变量,要注意要通过 ros::Time()方法来赋值,通过ros::Time()::now() 容易出现获取不到数据的情况, 后者的频率过高。 另外,在header结构体中 frame_id要指定当前点所在的坐标系。
- 在调用rviz的时候, 执行 rosrun rviz rviz -d ‘rospack find turtlesim‘/config/default.rviz 命令,避免一些路径相关的hard-code,除此之外, 注意
在launch文件中, 可以通过find 替换符来实现rospack find的操作。
- 在tf树中, 比如存在以下结构,那么世界坐标系作为中间节点, 那么a到b的转换公式可以写成: Tab = Taw * Twb。
- param和rosparam命令得到的参数变量是在全局根路径下的变量,如
/param
, 而如果在node节点标签中嵌套使用 rosparam 加载yaml文件到参数服务器或者使用param命令设置参数的时候, 得到的参数服务器的参数名将继承于节点命名空间,如/node_space/A,
正常为/A
所以,在代码中获取参数值得时候, 一般会通过ros::Nodehandle nh("~")
将工作命名空间命名为私有(正常工作空间都是全局的路径下),即工作空间将会继承节点命名空间的名字, 这样在c++中可以直接通过 nh.param函数去获取参数服务器参数的时候,因为工作空间已经是节点的命名空间下, 我们就不必再重复填写节点名; 在python中,我们可以直接通过 nh.param(“~param_name”)去获取节点命名空间下的参数,~
代表在节点命名空间下。
<!--
exmaple.yaml如下所示:
A: 123
B: 3
group:
C: 123
-->
<rosparam command="load" file="$(find rosparam)/example.yaml" />
最终得到参数名格式为 /node_space/A, /node_space/B, /node_space/group/C。
-
使用URDF进行仿真设计的时候, 有以下几点需要注意:
24.0 urdf文件不能有注释, 否则ros 可能会有崩溃情况。urdf 官方学习网站: http://wiki.ros.org/urdf
24.1 定义一个link, 就会产生一个tf坐标系, 其名字为link的属性name的值,模型显示的相对位置在该坐标系上进行调整以便正常显示。
24.2 定义一个joint, 就会发布一个相关的tf变换, 这个tf变换取决于 parent link坐标系, child link坐标系和 origin属性(xyz平移,rpy旋转)
的值, joint 有六种关节特性,注意:
我们可以通过 标签来设置joint 的旋转轴。
24.3 出现多个link的时候, 注意需要定义 joint标签来联系二者的关系, 否则定义模型无法被正确显示, 当joint的平移 旋转变换未定义的话,则产生的坐标系默认与parent 坐标系重合。
24.3 实现urdf的模型设计的时候, 先命名一个简单的link坐标系(viausl模型可以暂时不做配置), 再着重设置joint关节的变换(joint 变换得到的点为child 坐标系的原点), 最后在joint变换之下的link坐标系调整 visual标签内的模型的坐标位置。
24.3 一般如果不太需要joint关节进行作用, 可以不设置joint标签的变换,便能使其与parent_link坐标系重合, 调整link中visual标签内模型的位置即可实现设计目的。
24.3 如果没有定义 map坐标系的话,那么 ,记得修改 global options的坐标系为base_link坐标系, 模型的相关变换才能这个正常显示和生效, 可以借助check_urdf
的工具对写好的urdf文件进行初步检查。
24.4 设置全局服务器参数/use_gui
,可以显示出Joint State Publisher
操作界面, 便于我们进行模型关节 joint操作, indigo的这个代码包有严重的bug, 推荐使用joint_state_publisher_gui的功能包。
24.4 在visual标签里面定义的模型的位移, 都是相对于 link的坐标系进行转换。
24.5 为了便于看清rviz的坐标系, xyz的坐标轴分别对应 rgb 三种颜色。
24.6 在visual标签下的geometry标签中,设置 color标签里的rgba属性,其值分别对应对应 red,green, blue, alpha(透明度, 1表示完全透明。)
24.7 在geometry标签中设置mesh标签的filename属性为"package://pkg_name/mesh/kinetct.dae"
就可以导入其他软件的模型文件。 -
在使用urdf之后, 其实会发现urdf其实过于繁琐,因此ros提供了一个工具为 xacro, 便于进行urdf描述的优化, 其中要点包括:
注意: urdf的描述是一次性原则, 如果有两个一模一样的标签描述, 以第一个描述为准, 后续的不起作用。
25.0 通过设置xmlns:xacro属性,<robot name="robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
,来启用xacro工具, 具体的学习网站: https://wiki.ros.org/xacro。
25.1 使用<xacro:property name="a" value="1" />
定义变量,
25.1 使用<xacro:if value="if value="${var == 'useit'}""> </xacro:if>
进行条件判断。
25.2 使用<xacro:macro name="function_name" params="var1 var 2">
</xacro:macro>
定义函数,name
属性的值为后续调用的函数名,params
属性的值为传入到宏内部的形参变量名, 在宏内部通过${var1}
来调用, 整体的功能和C语言中的宏定义很像,都是代入,然后展开。
25.3 调用宏的时候, 通过<xacro:function_name var1="1" var2="2" />
的形式来调用相应的宏,并传入相应的参数。
25.4 使用${ }
可以用来调用property变量,也可用来做四则运算,要注意在定义的宏内部,同样可以使用${var} 来调用 params 中定义的变量。
25.5 xacro同样可以像launch一样通过<xacro:include filename="$(find package_name)/file_name">
来包含想要包含的模型描述,find替换符的作用就是类似 rospack find。
25.6 xacro的文件需要通过xacro功能包下的xacro.py
工具进行解析并生成相应的urdf文件 以便rviz可以显示。
25.7 urdf的模型在gazebo要如何显示和解析呢?
通过rosrun gazebo_ros spawn_model -urdf -model robot1 -param robot_description
的命令去加载urdf描述道gazebo中。
25.8 设计小技巧: 在设计link和joint的时候,尽量使用变量去设计,比如base_footprint到base_link的坐标系变换,可以使用轮子的高度加上模型的高度, 这样在改变变量的时候,便能够实现整体的改变, 在设计某个传感器的时候, link的名字由外部传入xacro macro的内部,joint 的坐标变换相关的设计放在模型的xacro macro函数的调用外。
25.9 调试的时候: 注意先用xacro的工具生成urdf文件,然后通过check_urdf工具检查模型编写是否正确。
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_model_simu)/urdf/robot.xacro'" />
<param name="robot_description" command="$(arg model)"> 通过param的command属性执行命令,xacro工具可将xacro文件转化为urdf值。
-
在ROS中使用 gazebo进行模型仿真是十分有意义的一件事情, ros中的学习官网位于: < https://wiki.ros.org/gazebo>
在gazebo中加入使用urdf编写的机器人模型的仿真步骤 如下:
在gazebo控制机器人的urdf描述的学习可参考:
ros开发相关的插件:http://wiki.ros.org/urdf/Tutorials/Using a URDF in Gazebo
http://gazebosim.org/tutorials?tut=ros_gzplugins
详细编写插件的教程: http://gazebosim.org/tutorials/browse
git上参考仓库:https://github.com/ros/urdf_sim_tutorial
各类gazebo中仿真的插件: http://gazebosim.org/tutorials?tut=ros_gzplugins
注意:
在早期版本的gazebo中使用build_editor工具进行墙,玻璃,环境的创建的时候,只会生成当前环境模型的环境描述,而没有地板, 太阳等等描述, 需要从sdf文件拷贝相应的模型<model>
标签描述,然后放置到通过gazebo创建的world文件当中,这样才能被正常加载,不然会出现模型直接坠落的现象,因为build_editor生成文件没有地板的描述。1. 每个link都务必要添加,惯性矩阵到<inernal>标签中以及碰撞属性, 这样才能更为真实地模拟模型的运动。 添加惯性矩阵的小技巧: <xacro:default_inertial mass="0.05"/> 也可以去维基百科查询惯性矩阵公式, 但是,在通过inertia标签添加长方体的惯性矩阵的公式的时候,不懂为何一直出现python无法计算浮点数的错误。 2. 为每个link添加gazebo纹理描述或者传感器描述, 通过<gazebo refrence="link name"> </gazebo> 来描述。 3. 为非固定的joint标签添加 <transmission>标签描述,用于描述传动装置的硬件情况,比如速度接口,电机属性,减速比等等。 4. 添加在gazebo的控制器插件,包含从cmd_vel到模型的差速控制的驱动,激光到scan数据的驱动,这些插件在gazebo_plugins的代码包中,有很多已经 支持的传感器插件。 同样,我们可以设置odom坐标系,以及odom话题, 然后设置<broadcastTF>标签进行广播。
27.1 我们如何去区分odom坐标系以及map坐标系呢? 原则上,按照实际当中的使用,两个坐标系应该是重合的, 但是在二者的概念是不那么相同的。/odom坐标系是我们机器人初始化启动的坐标系, 然而,/map坐标系是我们构建地图的原点。
-
ROS中使用cv_bridge进行opencv图像识别演示: http://wiki.ros.org/cv_bridge/Tutorials
基于opnecv集成的一些常用的app应用:http://wiki.ros.org/opencv_apps
28.0 先通过 camera_calibration 进行摄像头矫正:
rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.027 image:=/camera/image_raw camera:=/usb_cam
, 然后进行前后左右上下倾斜的移动,保证矫正的数据足够好。
28.1 通过cv-bridge进行数据的转换, 参考网址如: http://wiki.ros.org/cv_bridge
28.2 通过TensorFlow进行目标识别检测, 参考的学习网址如: https://github.com/osrf/tensorflow_object_detector, 由于python 2.7的版本有一些问题, 所以安装使用tensorflow时,版本的信息要额外指定一下。 -
机器人的建图与导航, ROS官网的学习网站: http://wiki.ros.org/navigation, 该链接上面有很多机器人的建图和导航的学习教程。代码教程可以参考:https://github.com/ros-planning/navigation_tutorials
29.1 机器人的导航整体框架图:
灰色模块表示可选的输入节点, 蓝色模块表示需要给指定的节点, 白色模块表示move_base中内置的节点。
29.2 amcl功能包主要用于机器人的位置定位, 通过粒子的收敛来进行定位,该方法通过输入地图数据,激光雷达数据,里程计数据,输出机器人在地图中的位姿, 因此可以帮助机器人消除里程计等传感器的一些漂移现象,想要实现导航的同时也建图,先把地图服务器的节点先关闭,此时不需要发布地图, 把蒙特卡洛定位的节点关闭, 蒙特卡洛的定位需要借助已知的地图数据与激光雷达数据进行概率性判断来输出相对地图的位姿, 蒙特卡洛主要是通过里程计得到一个大致的位置,获得机器人的大致相对运动, 然后再通过地图与激光进行概率匹配而弥补里程计的相对漂移误差。
, 具体的坐标转换如下图所示: 蒙特卡洛所作的变换主要就是弥补地图坐标系
到里程计坐标系
之间的偏移姿态预测。这个误差主要来自于航迹推算等产生的误差。蒙特卡洛变换包含了激光雷达坐标系到里程计坐标系的转换, 因此,ROS中的tf树一定要包括里程计到激光雷达的转换。 蒙特卡洛主要弥补机器人里程计上漂移误差,所以在实际仿真中,我们可以发现, odom坐标系和map坐标系在加入蒙特卡洛之后, 会出现跳动变换, 这个变换就是蒙特卡洛在通过激光,里程计等数据计算得到的漂移变换, 因为有时候机器人在地图上的里程计算的位置还没到某个点,但是实际上很有可能已经超过那个位置点了, 那么通过蒙特卡洛得到变换,就会将对机器人的位置加上一个漂移变换来确保机器人在地图上的位置较为准确。建图的时候,通过gmapping去发布map与odom之间的变换,这个时候基本认为二者是重合的, 但是导航的时候, 利用的是amcl去发布二者的变换, 这个时候, 由于参考了地图和激光雷达的数据,会不断地矫正, 所以odom和map之间的坐标系会不断进行微调。
29.2 除此之外,也通过里程计的数据进行TF位姿转换来进行机器人的定位, 并且局部路径规划也需要机器人的位姿变换来获取位置,朝向等等数据状态。
29.3 map_server的节点也是可选的,这个主要用于给机器人提供地图数据, 但是就像人一样, 不需要地图数据, 也可以乱走, 凭感觉走出一条路径出来,这样的话,我们就可以边建图边进行路径探索, 当然有地图的话,我们便可以更好地去规划一条路径出来和更好地避障。
29.4 sensor_sources中加入激光雷达点云数据,可用于产生避障的局部和全局代价地图。
29.5 以下是导航路径规划的代码接口注释, 通过该导航的move_base的功能包中的action服务器,我们可以实现发布目标点以及取消目标点, 监听目标点状态,在ros中的话题名为 (move_base/topic_name)。
相关的launch参数文件配置 包括代价地图参数配置, 局部代价地图配置, 全局代价地图配置, 功能包控制的参数配置, D* 局部规划参数配置。
具体的详情,可参考move_base官方相关的wiki配置网址: http:wiki.ros.org/move_base。
29.6 注意: 在gazebo中的kinetic以下等几个版本中, 使用gazebo中的差速驱动,会发现左右轮出现出现了颠倒,要把joint相关配置反过来, 这点非常容易导致后续导航出问题。
29.7 关于导航参数的具体配置可以参考这个网址: http://wiki.ros.org/navigation/Tutorials/RobotSetup,
关于本地路径规划可以参考以下网址:http://wiki.ros.org/base_local_planner,
dwa算法参考网址: http://wiki.ros.org/dwa_local_planner,在launcn中,通过将base_local_planner参数赋值为 dwa_local_planner/DWAPlannerROS来选择dwa路径算法包。<launch> <node pkg="move_base" type="move_base" respawn="false" name="move_base_node" output="screen"> <param name="footprint_padding" value="0.01" /> <param name="controller_frequency" value="10.0" /> <param name="controller_patience" value="3.0" /> <param name="oscillation_timeout" value="30.0" /> <param name="oscillation_distance" value="0.5" /> <!-- <param name="base_local_planner" value="dwa_local_planner/DWAPlannerROS" /> --> <rosparam file="$(find navigation_stage)/move_base_config/costmap_common_params.yaml" command="load" ns="global_costmap" /> <rosparam file="$(find navigation_stage)/move_base_config/costmap_common_params.yaml" command="load" ns="local_costmap" /> <rosparam file="$(find navigation_stage)/move_base_config/local_costmap_params.yaml" command="load" /> <rosparam file="$(find navigation_stage)/move_base_config/global_costmap_params.yaml" command="load" /> <rosparam file="$(find navigation_stage)/move_base_config/base_local_planner_params.yaml" command="load" /> <!-- <rosparam file="$(find navigation_stage)/move_base_config/dwa_local_planner_params.yaml" command="load" /> --> </node> </launch>
两种路径规划控制的算法主要包括以下流程:
Discretely sample in the robot’s control space (dx,dy,dtheta) #控制空间进行速度采样
For each sampled velocity, perform forward simulation from the robot’s current state to predict what would happen if the sampled velocity were applied for some (short) period of time. #前向预测机器人采样速度可能发生的状况。
Evaluate (score) each trajectory resulting from the forward simulation, using a metric that incorporates characteristics such as: proximity to obstacles, proximity to the goal, proximity to the global path, and speed. Discard illegal trajectories (those that collide with obstacles). #计算采样速度造成的结果的分数,抛弃非法路径
Pick the highest-scoring trajectory and send the associated velocity to the mobile base. # 选取分数最高的路径,然后发送速度控制。
Rinse and repeat.通过蒙特卡洛定位的学习: http://wiki.ros.org/amcl
机器人模型的base_footprint会出现不停的抖动? 因为通过蒙特卡洛定位的时候会发布map到base的tf, gmapping也会发布同一个tf, 那么就会导致tf不断地抖动,这个可以通过 rosrun rqt_tf_tree rqt_tf_tree 去调试查看。
29.8 局部代价地图的学习参考以下链接:http://wiki.ros.org/costmap_2d, 注意局部代价地图可以通过引入自定义传感器来添加避障范围。包括 sensor_msgs/PointCloud,sensor_msgs/PointCloud2, 代价地图分为几个主要的层:插件: Static Map Layer Obstacle Map Layer:支持sensor_msgs/PointCloud,sensor_msgs/PointCloud2,可支持voxel数据显示三维障碍。 Inflation Layer: 膨胀层 Other Layers :Social Costmap Layer, Range Sensor Layer(测距传感器) 其添加层的基本实现如下: 1. 自定义添加 层的数据结构的相关代码。 2. 在cost_map_params的参数配置文件添加以下配置: plugins: - {name: static_map, type: "costmap_2d::StaticLayer"} #添加层插件,name为层的名字,type为显示层的数据结构 - {name: obstacles, type: "costmap_2d::VoxelLayer"} publish_frequency: 1.0 obstacles: #指定obstacle插件层,每个层都能支持多种数据显示 #该层的输入数据类型 observation_sources: base_scan #该输入数据类型的相关描述 base_scan: {data_type: LaserScan, sensor_frame: /base_laser_link, clearing: true, marking: true, topic: /base_scan}