ROS全称叫机器人操作系统(Robot Operating System, ROS),虽然它叫操作系统,但实际上和我们认识的linux、mac、windows不一样,它是基于ubuntu(linux)上运行的一套框架,我们编写的ROS程序都基于这套框架上运行。对于ROS系统,有一个重要的概念叫 “节点”,其实每个节点就是单独的一个程序,比如处理摄像头、雷达、电机等,都可以由不同的开发人员独自写一个程序运行并关联(通信),他们也都有一个属于自己的二进制文件。因为这套框架可以实现对各个进程的组织、通信、文件系统等功能,所以可以称它为一个操作系统,实际上就是一个运行在linux上的模拟操作系统。
环境搭建以及测试demo可以见链接
以下提到的一级目录、二级目录等为ROS文件系统图中的目录位置,例如工作空间为一级目录
功能:创建一个新的功能包
使用方法:catkin_create_pkg
示例:catkin_create_pkg ros_server std_msgs rospy roscpp
备注:示例中创建了一个新包,名为 ros_server,后面跟着的为这个包所需的依赖,可以暂时理解为我们在用c或c++编写多线程代码时,在编译阶段需要加-lpthread一样,这些依赖中包含了我们后续写代码用到的环境。std_msgs 为消息类型,创建节点通信时需要使用到,比如std_msgs::String,代表消息类型为string,剩下两个为c++和python所需要的依赖
功能:自动构建并编译代码
使用方法:catin_make
示例:catin_make
备注:注意,这个命令需要在二级目录下使用,使用后会自动去寻找cmakelists文件并根据其中的规则构建
功能:启动ROS主节点
使用方法:roscore
示例:roscore
备注:要启动ROS节点,必须先启动主节点,否则会运行失败
功能:运行节点
使用方法:rosrun [–prefix cmd] [–debug] pkg_name node_name [ARGS]
示例:rosrun ros_server ros_server_node
备注: [–prefix cmd] [–debug]为可选参数,大多数情况不使用,后续使用到再补充,pkg_name 为包名,也就是catkin_create_pkg创建的包名,node_name为包中的节点名,因为一个包中可能有多个不同节点,所以需要指定具体节点
功能:查看节点相关信息
使用方法:rosnode
示例:rosnode list、rosnode ping server等
备注:这里用到的节点名称默认为我们程序调用ros:init时的参数名,如果我们指定了运行名,比如在roslaunch中指定了name,那么节点名就是指定的名字,roslaunch下面介绍
功能:可以一次启动多个节点,并且可以在启动时指定参数信息
使用方法:roslaunch pkg_name launch_file [ARGS]
示例:roslaunch ros_server ros_server.launch
备注:pkg_name为三级目录创建的功能包名。 launch_file为创建的.launch文件,这个文件一般需要我们手动创建,建议创建在四级目录下的launch下。ARGS为可选参数,下面会举例
首先写一个获取参数并打印的ros节点编译后运行
#include "ros/ros.h"
int main(int argc, char *argv[])
{
//执行 ros 节点初始化
ros::init(argc,argv,"hello");
//创建 ros 节点句柄(非必须)
ros::NodeHandle n;
//控制台输出 hello world
printf("%s\n",argv[1]);
return 0;
}
08:02 下午 ros_t rosrun demo demo_node hello
[ INFO] [1702382616.197735701]: hello
可以看到原样输出了我们的参数hello,现在是使用rosrun手动启动节点,下面介绍launch启动
由第二节的文件系统图上可以看到,一般来说我们可以在功能包下手动创建一个lanuch的文件夹,可以把我们的.lanuch文件存放在这个目录下
launch 文件中使用的是xml格式来表示信息,xml以标签的形式展示数据,以
<launch>
<node ...></node>
<param .../>
<rosparam .../>
<include .../>
<remap .../>
<arg .../>
<group> </group>
</launch>
下面详细解释上图各个标签标识的含义,节点中并不要求这些标签都有,最简单的有lanuch与node就行
lanuch
为lanuch文件的根节点,无特殊作用
node
node标签可以指定一个节点的允许,如果存在多个node标签,则可以实现同时启动多个节点,比手动去一个个rosrun方便许多
<launch>
<node pkg="package_name" type="executable_node" name="node_name" args="$()" respawn="true" output="sceen">
</launch>
pkg:为功能包名;
type:为可执行文件名;
name:可以指定节点运行名(rosnode可以查看,如果rosrun运行不指定则节点名为代码中init指定的名字);agrs:为程序运行的参数;
respawn:是否自动重启,true表示如果节点未启动或异常关闭,则自动重启;false则表示不自动重启,默认值为false;
output:是否将节点信息输出到屏幕,如果不设置该属性,则节点信息会被写入到日志文件,并不会显示到屏幕上。
示例:
1.使用nodo中的args传递参数
执行roslaunch demo demo.launch后发现终端上没有输出hello,是因为roslaunch 启动后把标准输出的内容给重定向到一个.log中了,具体log名请看运行后的提示,如果想在终端打印可以添加--screen选项,roslaunch --screen demo demo.launch
2. 还可以使用arg或param等方式传递,见下文
<launch>
<!-- ... 其他配置 ... -->
<node name="my_node" pkg="my_package" type="my_node" >
<param name="param_name" type="param_type" value="param_value" />
</node>
</launch>
编写一段测试代码
#include "ros/ros.h"
int main(int argc, char *argv[])
{
//执行 ros 节点初始化
ros::init(argc,argv,"hello");
std::string data;
//创建 ros 节点句柄(非必须)
ros::NodeHandle n;
if(ros::param::get("/demo_hello/arg1",data)){
printf("arg1 is %s\n",data.c_str());
}else{
printf("not find\n");
}
return 0;
}
launch文件内容为:
<launch>
<node pkg="demo" type="demo_node" name="demo_hello" args="hello">
<param name="arg1" value="hello111" type="string"/>
</node>
</launch>
运行结果为
arg1 is hello111
这里需要注意的是,我们的param name写的是arg1,但是在源文件中,get的参数前带有路径,这个路径代表这个节点的节点名,如果不带上这个路径,则会捕获失败,这个参数也可以在运行roslaunch时看到
04:13 下午 ros_t roslaunch --screen demo demo.launch
... logging to /home/lotus/.ros/log/fba405be-9d7c-11ee-953a-a1a059285c1b/roslaunch-master-2230523.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.
started roslaunch server http://10.167.120.3:34087/
SUMMARY
========
PARAMETERS
* /demo_hello/arg1: hello111 --------------这里就是我们的参数信息
* /rosdistro: noetic
* /rosversion: 1.15.15
NODES
/
demo_hello (demo/demo_node)
ROS_MASTER_URI=http://localhost:11311
process[demo_hello-1]: started with pid [2230635]
arg1 is hello111 ----------------打印出的消息
[demo_hello-1] process has finished cleanly
log file: /home/lotus/.ros/log/fba405be-9d7c-11ee-953a-a1a059285c1b/demo_hello-1*.log
all processes on machine have died, roslaunch will exit
shutting down processing monitor...
... shutting down processing monitor complete
done
表示可以加载file.yaml中的配置,功能上与param的launch文件中添加多个param标签没什么区别,使用rosparam方便多个节点同用一个配置
<include file="$(find package_name)/launch_file_name">
<remap from="/pub_data" to="/talker" />
表示把pub_data这个topic名字映射为talker供别人使用
from=“xxx”: 你节点中原来发布的主题名字为xxx
to=“yyy”: 重映射的目标名字为yyy
注意当remap这个标签放在不同的位置,作用域是不同的,具体作用域见:remap作用域说明
<arg name="name" default="value"(可选) value="arg_value"(可选)/>
default为这个参数的默认值
value为这个参数的设置值
注意,命令行传递的参数可以覆盖default的值,但是不能覆盖value的值
例如launch文件内容是这样的:
<launch>
<arg name="xxx" value="v"/)
</launch>
当我们命令行调用roslaunch demo.launch xxx:="vvv"时,xxx还是取的还是value原本的值(v),会忽略命令行的参数(vvv),如果是default反之
那么有一个问题,既然arg与param和rosparam都是给节点传递参数,那么他们有什么区别呢?
其实arg参数作用域在这个launch文件中,文件中可以使用这个标签定义的变量,而param与rosparam则会设置进rosmaster,便于各个节点使用
group
group标签可以实现将一组配置应用到组内的所有节点,它也具有命名空间ns特点,可以将不同的节点放入不同的 namespace。
<!-- 用法1 -->
<group ns="namespace_1">
<node pkg="pkg_name1" .../>
<node pkg="pkg_name2" .../>
...
</group>
<group ns="namespace_2">
<node pkg="pkg_name3" .../>
<node pkg="pkg_name4" .../>
...
</group>
<!-- 用法2 -->
<!-- if = value:value 为 true 则包含内部信息 -->
<group if="$(arg foo1)">
<node pkg="pkg_name1" .../>
</group>
<!-- unless = value:value 为 false 则包含内部信息 -->
<group unless="$(arg foo2)">
<node pkg="pkg_name2" .../>
</group>
<!--
当 foo1 == true 时包含其标签内部
当 foo2 == false 时包含其标签内部
-->
rostopic list
列出所有的topic(没有消息传递的topic可能不会展示)
rostopic info
通过此命令,您可以获取特定话题的详细信息,包括消息类型、发布者和订阅者等。
rostopic echo
可以实时展示话题消息内容
rostopic pub
发布消息到对应的topic上
例如:rostopic pub /chatter std_msgs/String “hello”
rostopic pub -r
以指定速率持续发送消息到对应topic上
rostopic type
查看话题消息类型
rostopic hz
查看话题的频率