1 工程结构
1.1 Catkin工作空间与编译系统
1.1.1 创建工作空间catkin_ws
$ mkdir -p ~/catkin_ws/src # mkdir -p 可以一次创建多级文件夹
$ cd ~/catkin_ws/
$ catkin_make # 建立工作空间
1.1.2 编译
$ cd ~/catkin_ws # 回到工作空间
$ catkin_make
$ source -/catkin_ws/devel/setup.bash # 把新建的工作空间刷新到环境变量中
1.1.3 工作空间下级文件夹
src: package源代码
build: cmake&catkin缓存和中间文件
devel: 目标文件 { 头文件、动态链接库、静态链接库、可执行文件
1.2 Package组成
1.2.1 最精简结构:CMakeList.txt 和 package.xml
CMakeList.txt:规定catkin编译的规则,例如 源文件、依赖项、目标文件
具体语法介绍参考:https://blog.csdn.net/afei__/article/details/81201039
package.xml:如果要更改只需要<build_depend>(编译时的依赖),<run_depend>(运行时的依赖)
1.2.2 代码文件
scripts:python文件( *.py),shell文件( *.sh)
include:C++头文件( *h.)
src:C++源文件( *.cpp),python modle(可被import的python文件)
1.2.3 自定义通信格式
新建 srv 文件夹:存放 *.srv 文件
新建 msg 文件夹:存放 *.msg 文件
新建 action 文件夹:存放 *.action 文件
1.2.4 launch以及配置文件
*.launch 文件:同时运行多个可执行文件,放在package下的launch路径中
*.yaml 配置文件:配置文件,放在package下的config路径中
1.3 常用指令
1. rospack
$ rospack find package_name:查找某个pkg的地址
$ rospack list:列出本地所有pkg
2. rosch
$ rosch package_name:跳转到某个pkg路径下
3. rosls
$ rosls package_name:列举某个pkg下的文件信息
4. rosed
$ rosed package_name file_name:编辑pkg中的文件
5. catkin_create_pkg
$ catkin_create_pkg<pkg_name>:创建一个pkg
6. rosdep
$ rosdep install [pkg_name]:安装某个pkg所需的依赖
2 通信架构
2.1 Master和Node
Master:每个Node启动时都要向Master注册;管理各Node之间的通信
$ roscore:启动Master(节点管理器),同时启动 rosout(日志输出)、parameter server(参数服务器)
--------------------
Node:ROS的进程;pkg里的可执行文件运行的实例
$ rosrun [pkf_name] [node_name]:启动一个node
$ rosnode list:列出当前运行node信息
$ rosnode info [node_name]:显示某个node的详细信息
$ rosnode kill [node_name]:结束某个node
--------------------
$roslaunch [pkg_name] [file_name.launch]:启动master 和多个node
2.2 通信方式
Topic(主题) Service(服务) Parameter Server(参数服务器) Actionlib(动作库)
2.2.1 Topic和Msg
Topic 是ROS中的异步通信方式。Node通过 publish—subscribe机制,以Topic为中转站的方式进行通信。
--------------------
Message 是topic中内容的数据类型,定义在 *.msg 文件中。
--------------------
$ rostopic list:列出当前所有topic
$ rostopic info [topic_name]:显示某个topic的属性信息
$ rostopic echo [topic_name]:显示某个topic的内容
$ rostopic pub [topic_name]:向某个topic发布内容
$ rosmag list:列出系统上所有的msg
$ rosmag show [msg_name]:显示某个msg内容
2.2.2 Service和srv
Service 是ROS种的同步通信方式,Node之间可以通过 reques—reply 方式通信
--------------------
srv 是Service通信的数据格式,定义在 *.srv文件中
--------------------
$ rosservice list:列出当前所有活跃的service
$ rosservice info [service_name]:显示某个service的属性信息
$ rosservice call [service_name] [args]:调用某个service参数
$ rossrv list:列出系统上所有的srv
$ rossrv show [srv_name]:显示某个srv的内容
Topic VS Service
Topic Service 通信方式 异步通信 同步通信 实现原理 TCP / IP TCP / IP 通信模型 Publish - Subscribe Request - Reply 多对多 多对一 接收者收到数据会回调 (Callback) 远程过程调用(RPC)服务器端的服务 应用场景 连续、高频的数据发布 偶尔调用的功能/具体的任务 举例 激光雷达、里程计发布数据 开关传感器、拍照、逆解计算
2.2.3 Parameter Server
Parameter Server 是存储各个参数的字典,可用命令行,launch文件和node(API)读写
Key | /rosistro | /rosversion | /use_sim_time | .... |
Value | 'kinetic' | '1.12.7' | true | .... |
$ rosparam list:列出当前所有参数
$ rosparam get [param_key]:显示某个参数的值
$ rosparam set [param_key] [param_value]:设置某个参数的值
$ rosparam dump [file_name]:保存参数到文件
$ rosparam load [file_name]:从文件读取参数
$ rosparam delete [file_name]:删除参数
在launch文件中:
<param>:<param name=" **** " value=" **** ">或<param name=" **** " command=" **** ">
<rosparam>:<rosparam file = "$指定yaml文件" command=" **** ">
2.2.4 Action
Action 类似于Service,是一种带有状态反馈的通信方式。通常用在长时间、可抢占的任务中。
--------------------
action 通信得数据格式,定义在 *.action文件中。
举例:
./action/DoDishes.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
move_base_msgs/action/MoveBase.action
# Define the goal
geometry_msgs/PoseStamped target_pose
---
# Define the result
---
# Define a feedback message
geometry_msgs/PoseStamped base_position
3 常用工具
3.1 Gazebo
应用于机器人仿真工具,ODE物理引擎,用于动力学、导航、感知等任务的模拟
3.2 RViz
The Robot Visualization tool 机器人可视化工具。可方便监控和调试
3.3 rqt
可视化工具,常用于rqt_graph(显示通信架构),rqt_plot(绘制曲线),rqt_console(查看日志)
3.4 RosBag
ROS命令行工具,记录和回放数据流
# 记录某些topic到bag中
$ rosbag record [topic_name]
# 记录所有的topic到bag中
$ rosbag record -a
# 回放bag
$ rosbag play [bag_files]
4 roscpp
4.1 Client Library 与 roscpp
roscpp 是ROS为机器人开发者们提供了不同语言的编程接口,其中C++接口叫做roscpp。
Client Libaray 是提供ROS编程的库。例如建立node,发布消息,调用服务。
void ros::init(); # 解析ROS参数,为该node命名。利用限定符“::”表示该命名实在ROS这个限定空间中的
ros::NodeHandle Class //类
//常用成员函数
# 创建topic的publisher
ros::Publisher advertise(const string &topic,uint32_t queue_size);
# 创建topic的subscriber
ros::Subscriber subscriber(const string &topic,uint32_t queue_size,void(*)(M));
# 创建服务的server
ros::ServiceServer advertiseService(const string &service,bool(*srv_func)(Mreq &,Mres &));
# 创建服务的client
ros::ServiceClient serviceClient(cont string &service_name,bool persistent=false);
# 查询某个参数的值
bool getParam(const string &key,void &val);
# 给参数赋值
bool setparam(const string &key,void val);
ros::master Namespace
//常用函数
# 检查master是否启动
bool check();
# 返回master所处的hostname
const string& getHost();
# 从master返回已知的node名称列表
bool getNodes(V_string &nodes);
# 返回所有正在被发布的topic列表
bool getTopic(V_Topiclnfo &topics);
# 返回到master的URI地址
bool getURI();
# 返回master运行在的端口
uint32_t getPort();
ros::this_node Namespace
//常用函数
# 返回本node发布的topic
void getAdvertisedTopics(V_string &topics);
# 返回当前node的名称
const string &getName();
# 返回当前node的命名空间
const string &getNamespace();
#返回当前node订阅的topic
void getSubscribedTopics();
ros::service Namespace
常用函数
# 调用一个RPC服务
bool call(const string &service,Service &service);
# 创建一个服务的client
serviceClient createClient(const string& service_name,bool persistent=false);
# 确认服务可调用
boll exists(const string &service_name,bool print_failure_reason);
# 等待一个服务,直到他可调用
bool waitForService(const string &service_name,int32_t timeout);
ros::names Namespace
//常用函数
# 追加名称
string append(const std::string &lefy,const std::string &right)
# 清除图资源名称:删除双斜杠、结尾斜杠等
string clean(const string &name);
# 返回重映射remapping
const M_string &getRemappings();
# 解析出名称的全名
string resolve(const string &name,bool remap=ture);
# 验证名称
bool validate(const string &name,string &error);
4.2 topic_demo
功能描述:两个node,一个用于发布模拟的GPS消息(格式为自定义,包括坐标和工作状态),另一个接收并处理该信息(计算到原点的距离)。
步骤:①package ②msg ③talker.cpp ④listener.cpp ⑤CMakeList.txt & package.xml
①package包含我们所有的源代码
$ cd ~/catkin_ws/src
$ catkin_create_pkg topic_demo roscpp rospy std_msgs
//在使用catkin_create_pkg时,一些依赖的功能包需要提供出来,这些直接(一级)依赖关系可以通过命令工具rospack查看:"rospack depends1 beginner_tutorials"
②msg是我们自定义的消息格式
$ cd topic_demo/
$ mkdir msg
$ cd msg
$ vi gps.msg
gps.msg
# 坐标
float32 x
float32 y
# 定义工作状态
string state
然后需要对该文件进行编译。编译完成后会在~/catkin_ws/devel/include/topic_demo下创建gps.h文件
③④我们将两个node分别放在两个cpp文件中。
talker.cpp
#include <ros/ros.h>
#include <topic_demo/gps.h>
int main(int argc,char **argv)
{
ros::init(argc,argv,"talker"); //解析参数,将node命名为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,命名为gps_info
pub.publish(msg); //发布消息
ros::Rate loop_rate(1.0); //定义循环发布的频率1Hz
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(); //控制程序休眠,从而达到以一定频率发布
}
return 0;
}
listener.cpp
#include <ros/ros.h>
#include <topic_demo/gps.h>
#include <std_msgs/Float32.h>
void gpsCallback(const topic_demo::gps::ConstPtr &msg) //长指针ConstPtr
{
std_msgs::Float32 distance;
distance.data = sqrt(pow(msg->x,2),pow(msg->y,2));
ROS_INFO("Listener:Distance to otingin = %f,state = %s",distance.data,msg->state.c_str());
}
int main(int argc,char **argv)
{
ros::init(argc,argv,"listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("gps_info",1,gpsCallback); //创建subscriber
ros::spin(); //反复调用当前可触发的回调函数,阻塞函数
return 0;
}
⑤修改CMakeList.txt & package.xml提示编译系统去编译这些文件
CMakeList.txt
package.xml
4.3 service_demo
功能描述:两个node,一个发布请求(格式自定义),另一个接收处理该信息,并返回信息。
步骤:①package ②srv ③server.cpp ④client.cpp ⑤CMakeList.txt & package.xml
①package包含我们所有的源代码
$ cd ~/catkin_ws/src
$ catkin_create_pkg service_demo roscpp rospy std_msgs
//在使用catkin_create_pkg时,一些依赖的功能包需要提供出来,这些直接(一级)依赖关系可以通过命令工具rospack查看:"rospack depends1 beginner_tutorials"
②msg是我们自定义的消息格式
$ cd service_demo/
$ mkdir srv
$ cd srv
$ vi Greeting.srv
Greeting.srv
#服务请求的数据
string name
int32 age
---
#服务回传的内容
string feedback
然后需要对该文件进行编译。
编译完成后会在~/catkin_ws/devel/include/service_demo下创建Greeting.h GreetingRequest.h GreetingResponse.h 文件
③server.cpp
#include <ros/ros.h>
#include <service_demo/Greeting.h>
bool handle_function(service::demo::Greeting::Request &req,service::demo::Greeting::Response &res) //传入的参数包含两部分,这两种都是在编译Greeting.srv时编译器自动生成的
{
//显示请求信息
ROS_INFO(“Request from %s with age %d”,req.name.c_str(),req.age); //string转化成const char*
//处理请求,并将结果写入response
res.feedback = "HI" + req.name + "I am server!";
//返回true,正确处理了请求
return true;
}
int main(int argc,char **argv)
{
ros::init(argc,argv,"Greetings_server");
ros::NodeHandle nh;
ros::ServiceServer service = nh.advertiseService("greetings",handle_function); //服务处理函数
ros:spin();
return();
}
④client.cpp
#include <ros/ros.h>
#include <service_demo/Greeting.h>
int main(int argc,char **argv)
{
ros::init(argc,argv,"greetings_server");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<service_demo::Greeting>("greetings");
// 定义service客户端,service名字为“greetings”,service类型为Service_demo
service_demo::Greeting srv;
srv.request.name = "HAN";
srv.request.age = "20";
if (client.call(srv))
{
ROS_INFO("Feedback from server: %s.",srv.response.feedback);
}
else
{
ROS_EEROR("Failed to call service greetings.");
return 1;
}
return 0;
}
4.4 param_demo
param_demo.cpp
#include <ros/ros.h>
int main(int argc,char **argv)
{
ros::init(argc,argv,"greetings_server");
ros::NodeHandle nh;
int parameter1,parameter2, parameter3,parameter4,parameter5;
//获取参数的不同方式
ros::param::get("param1",parameter1);
nh,getParam("param2",parameter2);
nh.param("param3",parameter3,100); //将parameter3的值赋值给param3,如果parameter3中没有值,将默认值100赋值给param3
//设置参数
ros::param::set("param4",parameter4);
nh.setParam("param5",parameter5);
//检查参数是否存在
ros::param::has("param5");
nh.hasParam("param6");
//删除参数
ros::param::del("parma5");
nh.deleteParam("param6");
return 0;
}
param_demo_cpp.launch
<launch> //开始
<!--Param标签设置单个参数-->
<param name = "param1" value="1" />
<param name = "robot_description" command="$(find xacro)/xacro.py $(find demo)/urdf/robot.urdf" />
<!--rosparam标签设置多个参数-->
<rosparam>
param3:3
param4:4
param10:helloworld!
</rosparam>
<rosparam file="$(find param_demo)/config/myparam.yaml" command="load" />
<node pkg="param_demo" type="param_demo" name="param_demo" output="screen" />
</launch> //结束
5 rospy
5.1 rospy介绍
5.1.1 rospy-Node相关
函数:
类型 | 函数名 | 注释 |
init_node(name) | 注册和初始化node | |
MasterProxy | get_master() | 获取master的句柄 |
bool | is_shutdown() | 返回是否关闭 |
on_shutdown(fn) | 在node关闭时调用函数 | |
str | get_node_uri() | 返回节点的URI |
str | get_name() | 返回本节点的全名 |
str | get_namespace() | 返回本节点的名字空间 |
5.1.2 rospy-Topic相关
函数:
[[str,str]] | get_published_topics() | 返回正在被发布的所有topic_name(str)和topic_type(str) |
Message | wait_for_message(topic,topic_type,time_out=None) | 等待指定topic的一个message |
spin() | 出发topic或service的处理,会阻塞直到关闭 |
Publisher类:
__init__(self,name,data_class,queue_size=None) | 构造函数 |
publish(self,msg) | 成员函数 发布消息 |
unregister(self) | 成员函数 停止发布 |
Subscriber类:
__init__(self,name,data_class,call_back=None,queue_size=None) | 构造函数 |
unregister(self) | 成员函数 停止订阅 |
5.1.3 rospy_Service相关
函数:
wait_for_service(service,timeout = None) | 阻塞直到服务可用 |
Service类(server):
__init__(self,name,service_class,hanlder) | 构造函数 提供服务 |
shutdown(self) | 成员函数 关闭服务 |
ServiceProxy类(client):
__init__(self,name,service_class) | 构造函数 服务的请求方 | |
Response | call(self,*args,**kwds) | 调用服务 |
Response | __call__(self,*args,**kwds) | 调用服务 |
5.1.4 rospy_Param相关
函数:
XmlRpclegalValue | get_param(param_name,default = _unspecified) | 获取参数的值 |
[str] | get_param_names() | 获取参数的名称 |
set_param(param_name,param_value) | 设置参数的值(key,value) | |
delete_param(param_name) | 删除参数 | |
bool | has_param(param_name) | 参数是否存在于参数服务器上 |
str | search_param() | 搜索参数 |
5.1.5 rospy_Time相关
Time类:
__init__(self,secs=0,nsecs=0) | 构造函数 | |
Time | now() | 静态方法 返回当前时刻的Time对象 |
函数:
Time | get_rostime() | 当前时刻的Time对象 |
float | get_time | 返回当前时间,返回float(单位 秒) |
sleep(duration) | 执行挂起 |
Rate类:
__init__(self,frequency) | 构造函数 | |
sleep(self) | 挂起 考虑上一次的rate.sleep()时间 | |
Time | remaining(self) | 成员函数 剩余sleep实践 |
Duration类:
__init__(self,secs=0,nsecs=0) | 构造函数 秒和纳秒 |
5.2 topic_demo
功能描述:两个node,一个用于发布模拟的GPS消息(格式为自定义,包括坐标和工作状态),另一个接收并处理该信息(计算到原点的距离)。
步骤:①package ②msg ③talker.py ④listener.py ⑤CMakeList.txt & package.xml
gps.msg
# 坐标
float32 x
float32 y
# 定义工作状态
string state
然后需要对该文件进行编译。编译完成后会在~/catkin_ws/devel/lib/python2.7/dist-pacakges/topic_demo/msg/下创建__init__.py文件和msg文件夹(包含__init__.py文件 和 _gps.py)。
可以 from topic_demo. msg import gps
③pylistener.py
import rospyn
import math
from topic_demo.msg import gps
def callback(gps):
distance = math.sqrt(math.pow(gps.x,2)+math.pow(gps.y,2))
rospy,loginfo('Listener:GPS distance = %f,state :%s',distance,gps.state) //输出
def listener():
rospy.init_node('pylistener')
rospy.Subscriber('gps_info',gps,callback) //订阅消息的名称,订阅消息的类型(上面import的类型),收到消息后执行的函数
ros.spin()
if __name__ == '__main__':
listener()
④pytalker.py
import rospy
from topic_demo.msg import gps
def talker():
pub = rospy.Publisher('gps_info',gps,queue_size=10)
rospy.init_node('pytalker',anonymous = Ture)
rate = rospy.Rate(1)
x = 1.0
y = 2.0
state = 'working'
while not rospy is shutdown():
rospy.loginfo('Talker: GPS: X = %f,y = %f')
pub.publish(gps(state,x,y))
x = 1.03 * x
y = 1.03 * y
rate.sleep()
if __name__ == '__main__':
talker()
5.3 service_demo
功能描述:两个node,一个发布请求(格式自定义),另一个接收处理该信息,并返回信息。
步骤:①package ②srv ③server.cpp ④client.cpp ⑤CMakeList.txt & package.xml
Greeting.srv
#服务请求的数据
string name
int32 age
---
#服务回传的内容
string feedback
然后需要对该文件进行编译。
编译完成后会在~/catkin_ws/devel/lib/python2.7/dis-pacakages/service_demo/srv/下创建__init__.py Greeting.py GreetingRequest.py GreetingResponse.py 文件
可以 from service_demo.srv import *(表明import该文件夹下所有文件)
③server_demo.py
import rospy
from service_demo.srv import *
def server_srv():
rospy.init_node('greeting_server')
s = rospy.Service('greetings',Greeting,handle_function) //name,type,函数
rospy.loginfo('Ready to handle the request:')
rospy.spin() //需要这一句来触发所有的service
def handle_function(req):
rospy.loginfo('Request from',req.name,'with age',req.age)
return GreetingResponse('Hi %s.I am server!',%req.name)
if __name__ == '__main__'
server_srv()
④client_demo.py
import rospy
form service_demo.srv import *
def client_srv():
rospy.init_node('greeting_client')
rospy.wait_for_service('greetings') //通过rospy.wait_for_service()函数来阻塞直到服务有效。
try:
greeting_client = rospy.ServiceProxy('greetings',Greeting) //通过 rospy.ServiceProxy函数指定服务名来调用服务。
rosp = greetings_client('HAN',20)
rospy_loginfo('Message From Server: %s',%rosp.feedback)
except rospy.ServiceException, e:
rospy.logwarn('Service call failed: %s', %e)
if __name__ = '__main__':
client_srv()
6 TF & URDF
6.1 TransForm(TF)
TF 是坐标变换(位置+姿态),坐标系数据维护的工具。坐标转换的标准(tree)、话题(topic)、工具、接口。
ROS中机器人模型包含大量的部件,这些部件统称之为link,每一个link上面对应着一个frame, 即一个坐标系.link和frame概念是绑定在一起的。每一个圆圈代表一个frame,对应着机器人上的一个link,任意的两个frame之间都必须是联通的,如果出现某一环节的断裂,就会引发error系统报错.所以完整的tf tree不能有任何断层的地方,这样我们才能查清楚任意两个frame之间的关系。每两个frame之间都有一个broadcaster,这就是为了使得两个frame之间能够正确连通,中间都会有一个Node来发布消息。broadcaster就是一个publisher,如果两个frame之间发生了相对运动,broadcaster就会发布相关消息。
6.2 TransformStamped.msg (TF消息)
TransformStamped.msg 是两个 frame 之间的通信格式。
std_msgs/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
float64 z
float64 w
tf/tfMessage.msg 或者 tf2_msgs/TFMessage 表示 TF tree 的通信格式。
geometry_msgs/TransformStamped[] tranforms //将 TransformStamped 变成一个数组之后发给TF
std_msgs/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
float64 z
float64 w
一个 TransformStamped 描述的是两个坐标系之间的关系,一个 TransformStamped数组 描述的就是一个 TF tree 之间的关系。
6.3 TF in C++
6.3.1 TF相关数据类型
向量 | tf::Vector |
点 | tf::Point |
四元数 | tf::Quaternion |
3*3矩阵 | tf::Matrix3*3 |
位姿 | tf::Pose |
变换 | tf::Transform |
带时间戳的以上类型 | tf::Stamped<T> |
带时间戳的变换 | tf::StampedTransform |
6.3.2 tf::TransformBroadcaster类
void sendTransform (const StampedTransform &transform)
void sendTransform (const std::vector<StampedTransform> &transforms)
void sendTransform (const geometry_msgs::TransformStamped &transform)
void sendTransform (const std::vector<geometry_msgs::TransformStamped> &transform)
6.3.3 tf::TransformListener类
void lookupTransform(const std::string &target_frame, //目标坐标系
const std::string &source_frame, //原坐标系
const ros::Time &time, //查询具体哪一时刻的转换关系
StampedTransform &transform)const //将查询的转换关系存放在第四个参数中
bool canTransform() //Transform是否联通
bool waitForTransform()const //等待Transform联通
6.4 tf in Python
https://docs.ros.org/api/tf/html/python/tf_python.html#
python 中存在 tf库,即使用时可以 import tf。
6.4.1 tf.transformations
tf.transformations中包含基本数学运算函数。
euler_matrix(ai,aj,ak,axes='sxyz') # 欧拉角到矩阵
euler_form_matrix(matrix,axes='sxyz') # 矩阵到欧拉角
euler_form_quanternion(quaternion,axes='sxyz') # 四元数到欧拉角
quaternion_form_euler(ai,aj,ak,axes='sxyz') # 欧拉角到四元数
quaternion_matrix(quaternion) # 四元数到矩阵
quaternion_form_matrix(matrix) # 矩阵到四元数
6.4.2 tf.TransformLister类
# frame是否相通
canTransform(self,traget_frame,source_frame,time)
# 阻塞直到frame相通
waitForTransform(self,target_frame,source_frame,time,timeout)
# 查看相对的tf,返回(trans,quat)
lookupTransform(self,target_frame,source_frame,time)
6.4.3 tf.TransformBroadcaster类
# 向 /tf (topic)发布消息
sendTransform(translation,rotation,time,child,parent)
sendTransformMessage(transform)
6.4.4 tf 指令
# 根据当前的tf树创建一个PDF图
$ rosrun tf view_frames
# 查看当前的tf树
$ rosrun rqt_tf_tree rqt_tf_tree
# 查看两个frame之间的变换关系
$ rosrun tf tf_echo [reference_frame][target_frame]
6.5 urdf
urdf 是指 Unified Robot Description Format 统一机器人描述格式。
7 SLAM(Simutaneous Localization And Mapping 同步定位和制图)
7.1 SLAM 与 Map
std_msgs/Header header
uint32 seq # 序号
time stamp # 时间戳
string frame_id # map
nav_msgs/MapMetaData info # 存储地图相关信息
time map_load_time # 地图加载时间
float32 resolution # 地图分辨率(m/pixd)(每个像素点实际是多少米,通常选择0.05或者0.02)
uint32 width # 单位 pixd
uint32 height
geometry_msgs/Pose origin(初始化时地图相对于frame:map的位置与朝向)
geometry_msgs/Point position
float64 x
float64 y
float64 z
geometry_msgs/Quaternion orientation
float64 x
float64 y
float64 z
float64 w
int8[] data # cost,即机器人走到这一个需要消耗的成本。
# 数组长度:width * height
7.2 Gmapping SLAM包
7.2.1 Gmapping Topic
Odom:encoder(轮子上的编码器,来测轮子的转速),visual(摄像头),imu
输入的Topic: 1. /tf 和 /tf_static。必须包含odom到base之间的转换关系,即里程计到机器人地盘之间的关系。
2. /scan。激光雷达
输出的Topic: 1. /tf。map(frame 坐标系)到Odom之间的转换关系。即将map与底盘base连通
2. /map_metadata。将地图相关信息单独作为一个topic发布出去
3. /map。是一个Topic,也是一个图片。将每一次建图的结果发布到这个topic上,以此达到更新地图的目的。
4. /slam_gmapping/entropy。浮点型,建图精度。
7.2.2 Gmapping Service
Service: /dynamic_map
Type: nav_msgs/GetMap
nav_msgs/GetMap.srv
#Get the map as a nav_msgs/OccupancyGrid
---
nav_msgs/OccupancyGrid map