ROS1(五):ROS1常用组件


目录:

ROS1常用组件

坐标管理系统

ROS中的机器人坐标变换可以参考:《机器人导论》

在代码中可以通过 TF 功能包来进行坐标变换

TF功能包可以保存 10s 内机器人系统内各个坐标系的位置,内部通过 TF Tree 树形结构来进行保存。其是基于两种方式来实现的:

  • 广播TF变换
  • 监听TF变换

可以通过如下的一个案例来演示 TF 坐标系统可以有哪些用处:

可以看到在操作第一只海龟运动的时候,第二只海龟会自动跟随第一只海龟的运行

sudo apt install ros-melodic-turtle-tf
roslaunch turtle_tf turtle_tf_demo.launch
rosrun turtlesim turtle_teleop_key

# 文件格式查看TF角标变换
rosrun tf view_frames

# 命令行下查看TF角标变换关系
rosrun tf tf_echo turtle1 turtle2

# 可视化查看两个坐标系的变换关系
rosrun rviz rviz `rospack find turtle_tf` /rviz/turtle_rviz.rviz

文件格式查看TF角标变换的结果:

tf坐标洗广播与监听的编程实现

创建对应的功能包:

cd ~/catkin_ws/src
catkin_create_pkg learning_tf roscpp rospy tf turtlesim

接下来实现一个tf广播器(turtle_tf_broadcaster.cpp):

  • 定义TF广播器
  • 创建坐标变换值
  • 发布坐标变换

代码如下:

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>

std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg){
    // 创建tf的广播器
    static tf::TransformBroadcaster br;

    // 初始化tf数据
    tf::Transform transform;
    transform.setOrigin(tf::Vector3(msg->x,msg->y,0.0));
    tf::Quaternion q;
    q.setRPY(0,0,msg->theta);
    transform.setRotation(q);

    // 广播world与海龟坐标系之间的tf数据
    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(),"world",turtle_name));
}

int main(int argc, char** argv){
    // 初始化ros节点
    ros::init(argc,argv,"my_tf_broadcaster");

    // 输入参数作为海龟的名字
    if(argc != 2){
        ROS_ERROR("need turtle name as argument");
        return -1;
    }
    turtle_name = argv[1];

    // 订阅海龟的位置姿态话题
    ros::NodeHandle node;
    ros::Subscribe sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

    // 循环等待回调函数
    ros::spin();

    return 0;
}

创建完广播器之后实现对应的监听器(turtle_tf_listener.cpp):

  • 定义TF监听器
  • 查找坐标变换
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv){
    // 初始化ros节点
    ros::init(argc,argv,"my_tf_lister");

    // 创建节点句柄
    ros::NodeHandle node;

    // 请求产生turtle2
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);

    // 创建发布 turtle2 速度控制指令的发布者
    ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_val",10);

    // 创建tf的监听器
    tf::TransformListener listener;

    ros::Rate rate(10.0);
    while(node.ok()){
        // 获取turtle1与turtle2坐标系之间的tf数据
        tf::StampedTransform trandform;
        try{
            listener.waitForTransform("/turtle2", "/turtle", ros::Time(0), ros::Duration(3.0));
            listener.lookupTransform("/turtle2", "turtle1", ros::Time(0), transform);
        }
        catch(tf::TransformException &ex){
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }

        // 根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度控制指令
        geometry_msgs::Twist vel_msg;
        vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),transform.getOrigin().x())
        vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(),2) + pow(transform.getOrigin().y(),2));
        turtle_vel.publish(vel_msg);

        rate.sleep();
    }
    return 0;
}

修改编译文件(CMakeList.txt):

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})

add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

编译运行:

catkin_make

source devel/setup.bash
roscore

# 启动海龟1
rosrun turtlesim turtlesim_node

# 重定向启动两个 broadcaster,node的名字会被 __name 后指定的所替代
rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2

# 启动listener,创建海龟2
rosrun learning_tf turtle_tf_listenr

# 控制海龟1
rosrun turtlesim turtle_teleop_key

launch启动文件

之前想要验证所实现的功能的时候往往需要启动很多的终端来启动对应的node才可以,其实可以通过 launch 文件来将这些node进行配置化启动,节省时间,提高效率。

launch 文件:通过xml文件实现多节点的配置和启动(可自动启动ros master)

launch 文件中的根元素采用 <launch> 标签定义,对应的节点使用 <node> 标签:

<launch>
    <node> pkg="turtlesim" name="sim1" type="turtlesim_node"</node>
</launch>

node 对应的配置:

  • pkg:节点所在的功能包名称
  • type:节点的可执行文件名称
  • name:节点运行时的名称
  • output:(可选)是否要输出日志信息到终端中,=screen则输出到终端中
  • respawn:(可选)是否自动重启
  • required:(可选)
  • ns:(可选)命名空间
  • args:(可选)

除了 node 之外还有其他的标签:

  • params/rosparam:设置ros系统运行中的参数,存储在参数服务器中
<param name="output_frame" value="odom"/>

<rosparam file="params.yaml" command="load" ns="params">
  • arg:launch文件内部的局部变量,仅限于launch文件使用
<arg name="arg-name" default="arg-value"/>

调用:
<param name="foo" value="$(arg arg-name)"/>
<node name="node" pkg="package" type="type" args="$(arg arg-name)"/>
  • remap:重映射ros计算图资源的命名
<remap from="turtlebot/cmd_vel" to="/cmd_vel"/>

from:原命名
to:映射之后的命名
  • include:嵌套,包含其他launch文件
<include file="$(dirname)/other.launch">/

launch 文件一般都放置在 launch 文件夹中。启动launch文件的语法为:roslaunch 包名 launchFile

使用 launch 文件来实现上面的效果:

<launch>
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>

    <node pkg="learning_tf" type="learning_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster"/>
    <node pkg="learning_tf" type="learning_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster"/>

    <node pkg="learning_tf" type="learning_tf_listener" name="listener"/>
<launch/>

常用的可视化工具

下面这些工具是ros自带的qt界面工具:

  • rqt_console:日志输出工具
  • rqt_graph:计算图可视化工具
  • rqt_plot:数据绘图工具
  • rqt_image_view:图像渲染工具
  • rqt:所有ros可视化工具集合

下面是ros自带的工具包:

  • rviz:机器人数据三维可视化显示平台,启动方式:rosrun rviz rviz
  • Gazebo:三维物理仿真平台,启动方式:roslaunch gazebo_ros 启动文件.launch