ROS常用组件工具
一、Launch启动文件
创建一个learning_launch功能包,在其中新建launch文件,分别完成第3讲三道题的启动和测试,将每道题目中使用的所有rosrun命令替换为一个roslaunch命令。
解:
创建了turtlesim_communication功能包和1.launch文件,其中1.launch代码如下:
<launch>
<node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node" />
<node pkg="turtlesim_communication" type="turtle_subscriber" name="subscriber" output="screen"/>
<node pkg="turtlesim_communication" type="turtle_publisher" name="publisher" args="turtle1"/>
</launch>
最后的运行命令如下:
roscd turtlesim_communication
roslaunch launch/1.launch
二、仿真工具
gazebo是一个三维动态物理仿真平台,具备强大的物理引擎、高质量的图形渲染、方便的编程与图形接口,最重要的是其开源免费的特性。gazebo中的机器人模型与rviz使用的模型相同,但是需要在模型中加入机器人和周围环境的物理属性,例如质量、摩擦系数、弹性系数等。机器人的传感器信息也可以通过插件的形式加入仿真环境,以可视化的方式进行显示。
下载gazebo离线模型库,并放置在指定位置,成功运行gazebo后,在界面中添加模型进行测试。
建议:为保证模型顺利加载,请提前将模型文件库下载并放置在 ~/.gazebo/models
下
三、ROS机器人的tf变换
1. ROS的TF功能包
TF功能包,可以通过广播TF变换和监听TF变换获取如下坐标变换关系:
- 机器人局部坐标系相对于全局坐标系的关系。
- 机器人夹取的物体相对于机器人中心坐标系的位置.
- 机器人中心坐标系相对于全局坐标系的位置。
2. TF坐标变换
已知激光雷达和机器人底盘的坐标关系,广播并监听机器人的坐标变换,求解激光雷达数据在底盘坐标系下的坐标值。
2.1 TF广播器
首先定义TF 广播器(TransformBroadcaster),
// 创建tf的广播器
static tf::TransformBroadcaster br;
接着创建坐标变换值(transform),比如在这里的变换关系中,没有旋转变换,只有平移变换,所以四元数Quaternion可以为(0,0,0,1),而位移向量则Vector3是(0.1, 0.0, 0.2),位移的单位是米。
// 初始化tf数据
tf::Transform transform;
transform.setOrigin( tf::Vector3(0.1, 0.0, 0.2) );
transform.setRotation( tf::Quaternion(0,0,0,1) );
最后发布坐标变换(sendTransform)
// 广播base_link与base_laser坐标系之间的tf数据
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "base_link", "base_laser"));
完整代码如下,
/**
* 该例程产生tf数据,并计算、发布base_laser的位置指令
*/
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
int main(int argc, char** argv){
// 初始化ROS节点
ros::init(argc, argv, "robot_tf_broadcaster");
// 订阅base_link的位置话题
ros::NodeHandle node;
// 创建tf的广播器
static tf::TransformBroadcaster br;
while(node.ok()){
// 初始化tf数据
tf::Transform transform;
transform.setOrigin( tf::Vector3(0.1, 0.0, 0.2) );
transform.setRotation( tf::Quaternion(0,0,0,1) );
// 广播base_link与base_laser坐标系之间的tf数据
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "base_link", "base_laser"));
}
return 0;
};
2.2 TF监听器
首先定义一个TF监听器(TransformListener)
// 创建tf的监听器
tf::TransformListener listener;
获取TF变换后进行坐标系变化,此时我们可以设置检测到的base_laser(laser_point)为(0.3, 0.0, 0.0),然后就该检测信息,通过transformPoint转换到base_link(base_point)上的点。
geometry_msgs::PointStamped base_point;
listener.transformPoint("base_link", laser_point, base_point);
完成代码如下:
/**
* 该例程监听tf数据,并计算、发布base_laser的位置指令
*/
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/PointStamped.h>
int main(int argc, char** argv){
// 初始化ROS节点
ros::init(argc, argv, "robot_tf_listener");
// 创建节点句柄
ros::NodeHandle node;
// 创建tf的监听器
tf::TransformListener listener;
ros::Rate rate(10.0);
while (node.ok()){
//我们将在base_laser帧中创建一个要转换为base_link帧的点
geometry_msgs::PointStamped laser_point;
laser_point.header.frame_id = "base_laser";
//我们将在我们的简单示例中使用最近可用的转换
laser_point.header.stamp = ros::Time();
//laser_point检测点获取
laser_point.point.x = 0.3;
laser_point.point.y = 0.0;
laser_point.point.z = 0.0;
try{
// 等待获取监听信息base_link和base_laser
listener.waitForTransform("base_link", "base_laser", ros::Time(0), ros::Duration(3.0));
geometry_msgs::PointStamped base_point;
listener.transformPoint("base_link", laser_point, base_point);
ROS_INFO("base_laser: (%.2f, %.2f. %.2f) -----> base_link: (%.2f, %.2f, %.2f) at time %.2f",
laser_point.point.x, laser_point.point.y, laser_point.point.z,
base_point.point.x, base_point.point.y, base_point.point.z, base_point.header.stamp.toSec());
}
catch(tf::TransformException& ex){
ROS_ERROR("Received an exception trying to transform a point from \"base_laser\" to \"base_link\": %s", ex.what());
}
rate.sleep();
}
return 0;
};
3. 运行结果
启动两个节点时,就可以在监听处获得base_laser和base_link的坐标关系了。
说明的是检测物体为世界坐标系,base_laser和base_link是局部坐标系。
[ INFO] [1565335443.030506273]: base_laser: (0.30, 0.00. 0.00) -----> base_link: (0.40, 0.00, 0.20) at time 1565335443.00