ROS
echo "source /opt/ros/ros版本号/setup.bash" >> ~/.bashrc
ROS的环境配置,使得你每次打开一个新的终端,ROS的环境变量都能够自动配置好,也就是添加到bash会话中,因为命令source topt/ros/kinetic/setup.bash
只在当前终端有作用,即具有单一时效性,要想每次新开一个终端都不用重新配置环境,就用echo语句将命令添加到bash会话中。
换句话说,如果不用上面这句进行环境配置,那么每次打开终端需要source /opt/ros/ros版本号/setup.bash"
才能激活ros环境。
执行这条运行后,关闭终端,再打开新的终端时就会激活ROS环境;或者直接在当前终端source ~.bashrc
也可以直接激活终端,不必注销重登陆
catkin_make:调用系统自动完成编译和链接过程
rosinstall
锁存器
由于/map
中实际上存储的是一张图片,为了减少不必要的开销,这个Topic往往采用锁存(latched)的方式来发布。什么是锁存?其实就是:地图如果没有更新,就维持着上次发布的内容不变,此时如果有新的订阅者订阅消息,这时只会收到一个/map
的消息,也就是上次发布的消息;只有地图更新了(比如SLAM又建出来新的地图),这时/map
才会发布新的内容。 锁存器的作用就是,将发布者最后一次发布的消息保存下来,然后把它自动发送给后来的订阅者。这种方式非常适合变动较慢、相对固定的数据(例如地图),然后只发布一次,相比于同样的消息不定的发布,锁存的方式既可以减少通信中对带宽的占用,也可以减少消息资源维护的开销。
文件系统级的概念
catkin + 工作空间
catkin:用来编译ros程序的
catkin工作空间:是一个文件夹
初始化catkin工作空间
首先创建带有src文件夹的catkin工作空间。然后在catkin——ws下catkin——make
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/
$ catkin_make #初始化工作空间,更重要的是编译的作用
- src/: ROS的catkin软件包(源代码包)
- build/: catkin(CMake)的缓存信息和中间文件
- devel/: 生成的目标文件(包括头文件,动态链接库,静态链接库,可执行文件等)、环境变量
只有src是我们真正写代码的地方(们写的ROS程序、网上下载的ROS源代码包都存放在这里。),写完后catkin_make一下,build和devel都是自动生成。
package
是catkin编译系统
的基本单元,catkin编译系统
会递归地查找每一个package
,因为是递归的,所以可以把多个package
放在同一个文件夹下,如上图也是可以的。
一个catkin的软件包(package)必须包含两个文件
package.xml:包含了package的描述信息
CMakeLists.txt:构建package所需的CMake文件
package.xml
catkin编译的工作流程
catkin_ws/src/
下递归的查找其中每一个ROS的package。package.xml
和CMakeLists.txt
文件,Catkin(CMake)编译系统依据CMakeLists.txt
文件,从而生成makefiles
(放在catkin_ws/build/
)。make
刚刚生成的makefiles
等文件,编译链接生成可执行文件(放在catkin_ws/devel
)。也就是说,Catkin就是将cmake
与make
指令做了一个封装从而完成整个编译过程的工具。catkin有比较突出的优点,主要是:
catkin三步代码
cd xxx/catkin_ws # 回到工作空间,catkin_make必须在工作空间下执行
catkin_make # 开始编译
source xxx/catkin_ws/devel/setup.bash # 刷新环境
catkin编译之前需要回到工作空间目录,catkin_make在其他路径下编译不会成功。编译完成后,如果有新的目标文件产生(原来没有),那么一般紧跟着要source刷新环境,使得系统能够找到刚才编译生成的ROS可执行文件。这个细节比较容易遗漏,致使后面出现可执行文件无法打开等错误。
- ROS软件的基本组织形式
- catkin编译的基本单元,任何ROS程序只有组织成package才能编译。package也是ROS源代码存放的地方,任何ROS的代码无论是C++还是Python都要放到package中,这样才能正常的编译和运行。
- 一个package可以包含多个可执行文件(节点),即一个package可以编译出来多个目标文件(ROS可执行程序、动态静态库、头文件等等)。
一个package下常见的文件、路径有:
一个package下常见的文件、路径有:
├── CMakeLists.txt #package的编译规则(必须)
├── package.xml #package的描述信息(必须)
├── src/ #源代码文件
├── include/ #C++头文件
├── scripts/ #可执行脚本
├── msg/ #自定义消息
├── srv/ #自定义服务
├── models/ #3D模型文件
├── urdf/ #urdf文件
├── launch/ #launch文件,一般以.launch或.xml结尾。一个package下可以有多个可执行文件,我们把它们组织到launch文件,帮我们一次运行多个可执行文件
├── config/ #配置文件,如*ymal文件,参数、设置
其中定义package
的是CMakeLists.txt
和package.xml
,这两个文件是package
中必不可少的(其余路径根据软件包是否需要来决定。)。catkin编译系统在编译前,首先就要解析这两个文件。这两个文件就定义了一个package。
要在catkin_ws/src下,用到catkin_create_pkg命令
catkin_create_pkg package depends
# 其中package是包名,depends是依赖的包名,可以依赖多个软件包。
# 举例
catkin_create_pkg test_pkg roscpp rospy std_msgs
rospack find package_name 查找某个package的地址
rospack list 列出本机左右pkg
roscd,cd的改进版
roscd package_name 跳转到某个pkg的路径下
rosls,ls的改进版
rosls package_name 列举某个package下的文件信息
rosdep,管理ROS package依赖项
rosdep install [pkg_name] 安装某个pkg所需的依赖
# 用于安装从github上clone的package所需的依赖,因为我们自己catkin_create_pkg创建的package已经指定了依赖了
rosed
rosed package_name file_name 编辑pkg中文件
catkin_create_pkg
catkin_create_pkg <pkg_name> [deps] 创建一个pkg
cmake_minimum_required() #CMake的版本号
project() #项目名称
find_package() #找到编译需要的其他CMake/Catkin package
catkin_python_setup() #catkin新加宏,打开catkin的Python Module的支持
add_message_files() #catkin新加宏,添加自定义Message/Service/Action文件
add_service_files()
add_action_files()
generate_message() #catkin新加宏,生成不同语言版本的msg/srv/action接口
catkin_package() #catkin新加宏,生成当前package的cmake配置,供依赖本包的其他软件包调用
add_library() #生成库
add_executable() #生成可执行二进制文件
add_dependencies() #定义目标文件依赖于其他目标文件,确保其他目标已被构建
target_link_libraries() #链接
catkin_add_gtest() #catkin新加宏,生成测试
install() #安装至本机
有两个版本,新版本将之前的build和run依赖项描述进行了细分。
版本1内容:
<pacakge> 根标记文件
<name> 包名
<version> 版本号
<description> 内容描述
<maintainer> 维护者
<license> 软件许可证
<buildtool_depend> 编译构建工具,通常为catkin
<build_depend> 编译依赖项,与Catkin中的
<run_depend> 运行依赖项
一般我们要修改build_depend
和 run_depend
rosbuild编译系统采用的包信息清单,类似catkin的package.xml
比如,navigation
这个包中有一个文件夹叫做navigation
,这是一个虚包,里面只有一个package.xml
,一个 CMakeLists.txt
(最后一行表示它是个metapackage
), 一个log,一个README.
navigation 的metapakge,它的作用就是让我们安装的时候更方便,比如我们要用apt-get
的方式安装navigation的位二进制软件包,就不需要一个一个去install
,直接用ros-melodic-navigation
比如,sudo apt-get install ros-melodic-desktop-full
这个包就是metapackage
.由于它依赖了ROS所有的核心组件,我们在安装时也就能够安装整个ROS。
ROS程序中有可能有一些自定义的消息/服务/动作文件,为程序的发者所设计的数据结构,这类的文件以.msg
,.srv
,.action
结尾,通常放在package的msg/
,srv/
,action/
路径下。
urdf/xacro
文件是机器人模型的描述文件,以.urdf
或.xacro
结尾。它定义了机器人的连杆和关节的信息,以及它们之间的位置、角度等信息,通过urdf文件可以将机器人的物理连接信息表示出来。并在可视化调试和仿真中显示。
yaml文件一般存储了ROS需要加载的参数信息,一些属性的配置。通常在launch文件或程序中读取.yaml文件,把参数加载到参数服务器上。通常我们会把yaml文件存放在param/
路径下
dae或stl文件是3D模型文件,机器人的urdf或仿真环境通常会引用这类文件,它们描述了机器人的三维模型。相比urdf文件简单定义的性状,dae/stl文件可以定义复杂的模型,可以直接从solidworks或其他建模软件导出机器人装配模型,从而显示出更加精确的外形。
rviz文件本质上是固定格式的文本文件,其中存储了RViz窗口的配置(显示哪些控件、视角、参数)。通常rviz文件不需要我们去手动修改,而是直接在RViz工具里保存,下次运行时直接读取。
在ROS的世界里,最小的进程单元就是
节点(node)
。一个软件包里可以有多个可执行文件
,可执行文件在运行之后就成了一个进程(process)
,这个进程在ROS中就叫做节点。
我们在1.4节打开了小海龟的运动程序和键盘控制程序,在1.5节同样启动了键盘运动程序,这每一个程序便是一个node。
ROS系统中不同功能模块之间的通信,也就是节点间的通信。
我们可以把键盘控制替换为其他控制方式,而小海龟运动程序、机器人仿真程序则不用变化。这样就是一种模块化分工
的思想。
利用ROS提供给我们的节点管理器master, master在整个网络通信架构里相当于管理中心,管理着各个node。
当ROS程序启动时,第一步首先启动master,由节点管理器处理依次启动node。
可执行文件是静态的,当系统执行这些可执行文件,将这些文件加载到内存中,它就成为了动态的node。
1. 一般运行ROS,通过下面语句
roscore # 启动ros master
rosrun pkg_name node_name # 具体启动node的语句
roslaunch
命令首先会自动进行检测系统的roscore
有没有运行,也即是确认节点管理器是否在运行状态中,如果master
没有启动,那么roslaunch就会首先启动master,然后再按照launch的规则执行。launch文件里已经配置好了启动的规则。 所以roslaunch就像是一个启动工具,能够一次性把多个节点按照我们预先的配置启动起来,减少我们在终端中一条条输入指令的麻烦。
string
,就是一个频道Publisher
发布/camera-rgb
这个tiopic
*,即采集图像,node2负责图像处理,node1生成图像就完事了,不用等node2处理,也不用管有没有节点接收/camera-rgb
这个tiopic
,经过节点管理器的介绍,它就能建立和摄像头节点(node1)的连接。只要有消息从这个topic
里过来,我就处理它,不管谁发布的。topic
可以同时被多个node(subscriber)
订阅node(publisher)
可以同时向一个topic
发布Message不单单指一条发布或者订阅的消息,也指定为topic的格式标准.
每一个topic
是一个实例
在sensor_msgs/msg/image.msg里,它的结构如下:
std_msg/Header header
uint32 seq
time stamp
string frame_id
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
/
开头Message
不仅仅是我们平时理解的一条一条的消息,而且更是ROS中topic
的格式规范
。或者可以理解msg
是一个“类”
,那么我们每次发布的内容可以理解为“对象”
当一些节点只是临时而非周期性的需要某些数据,如果用topic通信方式时就会消耗大量不必要的系统资源,造成系统的低效率高功耗。
string类型
来指定service的名称,类似于topic。同步:
clien发完一个请求后会在原地等待reply,直到server返回结果(request)。等待过程中,client是处于阻塞状态
的。这样的通信模型没有频繁的消息传递,没有冲突与高系统资源的占用,只有接受请求才执行服务,简单而且高效。类似msg文件,srv文件
是用来描述服务(service数据类型
的,service通信的数据格式定义在*.srv中。它声明了一个服务,包括请求(request)和响应(reply)两部分。其格式声明如下:
my_pkg/srv/DetectHuman.srv # 必须放在srv文件夹下
bool start_detect # 请求服务
--- # 三个短横线分开
my_pkg/HumanPose[] pose_data # 应答,数组,因为可能不止一个人
my_pkg/msg/HumanPose.msg # srv只能嵌套一个msg,不能在嵌套一个srv,所以上面应答中的HumanPose文件如下:
std_msgs/Header header
string uuid
int32 number_of_joints
my_pkg/JointPose[]joint_data # 多个关节 所以用数组 又有了`下面的msg`
my_pkg/msg/JointPose.msg
string joint_name
geometry_msgs/Pose pose
floar32 confidence
roslaunch robot_sim_demo robot_spawn.launch
。rosservice list
,查看当前运行的服务。/gazebo/delete_light
服务,观察名称,是删除光源的操作。rosservice info /gazebo/delete_light
查看属性信息。可以看到信息,Node:/gazebo,Type:gazebo_msgs/DeleteLight, Args:Light_name。这里的类型type也就是下文介绍的srv,传递参数Light_namerosservice call /gazebo/delete_light sun
,这里的sun 是参数名,使我们模拟场景中的唯一光源太阳。操作完成后可以看到场景中的光线消失。
- 有别于异步通信和同步通信的通信方式
- 维护着一个数据字典,字典里存储着各种参数和配置
- 更加的静态
- 可用命令行,launch文件和node(API)读写
load和dump文件需要遵守YAML格式,YAML格式具体示例如下:
name:'Zhangsan'
age:20
gender:'M'
score{Chinese:80,Math:90}
score_history:[85,82,88,90]
简明解释。就是“名称+:+值”
这样一种常用的解释方式。一般格式如下:
key : value
遵循格式进行定义参数。其实就可以把YAML文件的内容理解为字典,因为它也是键值对的形式。
小车导航
,机械臂运动A点到B点
)、可抢占(任务执行到一半可被打断
)**的任务中Action规范文件的后缀名是.action,它的内容格式如下:
# Define the goal
uint32 dishwasher_id # Specify which dishwasher we want to use
---
# Define the result
uint32 total_dishes_cleaned
---
# Define a feedback message
float32 percent_complete
service
只能由一个server
提供.action
为三段式: #Goal --- #Result --- #Feedback 而`.srv`为两段式: #Request --- #Reply
RViz和Gazebo有本质的差异:
传感器数据
。左侧的插件相当于是一个个的subscriber,RViz接收信息,并且显示。1. ros::init() // 使用roscpp第一步要用到的函数
void ros::init(); // 解析ROS参数,为本node命名
2. ros::NodeHandle Class // 是一个类
3. ros:master Namespace //是一个命名空间,**Namespace和Class的区别**在与,调用它的函数是没有对象的,不需要创建对象,不是类的用法。比如直接
`ros::master::check();`就可以调用函数
4. ros::this_node Namespace
5. ros::service Namspace
6. ros::names Namespace
参考
wait_for_service
无返回值wait_for_message
返回一个messagerospy call
部分笔记,得知请求有两种。ServiceProxy()
与cpp
里的不要弄混。#TF与URDF
- TF:坐标转换
- URDF:统一机器人描述格式,定义了具体的机器人模型,机器人自身的属性
/tf
,话题中的message
保存的就是tf tree
的数据结构格式)、工具(tf
可看成一个package
,里面包含很多工具,比如可视化、查看关节间的tf
)、接口(roscpp rsopy里关于坐标变换的
API
)tf tree
frame
之间都有一个broadcaster
,即为了使得两个frame
之间能够正确连通,中间都会有个node
来发布消息来broadcaster
。 broadcaster
就是一个publisher
,两个frame
之间发生相对运动,broadcaster
就会发布相关消息。消息TransformStamped.msg
是处理两个frame之间一小段tf的数据格式.格式规范如下:
std_mags/Header header
uint32 seq
time stamp
string frame_id
string child_frame_id
geometry_msgs/Transform transform
geometry_msgs/Vector3 translation
float64 x
float64 y
float64 z
geometry_msgs/Quaternion rotation
float64 x
float64 y
flaot64 z
float64 w
TF tree是由很多的frame之间TF拼接而成。那么TF tree是什么类型呢?如下:
rostopic info /tf
transformBroadcaster()
void sendTransform(const StampedTransform &transform)
void sendTransform(const std::vector &transforms)
void sendTransform(const geometry_msgs::TransformStamped &transform)
void sendTransform(const std::vector &transforms)
void lookupTranform(const std::string &target_frame,const std::string &source_frame,const ros::Time &time,StampedTransform &transform)const
bool canTransform()
bool waitForTransform()const
根据当前的tf树创建一个pdf图:
$ rosrun tf view_frames
这个工具首先订阅/tf
,订阅5秒钟,根据这段时间接受到的tf信息,绘制成一张tf tree,然后创建成一个pdf图。
查看当前的tf树:
$ rosrun rqt_tf_tree rqt_tf_tree
该命令同样是查询tf tree的,但是与第一个命令的区别是该命令是动态的查询当前的tf tree,当前的任何变化都能当即看到,例如何时断开何时连接,捕捉到这些然后通过rqt插件显示出来。
查看两个frame之间的变换关系:
$ rosrun tf tf_echo[reference_frame][target_frame]
launch
文件一样gazebo
进行仿真时, 需要加载的机器人模型就是urdf模型moveit
对机器人进行路径规划, 在moveit setup assistant
教程中, 第一步就是关于如何将机器人模型导入进来, 导入的机器人模型就是urdf
(导入xacro
格式时也是先将其解析为urdf
).xacro文件
, 是提供了一些更为高级编辑方式的宏文件
. 这种格式的文件, 在使用时, 均先会调用rosrun xacro xacro.py xxx.urdf.xacro > xxx.urdf
, 将其解析成对应的urdf文件. 然后再使用.link
和 joint
joint
中定义parent link
和child link
link
所在位置,用origin
子标签link
形状,用origin
子标签