》ros工程结构
catkin工作空间:组织和管理功能包的文件夹(工作空间:管理和组织ros工程代码的地方)
catkin,编译构建系统,cmake的扩展
catkin workspace,用指令创建
建立工作空间
$mkdir -p ~/catkin_ws/src #必须要有src这个目录
$cd ~/catkin_ws/
$catkin_make
编译
$cd ~/catkin_ws #catkin_make之前一定要回到工作空间
$catkin_make
$source ~/catkin_ws/devel/setup.bash #把编译完成的workspace刷新到环境空间,让系统知道ros可执行文件的位置
catkin workspace中有src(package源代码),build(缓存和中间文件),devel(生成的目标文件:头文件,链接库,可执行文件)
src:代码空间
build:编译空间,放置编译过程中的中间文件
devel:开发空间,放置编译生成的可执行文件
src-build-devel
src里面放package。package是catkin编译的基本单元,catkin编译的对象就是package *递归查找package
》package
ros的基本组织形式,catkin编译的基本单元。一个package包含多个可执行文件(节点)
package中至少有CMakeList.txt和package.xml,还有scripts,include,src,msg,srv,action,launch,config
CMakeList.txt 规定catkin编译规则(讲义第二章) (看cmake实践书籍)
package.xml 定义package属性(讲义第二章)
代码文件:可执行脚本(shell脚本和python脚本),C++文件(头文件和源文件)
在package下创建scripts(放代码文件),include(*.h)和src(*.cpp)放头文件和源文件。
urdf/:存放机器人的模型描述(.urdf或.xacro)
自定义通信格式:消息msg(*.msg),服务srv(*.srv),动作action(*.action) #放在package下
launch文件(*.launch)(讲义第三章) #一次性运行多个可执行文件
配置文件config(*.yaml)
》常用指令
1.rospack
查找某个pkg的地址
$rospack find package_name
列出本地所有pkg
$rospack list
2.roscd
跳转到某个pkg路径下
$roscd package_name
3.rosls
列举某个pkg下的文件信息
$rosls package_name
4.rosed
编辑pkg中的文件
$rosed package_name file_name
5.catkin_create_pkg
创建一个pkg
$catkin_create_pkg <pkg_name> [deps]
6.rosdep
安装某个pkg所需的依赖
$rosdep install [pkg_name]
检查package的依赖是否满足
$rosdep check [pkg_name]
生成和显示依赖数据库
$rosdep db
初始化/etc/ros/rosdep中的源
$rosdep init
检查package的依赖是否满足
$rosdep keys
更新本地的rosdep数据库
$rosdep update
》metapackage(讲义第二章)
navigation(导航),moveit(运动规划),image_pipeline(图像获取处理),turtlebot,pr2_robot
metapackage把有相似功能的软件包组织起来
metapackage名称:navigation,moveit,image_pipeline,vision_opencv,turtlebot,pr2_robot
》ros通信架构(计算图级)
节点管理器master,每个node启动都要向master注册,管理node之间的通信
启动Ros Master,用roscore
node启动会向master注册,node是ros可执行文件运行的实例,node是ros的进程
roscore #同时启动ros master,parameter server,rosout
$roscore
rosout 日志输出
parameter server 参数服务器 #做参数配置
rosrun #启动一个node
$rosrun [pkg_name] [node_name]
rosnode #管理和查看node
列出当前运行的node信息
$rosnode list
显示某个node的详细信息
$rosnode info [node_name]
结束某个node
$rosnode kill[node_name]
测试连接节点
$rosnode ping
列出在特定机器或列表机器上运行的节点
$rosnode machine
清除不可到达节点的注册信息
$rosnode cleanup
roslaunch
启动master和多个node
$roslaunch [pkg_name][file_name.launch]
》通信方式
topic,service,parameter service,actionlib
topic前面都是/开头
topic通信是单向的
一个topic可以被发布而无人接收,也可以一直被接收而无人发布。
一个node可以发布多个topic。
一个topic可以有多个subscribers。
topic(string):
异步通信方式,会调用publish(),立即返回,不用等待处理结果
通信模式:publish-subscribe(多对多)
可以有多个subscribers,对于同一个topic可以有多个publishers
处理方式:接受者(subscriber)收到数据会回调(callback)
应用:连续、高频的数据发布
message是topic的格式标准,数据类型。msg类型:bool,int8,int16,int32,int64,float32,float64,string,time,duration,header,array.
列出当前所有topic
$rostopic list
显示某个topic的属性信息
$rostopic info /topic_name
显示某个topci的内容
$rostopic echo /topic_name
向某个topic发布内容
$rostopic pub /topic_name
$rostopic pub+话题名+话题类型+需要改的参数以及值
查看某个topic的宽带
$rostopic bw topic_name
查看某个topic的频率
$rostopic hz topic_name
查找某个类型的topic
$rostopic find topic_type
查看某个topic的类型
$rostopic type topic_name
列出系统上所有msg #(常见msg类型在讲义第三章)
$rosmsg list
显示某个msg内容
$rosmsg show /msg_name
service:
service通信是双向的
同步通信方式
node之间可以通过request-reply方式通信
通信模式:client-server(多对一)
处理方式:远程过程调用(PRC)服务器端的服务(在一个进程里面调用另一个进程的函数)
应用:偶尔调用的功能
srv是service的数据格式(srv文件要放在srv路径下)(srv只能嵌套msg,不能嵌套srv)
srv和msg要修改package.xml和CMakeList.txt,加上了srv和msg在编译和运行时需要的依赖(视频13)
列出当前所有活跃的service
$rosservice list
显示某个service的属性信息
$rosservice info service_name
调用某个service
$rosservice call service_name args
列出系统上所有srv
$rossrv list
显示某个srv内容
$rossrv show srv_name #区分rosservice和rossrv,rossrv指的是数据类型,类比rosmsg
(常见srv类型在讲义第四章)
.srv为两段式 Request---Reply
parameter server 存储各种参数的字典(映射关系),可用命令行,launch文件和node(API)读写
用命令行:
列出当前所有参数
$rosparam list
显示某个参数的值
$rosparam get param_key
设置某个参数的值
$rosparam set param_key param_value
保存参数到文件
$rosparam dump file_name #dump 的文件格式YAML,YAML格式 key:value
从文件读取参数
$rosparam load file_name #load 的文件格式YAML,YAML格式 key:value
删除参数
$rosparam delete param_key
在launch文件里:
param和rosparam标签
action
类似service,带有状态反馈的通信方式
应用:通常用在长时间、可抢占的任务中
数据格式*.action
.action分为三段,用---隔开
第一段goal,由client发布的
第二段result,server回传给client #result只传一次
第三段feedback,server回传给client #feedback是实时的状态,可以多次或持续的回传
例子:洗碗机。client给server发布goal,server给client发送feedback,反馈实时状态,goal执行完成后,server给client发送result,报告结果。
(常见action类型在讲义第四章)
》常用工具
仿真:gazebo
调试,可视化:rviz(the robot visualization tool),rqt
命令行工具:roslaunch,rostopic,rosbag
专用工具(机械臂):moveit
gazebo #仿真
ODE物理引擎,用于动力学、导航、感知等任务的模拟
gazebo官网,gazebo仿真建模,建立基本模型,设置传感器,设置gazebo插件
insert插入模型
rviz #可视化
add,remove,fixed frame,laserscan,camera,pointcloud,map,robotmodel
插件均为subscriber,接受了topic的信息并显示出来
rqt #可视化,显示更高级
rqt_graph:显示通信架构,计算图 #圆圈表示节点,箭头上是传输的topic
rqt_plot:绘制曲线 #topic /odom/twist/twist/angular
rqt_console:查看日志
rosrun rqt_按tab,查看所有rqt工具
rosbag:记录和回放数据流
记录某些topic到bag中
$rosbag record <topic-names>
记录所有topic到bag中
$rosbag record -a
回放bag
$rosbag play <bag-files>
rosbridge #使得ros和其他系统可以交互
包含协议Potocol和实现Implementation
安装
$sudo apt-get install ros- <rosdistro> -rosbridge-server
moveit #移动操作软件
$move_group
》client library #提供ros编程的库,建立node,发布消息,调用服务
roscpp(执行能力强,用于图形,slam)
要调用C++接口,需要
$#include<ros/ros.h>
ros::init() #::是作用域的限定符,表明init()在ros命名空间之中 #初始化节点的名称和其他信息
ros::NodeHandle
ros::master #节点的句柄,可以用来创建Publisher,Subscriber以及做其他事情
ros::this_node
ros::service
ros::param
ros::names
关闭节点用Ctrl+C(不好用的话试试Ctrl+Shift+C)
NodeHandle常用成员函数:
创建话题的publisher
创建话题的subscriber
创建服务的server
创建服务的client
查询某个参数的值
给参数赋值
topic_demo
两个node,一个是传感器信息,另一个接收并处理该信息。
步骤:
(1)package
$cd ~/catkin_ws/src
$catkin_create_pkg topic_demo roscpp rospy std_msgs
(2)msg
$cd topic_demo/
$mkdir msg
$cd msg
$vi gps.msg
gps.mgs内容:(gps.msg位置~/catkin_ws/devel/include/topic_demo/gps.h)
<
float32 x
float32 y
string state
>
(3)talker.cpp (讲义6.3) #全文背诵
#include<ros/ros.h>
#include<topic_demo/gps.h>
int main(int argc,char** argv){
ros::int(argc,argv,"talker" ); #解析参数,命名节点
ros::NodeHandle nh; #创建句柄,实例化node
topic_demo::gps msg; #创建gps消息
msg.x = 1.0;
msg.y = 1.0;
msg.state = "working" ;
ros::Publisher pub = nh.advertise<topic_demo::gps>("gps_info ,1); #创建publisher
pub.publish(msg); #发布消息
ros::Rate loop_rate(1.0); #定义循环发布的频率
while(ros::ok()){
msg.x = 1.03 * msg.x; #以指数增长,每隔1s
msg.y = 1.03 * msg.y;
ROS_INFO( "Talker:GPS:x = %f,y = %f , msg.x, msg.y); #输出当前msg
pub.publish(msg) #发布消息
loop_rate.sleep(); #根据定义的发布频率,sleep
}
retrun 0;
}
(4)listener.cpp (讲义6.3) #全文背诵
(5)CMakeLists.txt和package.xml
service_demo
两个node,一个发布请求,另一个接收、处理、返回信息
步骤:
(1)package
$cd ~/catkin_ws/src
$catkin_create_pkg service_demo roscpp rospy std_msgs
(2)srv
$cd service_demo/
$mkdir srv
$vi Greeting.srv
Greeting.srv内容:(位置:~/catkin_ws/devel/include/service_demo/Greeting.h)
<
string name
int32 age
---
string feedback
>
(3)server.cpp
(4)client.cpp
(5)CMakelist.txt和package.xml
param_demo
param用来存储一些静态的设置,有两种API:ros::param和ros::NodeHandle
获取参数
$ros::param::get("param1",parameter1)
$nh.getParam("param2",parameter2)
$nh.param("param3",parameter3,123) #NodeHandle会在param3上找它的值,没有找就取默认值123
设置参数
$ros::param::set("param4",parameter4)
$nh.setParam("param5",parameter5)
检查参数是否存在
$ros::param::has("param5")
$nh.hasParam("param6");
删除参数
$ros::param::del("param5")
nh.deleteParam("param6")
Time,Duration
$#include<ros/time.h>
$#include<ros/duration.h>
在roscpp中进行日志的输出,需要
$#include<ros/console.h>
rospy(开发效率高)(node,topic,service,param,time)
首先在python文件中 import rospy
日志输出 rospy.loginfo()
rospy-node:
init_node(name) #注册和初始化node,无返回值
get_master() #获得master的句柄,返回值masterproxy
is_shutdown() #返回是否关闭,返回值bool
on_shutdown(fn) #在node关闭时调用函数,无返回值
get_node_uri() #返回节点的URI,返回值str
get_name() #返回本节点的全名,返回值str
get_namespace() #返回本节点的名字空间,返回str
rospy-topic:
函数:
get_published_topics() #返回正在被发布的所有topic名称和类型,返回[str,str]
spin() #触发topic或service的处理,会阻塞直到关闭,无返回值(rospy里面没有spinonce())
wait_for_message(topic.topic_type,time_out_None) #等待指定topic的一个message,返回message
Publisher类:
_init_(self,name,data_class,queue_size=None) #构造函数,queue_size=none为同步通信方式,queue_size=1为异步通信方式)
publish(self,msg) #成员函数 发布信息
unregister(self) #成员函数 停止发布
Subscriber类:
_init_(self,name,data_class,call_back=None,queue_size=None) #构造函数
unregister(self) #成员函数 停止订阅
rospy-service:
函数:
wait_for_service(service,timeout=None) #阻塞直到服务可用,没有返回值
Service类(Server端):
_init_(self,name,service_class,handler) #构造函数 提供服务,没有返回值(handler绑定在server这端)
shutdown(self) #成员函数 关闭函数,没有返回值
ServiceProxy类(Clident端):
_init_(self,name,service_class) #构造函数 服务的请求方,没有返回值
call(self,*args,**kwds) #调用服务,返回值Response
_call_(self,*args,**kwds) #调用服务,返回值Response
rospy-param:
get_param(param_name,default=_unspecified) #获取参数的值,返回值value
get_param_names() #获取参数的名称,返回值[str]
set_param(param_name,param_value) #设置参数的值,没有返回值
delete_param(param_name) #删除参数,没有返回值
has_param(param_name) #参数是否存在于参数服务器上,返回值bool
search_param() #搜索参数,返回值[str]
rospy-time:
Time类(时刻):
_init_(self,secs=0,nsecs=0) #构造函数,没有返回值
now() #静态方法,返回当前时刻的Time对象
eg:rospy.Time(secs=1,nsecs=0) 初始时间1秒,0纳秒
Duration类(一段时间):
_init_(self,secs=0,nsecs=0) #构造函数,秒和纳秒
eg:rospy.Duration(1,0) 这段时间有1秒,0纳秒
Time和Duration关系:
Time-Time=Duration
Time+Duration=Time
Duration+Duration=Duration
Duration-Duration=Duration
函数:
get_rostime() #当前时刻的Time对象,返回值Time
get_time() #返回当前时间,返回值float,单位秒
sleep(duration) #执行挂起
Rated类:
_init_(self,grequency) #构造函数
sleep(self) #挂起,考虑上一次的rate.sleep()时间
remaining(self) #成员函数,剩余sleep时间,返回值Time
topic_demo
service_demo
》tf(transform)坐标变换(位置和姿态)
tf:坐标系数据维护的工具,坐标转换的标准、话题、工具、接口(roscpp,rospy)
部件link对应一个frame
tf本质是树状的数据结构,称为tf tree
每两个frame之间都有一个broadcaster,保证正常连通,broadcaster就是一个publisher
TransformStamped.msg(两个frame之间的数据格式)#依赖于ros的msg,与语言无关
tf tree的类型:tf/tfMessage.msg和tf2_msgs/TFMessage.msg
判断是第几代msg
$rostopic info /tf
一个TranformStamped数组就是一个TF tree
tf in c++:
tf数据类型
向量 tf::Vector3
点 tf::Point
四元数 tf::Quaternion
3✖3矩阵(旋转矩阵) tf::Matrix3x3
位姿 tf::Pose
变换 tf::Transform
带时间戳的以上类型 tf::Stamped<T>
带时间戳的变换 tf::StampedTransform #是c++的数据格式,不是一个msg;Transform Stamped.msg依赖于ros,与语言无关
tf::TransformBroadcaster类(发送)
tf::TransformListener类(接收)
void lookupTranform(const std::string &target_frame,const std::string &source_frame,const ros::Time &time,StampedTransform &transform)const
bool canTransform() #判断两个transform是否连通
bool waitForTransform()const #等待某两个transform之间连通
ros::Time(0)是最新的
tf in python:
Tuple,List,Numpy,Array通用
tf.transformations #基本数学运算函数,使用时
$import tf
tf::TransformListener类
canTransform(self,target_frame,source_frame,time) #frame是否相通
waitForTransform(self,target_frame,source_frame,time) #阻塞直到frame相通
lookupTransform(self,target_frame,source_frame,time) #查看相对的tf,返回(trans,quat)
tf::TransformBroadcaster类
sendTransform(translation,rotation,time,child,parent) #向/tf发布消息
sendTransformMessage(transform) #向/tf发布消息
根据当前的tf树创建一个pdf图
$rosrun tf view_frames #订阅5s
查看当前的tf树
$rosrun rqt_tf_tree rqt_tf_tree #动态的
查看两个frame之间的变换关系
$rosrun tf tf_echo [reference_frame][target_frame]
Ctrl+z停止
urdf(Unified Robot Description Format)机器人描述格式
应用:tf、可视化、仿真
<link> inertial,visual,collision
<joint> origin parent
》slam(simutaneous localization and mapping)同步定位和建图
移动机器人的任务:mapping,localizayion,path planning
(slam包括mapping,localization)
slam算法包:Gmapping,Karto,Hector,Cartographer
Map
Topic:/map
Type:nav_msgs/OccupancyGrid
Gmapping
核心node:slam_gamapping
/tf以及/tf_static: 坐标变换,类型为第一代的tf/tfMessage或第二代的tf2_msgs/TFMessage 其中一定得提供的有两个tf,一个是base_frame与laser_frame之间的tf,即机器人底盘和激光雷达之间的变换;一个是base_frame与odom_frame之间的tf,即底盘和里程计原点之间的坐标变换。odom_frame可以理解为里程计原点所在的坐标系。
slam_gmapping会从/tf中获得机器人里程计odom的数据
Gmapping Service
Sercice:dynamic map
Type:nav_msgs/GetMap
Karto
slam_karto
里程计:map,odom,base
odom_frame与base_frame之间的连通:encoder,visual odom,Imu #累积误差
map_frame与odom_frame:slam #误差修正
Odometry Localization
map和odom之间是固定的(fixed)会有累计误差
》navigation stack(metapackage)
路径规划,导航,机器人运动状态调整
https://github.com/ros-planning/navigation
amcl自适应蒙特卡洛定位
move_base全局规划(静态)、局部规划(动态)、处理异常行为(讲义10.2)
全局规划(base local planner):base_local_planner,dwa_local_planner
局部规划(base global planner):carrot_planner,navfn,global_planner
处理异常行为(recovery behavior):clear_costmap_recovery,rotate_recovery,move_slow_and_clear
设置的三个参数:base_global_planner,base_local_planner,recovery_behavior
costmap(代价地图)
用于路径规划,两张map(local_costmap,global_costmap)、2维、多层(是move_base的插件)
#map是全局的,costmap是用于导航的,在原来的map基础上建立
costmap的layer:
static layer:静态,不变
obstacle layer:动态,支持3D点云的投影
inflation layer:膨胀,把安全范围扩大,防止外壳碰
#yaml配置文件了解
MapServer提供地图,搭配AMCL自适应蒙特卡洛定位(用于定位)
发布topic:
/map_metadata: 发布地图的描述信息
/map: 发布锁存的地图消息
提供service:(amcl的服务只有一个)
$static_map #用于请求和响应当前的静态地图
设置param:
$~frame_id(string,default:"map")
启动map_server,发布地图
$rosrun map_server map_server my_map.yaml
保存地图
$rosrun map_server map_saver [-f my_map]
AMCL需要tf,scan,map,amcl_pose...