【移动机器人运动规划(ROS)】04_ROS-launch文件-TF变换-参数服务>
ros-launch
launch文件是ROS提供的一种启动多节点的文件,格式是XML格式,它能够使得启动多个ROS节点的过程变得简单,同时也简化了ROS参数服务器进行参数设置的过程,文件命末尾以.launch结尾。在前边提到过,一般的,我们会在功能包的路径下建立一个文件夹名为launch,用于存放属于该功能包delaunch文件。
-
格式
# ROS提供了roslaunch工具,与rosrun不一样的是,roslaunch可以同时启动多个节点,只需要把多个节点写进运行的launch文件中即可 roslaunch <pkg_name> <launch_file_name> # pkg_name:是表示功能包的名字,根据实际情况,换成launch文件所在功能包的名字; #launch_file_name:表示需要启动的launch文件的文件名。
单个节点launch文件
-
格式
<!--launch文件的格式是XML格式的,所有需要有个根元素,launch文件的根元素是一对,出现在launch文件的开头,出现在launch最后边--> <launch> ... ... </launch> <!-- launch文件里边是启动节点程序的,所以核心是一个或者多个node,每个node表示启动的节点程序,一个node启动必须包括以下几点: pkg:节点所在的功能包的名字 type:运行节点的可执行程序的名字 name:节点的名字,不一定要与程序中定义节点名字一样,可以任意命名 在launch文件中一个必要的节点node元素格式如下, --> <node pkg="pkg_name" type="excutable_name" name="node_name"/> <!--注意,最后的/是必不可少的,也可以写成--> <node pkg="pkg_name" type="excutable_name" name="node_name"> </node>
-
创建文件
# 我们在ros_ws/src目录下,使用以下命令建立一个功能名为learn_launch cd ~/ros_ws/src/ catkin_create_pkg learn_launch std_msgs rospy roscpp # 然后在learn_launch目录下,新建一个文件夹命名为launch,终端输入, cd ~/ros_ws/src/learn_launch mkdir launch # 回到工作空间目录下进行编译, cd ~/ros_ws catkin_make
-
编写launch文件
</!--然后在launch文件夹下,新建一个文件,文件名是turtle_node.launch,把以下内容负责到里边--> <launch> <node pkg="turtlesim" type="turtlesim_node" name="turtle"/> </launch>
-
运行launch文件
# 不用重新编译,launch运行后,会自动开启roscore,然后运行小海龟的程序,生成一只小海龟。 roslaunch learn_launch turtle_node.launch
多个节点的launch文件
我们写个launch文件,这个launch文件启动的节点有两个,一个是生成小海龟的节点,一个是运行键盘控制小海龟的节点。
-
创建
<!-- 在launch文件夹中,新建一个文件,命名为turtle_ctrl.launch,把以下内容复制到里边,--> <launch> <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop"/> <node pkg="turtlesim" type="turtlesim_node" name="turtlesim"/> </launch>
-
运行
# 运行后,点击终端界面,按下键盘上下左右键,即可控制海龟运动 roslaunch learn_launch turtle_ctrl.launch # 我们可以通过rosnode list命令来查看当前运行的节点有哪些 rosnode list # /turtle和/turtle_ctrl就是对应的小海龟节点以及控制节点,对应的名字就是launch文件代码中node的name的值
launch文件中加载运行Launch
launch文件中,不仅可以运行节点,还可以加载运行launch文件,也就是嵌套使用
-
格式
<!-- package_name:表示launch文件所在的功能包 launch_file_name:表示需要运行的launch文件的文件名字 --> <include file=”$(find package_name)/launch/launch_file_name”/> <!-- 我们新建个文件,命名为listen_turtle_pose.launch 这个程序实现这样的功能:程序运行后,会生成一只小海龟,终端会打印出小海龟的xy坐标; 点击终端我们通过键盘控制控制小海龟,小海龟移动, 终端会实时打印出小海龟的xy坐标。把以下内容复制粘贴到listen_turtle_pose.launch文件中, 这里在node这里启动的时候加入了一个参数output,因为我们是想要在终端打印这个信息的, 所以是输出到屏幕screen还有两个值分别是log输出到日志,none无输出 --> <launch> <include file="$(find learn_launch)/launch/turtle_ctrl.launch"/> <node pkg="learn_topic" type="turtle_pose_subscriber" name="turtle_pose" output="screen"/> </launch>
launch文件加载参数
在launch文件中,有两个变量是表示参数的,分别是arg和param,
arg用在launch文件内,可以灵活配置,arg的值可以作为param的值;
param用在节点的参数服务器,也就是用在节点程序当中;
-
格式
<!-- arg: 用于定义参数化的参数,可以在启动的时候传入不同的参数值,包括以下两个部分: -name:参数的名称 -default:参数的默认值 --> <arg name="arg_name" default="default_value"/> <!-- param: 用于在参数服务器(Parameter Server)中设置参数,包括以下两个部分: -name:参数的名称 -value:参数的值 --> <param name="parameter_name" value="parameter_value"/>
-
示例
<!--> 在launch文件夹新建一个launch文件,命名为set_backgroud_color.launch,这个程序启动后,会根据我们设定的参数来修改生成小海龟的背景板的颜色,把以下内容复制到set_backgroud_color.launch文件中, <--> <launch> <arg name="r_value" default="100"/> <arg name="g_value" default="0"/> <arg name="b_value" default="100"/> <node pkg="turtlesim" type="turtlesim_node" name="turtle"> <param name="/background_r" value="$(arg r_value)"/> <param name="/background_g" value="$(arg g_value)"/> <param name="/background_b" value="$(arg b_value)"/> </node> </launch>
-
运行
roslaunch learn_launch set_backgroud_color.launch # 背景板的颜色变成我们设定的rgb值为(100,0,100)的颜色了。我们还可以在启动的时候,在命令行修改rgb的值,只要带上参数即可,例如,终端输入, roslaunch learn_launch set_backgroud_color.launch b_value:=2 # 可以看出,我们在启动命令的时候,把b_value的值修改成2,然后传入进去。这里的参数是怎么传递的呢?在node标签里边,我们设置了三个参数,分别是/background_r、/background_g和/background_b,它们value的值,分别是$(arg r_value)、$(arg g_value)和$(arg b_value);再看看r_value、g_value和b_value的默认值,分别是上边arg标签里边的100、0和100,这就很明了了:我们在arg里定义的值,然后在arg里定义的值传给param里的value。在引用的时候加上$(arg g_value)即可,当然,你也可以不用,也就是写成把那么这样,这个/background_g的参数值就是固定的255了。
launch文件重映射话题名字
在实际的开发过程中,原本预设定话题的名字往往需要根据其他节点的话题名字进行修改,源码修改比较麻烦,C++版本的代码修改完还需要编译,比较麻烦,但在launch文件中,用标签即可实现重映射话题名字,把预设定的话题的名字映射成其它的
-
格式
<!-- original_topic:原来节点程序里边设定的话题名字 remapped_topic:映射后的话题名字 --> <remap from="original_topic" to="remapped_topic" />
-
示例
# 举例说明如何使用标签,在launch文件夹下新建一个文件,命名为remap_topic.launch, # 程序运行后,会把小海龟的速度控制话题名称修改成设定的话题名称,我们先看下原本的是什么,先启动小海龟看看 roslaunch learn_launch turtle_node.launch # 查看话题 rostopic list # 此时的速度控制话题是/turtle1/cmd_vel,把以下代码粘贴到remap_topic.launch, <launch> <node pkg="turtlesim" type="turtlesim_node" name="turtle"> <remap from="/turtle1/cmd_vel" to="/ctrl_vel"/> </node> </launch> # 运行 roslaunch learn_launch remap_topic.launch # 查看话题 rostopic list # 这时候,原来的/turtle1/cmd_vel没有了,映射成/ctrl_vel了,这是因为我们在launch文件,重新映射了话题,
launch文件中使用条件语句
我们在launch文件中,可以加上条件判断来选择启动的节点程序,条件判断在launch文件中由
if和unless来控制
-
示例
<!-- 在launch文件夹新建一个文件命名为condition_ctrl_turtle.launch,程序运行后,根据实际的参数,进行判断是否启动键盘控制节点来控制小海龟,把以下内容复制到condition_ctrl_turtle.launch中, if里边判断是true,unless判断是false --> <launch> <arg name="ctrl_node" default="true"/> <node pkg="turtlesim" type="turtlesim_node" name="turtle"/> <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_ctrl" if="$(eval arg('ctrl_node') == true)"/> </launch> -
运行
roslaunch learn_launch condition_ctrl_turtle.launch # 查看节点 rosnode list # 可以看到,我们是启动了小海龟的控制节点的,我们点击launch启动的终端,即可用上下左右来控制小海龟移动了。我们把参数ctrl_node设置成false试下,关闭启动的launch文件,终端输入, roslaunch learn_launch condition_ctrl_turtle.launch ctrl_node:=false # 查看节点 rosnode list # 查询得知是没有运行控制节点的,因为if判断是参数ctrl_node为true的时候,才启动键盘控制程序,
launch文件中使用命名空间
launch文件中有个组的概念,标签是,它可以将指定的节点nodes组织起来,将几个nodes放进同一个namespace中。namespace是命名空间,作用是避免名称冲突,ros系统中不允许出现相同的节点名字。比如说,我们想要在在一个终端打开两个小海龟的节点,如果没有命名空间,直接启动的话,是会产生冲突导致程序退出的,因此,我们需要在launch文件中,加上命令空间namespace,相当于在相同节点前再添加一个标识来区分两个节点。
-
格式
<!-- 在group中,添加namespece参数,即表示,在本组group里边所有的节点前都添加了命名空间,使用的格式入下, --> <group ns="namespace"> <node pkg=".." .../> </group>
-
示例
<!-- namespace改成自定义的内容,举个例子来说明下,在launch文件下新建一个launch文件,命名为group_turtle.launch,把以下内容,复制到group_turtle.launch中, --> <launch> <group ns="robot1"> <node pkg="turtlesim" type="turtlesim_node" name="turtle"/> </group> <group ns="robot2"> <node pkg="turtlesim" type="turtlesim_node" name="turtle"/> </group> </launch>
-
运行
# 保存后退出,程序运行后,会打开两个窗口,分别生成两只海龟, roslaunch learn_launch group_turtle.launch # 查看节点 rosnode list # 查询得知有两个小海龟的节点分别是/robot1/turtle和/robot2/turtle,这个两个节点名字前分别加上了/robot1和/robot2来区别两个节点,查看话题 rostopic list # 查询得知,前边同样是加上了/robot1和/robot2作为区分。我们在group中加上了ns,即可为所有节点名前添加命名空间,
ros-TF
TF变换,就是坐标转换,包括了位置和姿态两个方面的变换。它使用树形数据结构,根据时间缓冲并维护多个坐标系之间的坐标变换关系,可以帮助开发者在任意时间、坐标系间完成点、向量等坐标变换。本节课程来说明在ros系统中,常用的TF工具有哪些,以及如何广播/监听变换。
TF常用工具
在介绍TF工具的时候,先搞清楚两个重要的概念source_frame和target_frame,也称parent_frame和child_frame。它们各自代表的是一个坐标系,我们广播或者监听TF变换的时候,其实就是广播和监听这两者的相对位姿的变换。
-
常用工具
# tf_monitor: 用于打印TF树中所有坐标系的发布状态, rosrun tf tf_monitor # 可以打印指定的坐标的发布状态,需要在后边加上指定坐标系的名称,终端输入, rosrun tf tf_monitor source_frame target_frame # tf_echo: 用于查看指定坐标系之间的变换关系,终端输入, rosrun tf tf_echo source_frame target_frame # static_transform_publisher: 用于发布两个坐标系之间的静态坐标变换,这两个坐标系不发生相对位置变化。 # 该工具需要设置坐标的偏移参数和旋转参数,发布频率以ms为单位。 # 欧拉角yaw(围绕z轴旋转的偏航角)pitch(围绕y轴旋转的俯仰角)roll(围绕x轴旋转的翻滚角) rosrun tf static_transform_publisher x y x yaw pitch roll frame_id child_frame_id period_in_ms # 四元数格式显示 static_transform_publisher x y z qx qy qz qw frame_id child_frame_id period_in_ms # rqt_tf_tree: 观察在ros上被发布的坐标系树,可用刷新按钮来更新树的内容 rosrun rqt_tf_tree rqt_tf_tree
TF变换广播与监听编程实现
创建tf广播器
-
创建tf广播器
# 首先创建一个功能包,命令为learn_tf cd ~/ros_ws/src catkin_create_pkg learn_tf rospy roscpp turtlesim tf # 回到工作空间,编译 cd ~/ros_ws catkin_make # 在功能包learn_tf的src文件夹,创建一个c++文件(文件后缀为.cpp),命名为turtle_tf_broadcaster.cpp #include <ros/ros.h> #include <tf/transform_broadcaster.h> #include <turtlesim/Pose.h> std::string turtle_name; void poseCallback(const turtlesim::PoseConstPtr& msg) { static tf::TransformBroadcaster br;// 创建tf的广播器 // 初始化tf数据 tf::Transform transform; transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );//设置xyz坐标 tf::Quaternion q; q.setRPY(0, 0, msg->theta);//设置欧拉角:以x轴,y轴,z轴旋转 transform.setRotation(q); br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world",turtle_name));// 广播world与turtle坐标系之间的tf数据 } int main(int argc, char** argv) { ros::init(argc, argv, "turtle_world_tf_broadcaster");// 初始化ROS节点 if (argc != 2) { ROS_ERROR("Missing a parameter as the name of the turtle!"); return -1; } turtle_name = argv[1];// 输入参数作为海龟的名字 // 订阅海龟的位姿话题/pose ros::NodeHandle node; ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10,&poseCallback); // 循环等待回调函数 ros::spin(); return 0; }; -
cmake
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp) target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES}) -
编译
cd ~/ros_ws catkin_make
TF监听器
-
TF监听器
# 在功能包learn_tf的src文件夹,创建一个c++文件(文件后缀为.cpp),命名为turtle_tf_listener.cpp /** * 该例程监听tf数据,并计算、发布turtle2的速度指令 turtle2->turtle1 = world->turtle*world->turtle2 */ #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::init(argc, argv, "turtle1_turtle2_listener");// 初始化ROS节点 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 vel = node.advertise<geometry_msgs::Twist> ("/turtle2/cmd_vel", 10); tf::TransformListener listener;// 创建tf的监听器 ros::Rate rate(10.0); while (node.ok()) { // 获取turtle1与turtle2坐标系之间的tf数据 tf::StampedTransform transform; try { listener.waitForTransform("/turtle2", "/turtle1", 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 turtle2_vel_msg; turtle2_vel_msg.angular.z = 6.0 * atan2(transform.getOrigin().y(),transform.getOrigin().x()); turtle2_vel_msg.linear.x = 0.8 * sqrt(pow(transform.getOrigin().x(), 2)+pow(transform.getOrigin().y(), 2)); vel.publish(turtle2_vel_msg); rate.sleep(); } return 0; }; -
cmake
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp) target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES}) -
编译
cd ~/ros_ws catkin_make
-
运行
# 首先开启roscore roscore # 运行小海龟节点 rosrun turtlesim turtlesim_node # 运行广播tf程序,广播turtle1->world rosrun learn_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1 # 运行广播tf程序,广播turtle2->world rosrun learn_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2 # 运行监听tf程序 rosrun learn_tf turtle_tf_listener # 运行键盘控制小海龟节点 rosrun turtlesim turtle_teleop_key # 查看TF树 rosrun rqt_tf_tree rqt_tf_tree `如上图所示,turtle1与turtle2都与world有TF变换关系,所有,当turtle1相对与world发生相对坐标变换的时候,turtle2就知道turtle1在哪里,然后计算两者的相对位姿,调整turtle2的线速度和角速度,向turtle1所在的位置移动。`
ros-参数服务
参数服务器是节点存储参数的地方,可以通过修改参数来实现调试程序的作用。ros系统中,关于参数服务器的工具是rosparam。本节课程就说明下rosparam命令的使用方法。
##
-
示例
# 罗列出当前运行的ros节点中,参数服务器中的所有参数,例如,我们运行小海龟的节点,然后用该命令查看下有哪些参数 roslaunch learn_launch turtle_node.launch # 成功运行后,另一个终端输入rosparam list,查询参数 rosparam list # 获取某个参数的参数值,终端输入rosparam get 需要查询的某个参数即可查询得到,接着上边的小海龟程序,查询下/turtle/background_b的值是多少 rosparam get /turtle/background_b # 设置某个参数的参数值,终端输入rosparam set 需要设置的参数 需要设置的参数值 即可设置参数值,接着上边小海龟的程序,设置/turtle/background_b为0看下效果 rosparam set /turtle/background_b 0 # 发现输入后小海龟的背景没有改变,那是因为该参数不支持动态调节传入,我们可以再用上边指令rosparam get来获取一次,终端输入, rosparam get /turtle/background_b `如上图所示,查询得知/turtle/background_b的参数值修改成0了` # 用于删除某个参数,终端输入rosparam delete 需要删除的参数,即可删除参数,接着上边的小海龟程序,删除/turtle/background_b参数 rosparam delete /turtle/background_b # 输入rosparam list来查询下删除后,是否还有/turtle/background_b rosparam list `查询得知,已经没有了/turtle/background_b该参数` # 用于导出当前ros参数服务表的所有参数值并且保存到一个文件,终端输入rosparam dump保存参数的文件即可导出到指定文件,接着上边的小海龟程序,导出参数到终端目录下的turtle.yaml rosparam dump turtle.yaml `在终端目录下,可以看到有个turtle.yaml文件,双击打开` # 加载参数表文件到参数服务器,实现多个参数同时修改的功能,终端输入rosparam load 参数表文件名,我们刚才导出了一个turtle.yaml的参数表,复制一份修改里边的内容,命名为turtle_changed.yaml,把里边的background_g和background_r值改成0和255 background_g: 0 background_r: 255 # 保存后退出,然后在参数表文件所在的目录下打开终端输入 rosparam load turtle_changed.yaml # 然后,我们输入参数查询的指令,查询下 background_g和background_r的值, rosparam get /turtle/background_g rosparam get /turtle/background_r `查询得知,确实如我们参数表那样设置的数值一致。` # 在launch文件中,还可以传入这个yaml来设置参数。一般的,我们会在learn_launch文件夹下,新建一个param文件夹来存放参数表文件,所以我们在learn_launch文件夹下建立一个文件夹,命名为param, cd ~/ros_ws/src/learn_launch mkdir param # 然后,在param文件夹下,新建一个文件,命名为background_rgb.yaml,用于设置小海龟的背景板,把以下内容复制到background_rgb.yaml里, background_b: 0 background_g: 100 background_r: 255 # 注意冒号:后边是有空格,这里的background_b、background_g、background_r则是对应了小海龟的参数,保存后退出,在launch文件夹下新建一个launch文件,命名为turtle_param_set.launch,把以下内容复制到里边, <launch> <node pkg="turtlesim" type="turtlesim_node" name="turtle"> <rosparam file="$(find learn_launch)/param/background_rgb.yaml" command="load"/> </node> </launch> # 终端输入以下指令运行launch文件 roslaunch learn_launch turtle_param_set.launch `成功运行后如上图所示,终端还会打印出background_b、background_g、background_r的值,与参数表里的一致,说明加载成功,小海龟的背景也由默认的蓝色换成图中所示的颜色。launch文件中,加载参数表的代码如下,` <rosparam file="$(find pkg_name)/param/file_name.yaml" command="load"/> # 其中,pkg_name是功能包的名字,file_name.yaml表示的是参数表文件的名字,且默认参数表文件是放在功能包文件夹下的param文件夹下。 <rosparam file="$(find learn_launch)/param/background_rgb.yaml" command="load"/>
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)