https://download.csdn.net/download/qq_30022867/11120759?utm_medium=distribute.pc_relevant_download.none-task-download-baidujs-9.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-download-baidujs-9.nonecase
书籍下载
一、图概念概述
Nodes:节点,一个节点即为一个可执行文件,它可以通过ROS与其它节点进行通信。
Messages:消息,消息是一种ROS数据类型,用于订阅或发布到一个话题。
Topics:话题,节点可以发布消息到话题,也可以订阅话题以接收消息。
Master:节点管理器,ROS名称服务 (比如帮助节点找到彼此)。
rosout: ROS中相当于stdout/stderr。
roscore: 主机+ rosout + 参数服务器 (参数服务器会在后面介绍)。
运行ros节点的方法:
一般有两种:
第一种:使用rosrun,在设置完环境变量后,可以使用包名来直接运行一个包内的节点。
用法:
$rosrun [package_name] [node_name]
注意:这种方法需要新打开一个终端 运行
$roscore
第二种:使用roslaunch
用法:
$roslaunch [package] [fliename.launch]
launch是个文件,用launch文件来运行程序相对于rosrun的优点有两个
<1>可以向程序传参
<2>可以同时运行多个节点
launch文件结构
<launch>
<!--第1个节点-->
<node pkg="包名" type="包内可执行程序名字" name="节点名">
<!--注释内容-->
<para name="程序中的数值或布尔参数" value="" />
<para name="程序中的字符串参数" value=" " type="str" />
...
<node>
...
<!--第n个节点-->
<node pkg="包名" type="包内可执行程序名字" name="节点名">
<!--注释内容-->
...
<node>
</launch>
注意:
<1>包内可执行程序名由包内CMakeLists.txt控制生成。
<2>在每个可执行程序的main里都会有定义节点的语句,这里的节点名字只是用于代码编译,和launch文件的节点名不是一回事。
比如:
int main (int argc, char **argv)
{
ros::init(argc,argv,"节点名字");
}
二、ROS常用命令
命令 | 重要度 | 命令释义 | 详细说明 |
roscore | ★★★ | ros+core | master(ROS名称服务) + rosout(日志记录) + parameter |
rosrun | ★★★ | ros+run | 运行节点 |
roslaunch | ★★★ | ros+launch | 运行多个节点或设置运行选项 |
rosclean | ★★☆ | ros+clean | 检查或删除ROS日志文件 |
三、创建一个工作空间
前提:安装了相关的程序,比如:catkin
等
创建一个catkin工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
这里 -p
是允许创建目录下的子目录的意思效果如下(我这里实在桌面创建的)
四、ROS发布者节点以及订阅者节点的编写
4.1、编写发布者节点
“节点”是连接到ROS网络的可执行文件的ROS术语。在这里,我们将创建一个将继续广播消息的发布者(“talker”)节点。
将目录更改为您在catkin工作区之前的教程中创建的beginner_tutorials包:
首先使用指令更改一下roscd的绝对路径:
$ . ~/catkin_ws/devel/setup.bash
然后使用
$ roscd beginner_tutorials
在beginner_tutorials包目录中创建一个src目录:
$ mkdir -p src
该目录将包含beginner_tutorials包的任何源文件。
在beginner_tutorials包中创建src / talker.cpp文件并在其中粘贴以下内容:
#include "ros/ros.h"
/*ros/ros.h是一个方便的包括,包括使用ROS系统最常见的公共部分所需的所有标题。*/
#include "std_msgs/String.h"
#include <sstream>
/*这包括std_msgs / String消息,该消息驻留在std_msgs包中。
这是从该包中的String.msg文件自动生成的标头。*/
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
/*初始化ROS。这允许ROS通过命令行进行名称重映射 - 现在不重要。这也是我们
指定节点名称的地方。节点名称在正在运行的系统中必须是唯一的.
此处使用的名称必须是基本名称,即。它不能有/在其中。*/
ros::NodeHandle n;
/*为此进程的节点创建句柄。创建的第一个NodeHandle实际上将执行节点的初始化*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
/*告诉主人我们将在主题chatter中发布类型为std_msgs / String的消息。
这让主人告诉任何监听chatter节点的节点,我们将发布有关该主题的数据。
第二个参数是我们的发布队列的大小。在这种情况下,如果我们发布得太快,
它将在开始丢弃旧消息之前缓冲最多1000条消息。
NodeHandle::advertise()返回一个ros::Publisher对象,它有两个目的:
1)它包含一个publish()方法,它允许你将消息发布到它创建的主题上,以及
2)当它超出范围时,它会自动取消广告。*/
ros::Rate loop_rate(10);
/*使用ros :: Rate对象可以指定要循环的频率。它将跟踪自上次调用
Rate :: sleep()以来的持续时间,并在正确的时间内休眠。在这种情况下,
我们告诉它我们想要以10Hz运行。*/
int count = 0;
/*默认情况下,roscpp将安装一个SIGINT处理程序,该处理程序提供Ctrl-C处理,
如果发生这种情况,将导致ros :: ok()返回false。如果出现以下情况,
ros :: ok()将返回false:
收到SIGINT(Ctrl-C)
我们已被另一个具有相同名称的节点踢出网络
ros :: shutdown()已被应用程序的另一部分调用。
所有ros ::NodeHandles都被销毁了
一旦ros :: ok()返回false,所有ROS调用都将失败。
*/
while (ros::ok())
{
/*我们使用消息适配类在ROS上广播消息,通常从msg文件生成。
更复杂的数据类型是可能的,但是现在我们将使用标准的String消息,
它有一个成员:“data”。*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
/*ROS_INFO和friend是printf/cout的替代品。*/
ROS_INFO("%s", msg.data.c_str());
/*将消息广播给任何已连接的人。*/
chatter_pub.publish(msg);
/*这个简单的程序不需要调用ros :: spinOnce(),因为我们没有收到任何回调。
但是,如果您要在此应用程序中添加订阅,并且此处没有ros::spinOnce(),
则永远不会调用您的回调。所以,添加它是为了衡量。*/
ros::spinOnce();
/*现在我们使用ros::Rate对象在剩余时间内休眠,让我们达到10Hz的发布速度。*/
loop_rate.sleep();
++count;
}
return 0;
}
这是正在发生的事情的浓缩版本:
初始化ROS系统
宣传我们将把聊天主题上的std_msgs / String消息发布给master
循环发布消息,每秒10次聊天
现在我们需要编写一个节点来接收这些消息。
4.2、编写订阅者节点
在beginner_tutorials包中创建src / listener.cpp文件并在其中粘贴以下内容:
#include "ros/ros.h"
#include "std_msgs/String.h"
/*这是一个回调函数,当一个新消息到达chatter主题时将被调用。消息在
boost shared_ptr中传递,这意味着您可以根据需要将其存储起来,而
不必担心它会被删除,并且不会复制基础数据。*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
/*与主人订阅chatter主题。每当有新消息到达时,ROS都会调用chatterCallback()
函数。第二个参数是队列大小,以防我们无法足够快地处理消息。在这种情况下,
如果队列达到1000条消息,我们将在新消息到达时开始丢弃旧消息。
NodeHandle :: subscribe()返回一个ros::Subscriber对象,您必须保留该对象,
直到您想取消订阅为止。订阅者对象被销毁后,它将自动取消订阅chatter主题。
NodeHandle::subscribe()函数有一些版本允许您指定类成员函数,甚至可以指定
Boost.Function对象可调用的任何函数。该roscpp概述包含更多的信息。*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
/*ros::spin()进入一个循环,尽可能快地调用消息回调。不过不用担心,如果没有什么
可以做的话就不会占用太多的CPU。只要ros::ok()返回false,ros::spin()将退出,
这意味着已经调用了ros::shutdown(),默认的Ctrl-C处理程序,master告诉我们关
闭,或者手动调用它。*/
ros::spin();
return 0;
}
再次,这是一个精简版的正在发生的事情:
初始化ROS系统
订阅聊天主题
旋转,等待消息到达
当消息到达时,将调用chatterCallback()函数
五、ROS单线程和多线程订阅
5.1、单线程订阅
#include <ros/ros.h>
#include <std_msgs/Bool.h>
void tempCallback(const std_msgs::BoolConstPtr& msg)
{
int num = 10;
ros::Rate loop_rate(10);
while (ros::ok() && num)
{
ROS_INFO_STREAM("temp temp temp temp temp temp temp temp call back, num->"<<num);
num--;
loop_rate.sleep();
}
}
void longCallback(const std_msgs::BoolConstPtr& msg)
{
int num = 10;
ros::Rate loop_rate(10);
while (ros::ok() && num)
{
ROS_INFO_STREAM("long call back, num->"<<num);
num--;
loop_rate.sleep();
}
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "test_node");
ros::NodeHandle nh;
ros::Subscriber tempSub = nh.subscribe<std_msgs::Bool>("temp", 1, tempCallback);
ros::Subscriber longSub = nh.subscribe<std_msgs::Bool>("long", 1, longCallback);
ros::spin();
return 0;
}
输入:
rostopic pub /long std_msgs/Bool "data: false" -r 10
rostopic pub /temp std_msgs/Bool "data: false" -r 10
输出:
[ INFO] [1565918138.532995825]: long call back, num->10
[ INFO] [1565918138.633079367]: long call back, num->9
[ INFO] [1565918138.733064686]: long call back, num->8
[ INFO] [1565918138.832994117]: long call back, num->7
[ INFO] [1565918138.932998012]: long call back, num->6
[ INFO] [1565918139.032996832]: long call back, num->5
[ INFO] [1565918139.133071165]: long call back, num->4
[ INFO] [1565918139.232960431]: long call back, num->3
[ INFO] [1565918139.332995614]: long call back, num->2
[ INFO] [1565918139.432999905]: long call back, num->1
[ INFO] [1565918139.533033535]: temp temp temp temp temp temp temp temp call back, num->10
[ INFO] [1565918139.633149213]: temp temp temp temp temp temp temp temp call back, num->9
[ INFO] [1565918139.733148347]: temp temp temp temp temp temp temp temp call back, num->8
[ INFO] [1565918139.833150790]: temp temp temp temp temp temp temp temp call back, num->7
[ INFO] [1565918139.933050574]: temp temp temp temp temp temp temp temp call back, num->6
[ INFO] [1565918140.033141442]: temp temp temp temp temp temp temp temp call back, num->5
[ INFO] [1565918140.133159048]: temp temp temp temp temp temp temp temp call back, num->4
[ INFO] [1565918140.233151712]: temp temp temp temp temp temp temp temp call back, num->3
[ INFO] [1565918140.333148631]: temp temp temp temp temp temp temp temp call back, num->2
[ INFO] [1565918140.433147718]: temp temp temp temp temp temp temp temp call back, num->1
[ INFO] [1565918140.533266548]: long call back, num->10
[ INFO] [1565918140.633400237]: long call back, num->9
[ INFO] [1565918140.733407032]: long call back, num->8
[ INFO] [1565918140.833389468]: long call back, num->7
[ INFO] [1565918140.933386767]: long call back, num->6
[ INFO] [1565918141.033389566]: long call back, num->5
[ INFO] [1565918141.133389982]: long call back, num->4
[ INFO] [1565918141.233389902]: long call back, num->3
[ INFO] [1565918141.333395700]: long call back, num->2
[ INFO] [1565918141.433399861]: long call back, num->1
[ INFO] [1565918141.533413994]: temp temp temp temp temp temp temp temp call back, num->10
[ INFO] [1565918141.633477569]: temp temp temp temp temp temp temp temp call back, num->9
[ INFO] [1565918141.733526139]: temp temp temp temp temp temp temp temp call back, num->8
[ INFO] [1565918141.833527631]: temp temp temp temp temp temp temp temp call back, num->7
[ INFO] [1565918141.933526546]: temp temp temp temp temp temp temp temp call back, num->6
[ INFO] [1565918142.033560826]: temp temp temp temp temp temp temp temp call back, num->5
[ INFO] [1565918142.133522903]: temp temp temp temp temp temp temp temp call back, num->4
[ INFO] [1565918142.233525957]: temp temp temp temp temp temp temp temp call back, num->3
[ INFO] [1565918142.333525708]: temp temp temp temp temp temp temp temp call back, num->2
[ INFO] [1565918142.433521566]: temp temp temp temp temp temp temp temp call back, num->1
5.2、多线程订阅
#include <ros/ros.h>
#include <std_msgs/Bool.h>
void tempCallback(const std_msgs::BoolConstPtr& msg)
{
int num = 1;
ros::Rate loop_rate(10);
while (ros::ok() && num)
{
ROS_INFO_STREAM("temp temp temp temp temp temp temp temp call back, num->");
num--;
loop_rate.sleep();
}
}
void longCallback(const std_msgs::BoolConstPtr& msg)
{
int num = 10;
ros::Rate loop_rate(10);
while (ros::ok() && num)
{
ROS_INFO_STREAM("long call back, num->"<<num);
num--;
loop_rate.sleep();
}
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "test_node");
ros::NodeHandle nh;
ros::Subscriber tempSub = nh.subscribe<std_msgs::Bool>("temp", 1, tempCallback);
ros::Subscriber longSub = nh.subscribe<std_msgs::Bool>("long", 1, longCallback);
ros::MultiThreadedSpinner spinner(3);//三个线程订阅
spinner.spin();
return 0;
}
输入:
rostopic pub /long std_msgs/Bool "data: false" -r 10
rostopic pub /temp std_msgs/Bool "data: false" -r 10
输出:
[ INFO] [1565917840.270586066]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.332730168]: long call back, num->10
[ INFO] [1565917840.370680016]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.432812052]: long call back, num->9
[ INFO] [1565917840.470733027]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.532746151]: long call back, num->8
[ INFO] [1565917840.570822519]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.632748312]: long call back, num->7
[ INFO] [1565917840.670920845]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.732748731]: long call back, num->6
[ INFO] [1565917840.771020449]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.832749593]: long call back, num->5
[ INFO] [1565917840.871116535]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917840.932749685]: long call back, num->4
[ INFO] [1565917840.971215785]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.032749710]: long call back, num->3
[ INFO] [1565917841.071313507]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.132749230]: long call back, num->2
[ INFO] [1565917841.171407053]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.232743868]: long call back, num->1
[ INFO] [1565917841.271526256]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.332755181]: long call back, num->10
[ INFO] [1565917841.371625145]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.432905582]: long call back, num->9
[ INFO] [1565917841.471753894]: temp temp temp temp temp temp temp temp call back, num->
[ INFO] [1565917841.532846109]: long call back, num->8
六、创建编译并运行完整的ros helloword工程(c++版-加深理解)
6.1 、创建工作空间
1. 创建catkin工作空间
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
2. 编译catkin工作空间
$ cd ~/catkin_ws/
$ catkin_make
3.配置环境变量
$ source devel/setup.bash
echo $ROS_PACKAGE_PATH # 查看刚刚配置的环境变量是否生效
————————————————
6.2 、创建软件包(project)
创建名称为 my_ros_helloworld_cpluse 的软件包
cd catkin_ws/src # 进入工作空间下的 src 目录 (注意路径!!!)
catkin_create_pkg my_ros_helloworld_cpluse std_msgs rospy roscpp
cd .. #退到 catkin_ws 工作空间路径下
catkin_make #编译软件包
6.3、工程创建
1、 编写talker.cpp 创建消息发布器节点 (发布话题)
创建src/talker.cpp 文件
#include <iostream>
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <sstream>
using namespace std;
int main( int argc, char** argv )
{
// 创建节点
ros::init(argc, argv, "talker_node");
ros::NodeHandle n;
// 发布话题
ros::Publisher talkerPub = n.advertise<std_msgs::String>("chatter", 10);
// 设置发送频率
ros::Rate loop_rate(10);
int count = 0;
while(ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss << "hello world" <<count; // 将“hello world” 和 count 一起输出到 string流中
msg.data = ss.str(); // String 转为 str 作为消息体的数据
ROS_INFO("%s", msg.data.c_str());
talkerPub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
count ++;
}
return 0;
}
2、 编写listener.cpp 创建消息接收器节点 (订阅话题)
创建src/listener.cpp 文件
#include <iostream>
#include <ros/ros.h>
#include <std_msgs/String.h>
using namespace std;
// 定义回调函数
void listener_callback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard : [%s]" , msg->data.c_str()); // String 转为 str字符串
}
int main( int argc, char** argv)
{
// 创建节点
ros::init(argc, argv, "listener_node");
ros::NodeHandle n;
// 订阅话题 以及指定该话题的回调函数
ros::Subscriber listenerSub = n.subscribe("chatter", 10,listener_callback);
ros::spin();
return 0;
}
3、 编写CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(my_ros_helloworld_cpluse)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare a catkin package
catkin_package()
## 编译 talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(listener
src/listener.cpp
)
target_link_libraries(listener ${catkin_LIBRARIES})
add_executable(talker
src/talker.cpp
)
target_link_libraries(talker ${catkin_LIBRARIES})
4、 编译
在ros_test_ws 目录下 运行 catkin_make 命令编译,生成可执行文件 talker 以及 listener
6.4 、运行此工程
注意:用echo $ROS_PACKAGE_PATH 查看此工作空间是否在环境变量中
若没有则运行此命令 source ~/ros_test_ws/devel/setup.bash 也可以通过 直接将此命令写入 home目录下的 .bashrc文件中,这样就不需要每次都source ~/ros_test_ws/devel/setup.bash 。因为每打开一个新的终端,都会先加载 .bashrc文件。
确保环境变量加载正确,否则rosrun 找不到 软件包 !!!
开三个终端,分别运行如下命令:
1 运行 ros master
先运行命令 roscore
2 运行 talker
source ros_test_ws/devel/setup.bash
rosrun my_ros_helloworld_cpluse talker
————————————————
3 运行 listener
source ros_test_ws/devel/setup.bash
rosrun my_ros_helloworld_cpluse listener
七、launch文件
每当我们需要运行一个ROS节点或工具时,都需要打开一个新的终端运行一个命令。当系统中的节点数量不断增加时,每个节点一个终端的模式会变得非常麻烦。那么有没有一种方式可以一次性启动所有节点呢?答案当然是肯定的。
启动文件(Launch File)便是ROS中一种同时启动多个节点的途径,还可以自动启动ROSMaster节点管理器,而且可以实现每个节点的各种配置,为多个节点的操作提供了很大便利。
7.1、基本元素
首先来看一个简单的launch文件:
- <launch>
- <nodepkg="turtlesim"name="sim1" type="turtlesim_node"/>
- <nodepkg="turtlesim"name="sim2" type="turtlesim_node"/>
- </launch>
这是一个简单而完整的launch文件,采用XML的形式进行描述,包含一个根元素<launch>和两个节点元素<node>。
1.<launch>
XML文件必须要包含一个根元素,launch文件中的根元素采用<launch>标签定义,文件中的其他内容都必须包含在这个标签之中:
- <launch>
- ......
- </launch>
2. <node>
启动文件的核心是启动ROS节点,采用<node>标签定义,语法如下:
- <node pkg="package-name"type="executable-name" name="node-name" />
从上边的定义规则可以看出,在启动文件中启动一个节点需要三个属性:pkg、type和name。其中pkg定义节点所在的功能包名称,type定义节点的可执行文件名称,这两个属性等同于在终端中使用rosrun命令执行节点时的输入参数。name属性用来定义节点运行的名称,将覆盖节点中init()赋予节点的名称。这是三个最常用的属性,在某些情况下,我们还有可能用到以下属性:
· output = “screen”:将节点的标准输出打印到终端屏幕,默认输出为日志文档;
· respawn = “true”:复位属性,该节点停止时,会自动重启,默认为false;
· required = “true”:必要节点,当该节点终止时,launch文件中的其他节点也被终止;
· ns = “namespace”:命名空间,为节点内的相对名称添加命名空间前缀;
· args = “arguments”:节点需要的输入参数。
实际应用中的launch文件往往会更加复杂,使用的标签也会更多,例如一个启动机器人的launch文件如下:
<launch>
<node pkg="mrobot_bringup" type="mrobot_bringup" name="mrobot_bringup" output="screen" />
<arg name="urdf_file" default="$(find xacro)/xacro --inorder '$(find mrobot_description)/urdf/mrobot_with_rplidar.urdf.xacro'" />
<param name="robot_description" command="$(arg urdf_file)" />
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="state_publisher">
<param name="publish_frequency" type="double" value="5.0" />
</node>
<node name="base2laser" pkg="tf" type="static_transform_publisher" args="0 0 0 0 0 0 1 /base_link /laser 50"/>
<node pkg="robot_pose_ekf" type="robot_pose_ekf" name="robot_pose_ekf">
<remap from="robot_pose_ekf/odom_combined" to="odom_combined"/>
<param name="freq" value="10.0"/>
<param name="sensor_timeout" value="1.0"/>
<param name="publish_tf" value="true"/>
<param name="odom_used" value="true"/>
<param name="imu_used" value="false"/>
<param name="vo_used" value="false"/>
<param name="output_frame" value="odom"/>
</node>
<include file="$(find mrobot_bringup)/launch/rplidar.launch" />
</launch>
目前,我们只关注其中的标签元素,除了上边介绍的<launch>和<node>,这里还出现了<arg>、<param>、<remap>,这些都是常用的标签元素。
7.2、参数设置
为了方便设置和修改,launch文件支持参数设置的功能,类似于编程语言中的变量声明。关于参数设置的标签元素有两个:<param>、<arg>,一个代表parameter,另一个代表argument。这两个标签元素翻译成中文都是“参数”的意思,但是这两个“参数”的意义是完全不同的。
1. <param>
parameter是ROS系统运行中的参数,存储在参数服务器中。在launch文件中通过<param>元素加载parameter;launch文件执行后,parameter就加载到ROS的参数服务器上了。每个活跃的节点都可以通过 ros::param::get()接口来获取parameter的值,用户也可以在终端中通过rosparam命令获得parameter的值。
<param>的使用方法如下:
- <param name="output_frame" value="odom"/>
运行launch文件后,output_frame这个parameter的值就设置为odom,并且加载到ROS参数服务器上了。但是在很多复杂的系统中,参数的数量很多,如果这样一个一个的设置会非常麻烦,ROS也为我们提供了另外一种类似的参数加载方式——<rosparam>:
- <rosparamfile="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam>可以帮助我们将一个yaml格式文件中的参数全部加载到ROS参数服务器中,需要设置command属性为“load”,还可以选择设置命名空间“ns”。
2. <arg>
argument是另外一个概念,类似于launch文件内部的局部变量,仅限于launch文件使用,便于launch文件的重构,和ROS节点内部的实现没有关系。
设置argument使用<arg>标签元素,语法如下:
- <arg name=”arg-name” default=”arg-value”/>
launch文件中需要使用到argument时,可以使用如下方式调用:
- <paramname="foo" value="$(argarg-name)" />
- <node name="node" pkg="package" type="type "args="$(arg arg-name)" />
7.3、重映射机制
ROS的设计目标是提高代码的复用率,所以ROS社区中的很多功能包我们都可以拿来直接使用,而不需要关注功能包的内部实现。那么问题就来了,别人功能包的接口不一定和我们的系统兼容呀?
ROS提供一种重映射的机制,简单来说就是取别名,类似于C++中的别名机制,我们不需要修改别人功能包的接口,只需要将接口名称重映射一下,取个别名,我们的系统就认识了(接口的数据类型必须相同)。launch文件中的<remap>标签可以帮我们实现这个重映射的功能。
比如turtlebot的键盘控制节点,发布的速度控制指令话题可能是/turtlebot/cmd_vel,但是我们自己的机器人订阅的速度控制话题是/cmd_vel,这个时候使用<remap>就可以轻松解决问题,将/turtlebot /cmd_vel重映射为/cmd_vel,我们的机器人就可以接收到速度控制指令了:
- <remap from="/turtlebot/cmd_vel"to="/cmd_vel"/>
重映射机制在ROS中的使用非常广泛,也非常重要,方法不止这一种,也可以在终端rosrun命令中实现重映射,大家一定要理解好这种机制。
7.4、嵌套复用
在复杂的系统当中,launch文件往往有很多,这些launch文件之间也会存在依赖关系。如果需要直接复用一个已有launch文件中的内容,可以使用<include>标签包含其他launch文件,这和C语言中的include几乎是一样的。
- <include file="$(dirname)/other.launch" />
总而言之,launch是ROS框架中非常实用、灵活的功能,它类似于一种高级编程语言,可以帮助我们管理启动系统时的方方面面。在使用ROS的过程中,很多情况下我们并不需要编写大量代码,仅需要使用已有的功能包,编辑一下launch文件,就可以完成很多机器人功能。
https://blog.csdn.net/weicao1990/article/details/84400422
https://blog.csdn.net/weixin_37835423/article/details/99670885