ROS操作系统

一、初识ROS

1. 什么是ROS操作系统,它跟linux有什么关系?

ROS全称叫机器人操作系统(Robot Operating System, ROS),虽然它叫操作系统,但实际上和我们认识的linux、mac、windows不一样,它是基于ubuntu(linux)上运行的一套框架,我们编写的ROS程序都基于这套框架上运行。对于ROS系统,有一个重要的概念叫 “节点”,其实每个节点就是单独的一个程序,比如处理摄像头、雷达、电机等,都可以由不同的开发人员独自写一个程序运行并关联(通信),他们也都有一个属于自己的二进制文件。因为这套框架可以实现对各个进程的组织、通信、文件系统等功能,所以可以称它为一个操作系统,实际上就是一个运行在linux上的模拟操作系统。

2. ROS的文件系统(ROS架构)

ROS操作系统_第1张图片

  1. 源文件空间src :所有的包都存放在src文件夹中
  2. build :由catkin_make命令自动生成,用来存储工作空间编译过程中产生的缓存信息和中间文件。
  3. devel :由catkin_make命令自动生成,用来放置编译生成的可执行文件。
  4. package :存放我们的各个功能包,每个功能包都可以对应一些功能,比如一个功能包里是为电机驱动写的,我们可以在一个功能包中写多个电机驱动的代码,最后生成多个节点(多个二进制程序),当然也可以一个功能包对应一个节点,具体看个人设计实现
  5. cmakelists:功能包层面的一般不用动,功能包中的需要我们自己写一下,比如二进制怎么生成,需要什么库等等,cmakelists教程不会的可以搜一下
  6. package.xml :这个文件catkin_make会自动生成,里面存放了功能包清单,可得到该package的名称、版本号、信息描述、作者信息和许可信息等。更重要的,标签定义了代码编译所依赖的其它功能包,标签定义了可执行程序运行时所依赖的其它功能包。package.xml文件是功能包被ROS系统识别的前提。
  7. include :存放c++编写所需要的头文件
  8. msg:存放自定义消息类型,由于ROS各个节点之间存在通信,通信有数据类型,除了ROS自带的数据类型,我们也可以自定义
  9. srv :自定义服务类型
  10. scripts:可以直接运行的脚本,如python、shell等
  11. launch :存放快速启动的 .launch文件,为xml格式,有这个文件后就可以通过roslaunch来快速启动一个或多个节点
  12. 包中src :存放实际cpp代码

二、ROS安装

环境搭建以及测试demo可以见链接

1.ROS安装及测试

三、ROS命令以及使用

以下提到的一级目录、二级目录等为ROS文件系统图中的目录位置,例如工作空间为一级目录

1. catkin_create_pkg

功能:创建一个新的功能包
使用方法:catkin_create_pkg [depend1] [depend2] [depend3]
示例:catkin_create_pkg ros_server std_msgs rospy roscpp
备注:示例中创建了一个新包,名为 ros_server,后面跟着的为这个包所需的依赖,可以暂时理解为我们在用c或c++编写多线程代码时,在编译阶段需要加-lpthread一样,这些依赖中包含了我们后续写代码用到的环境。std_msgs 为消息类型,创建节点通信时需要使用到,比如std_msgs::String,代表消息类型为string,剩下两个为c++和python所需要的依赖

2. catkin_make

功能:自动构建并编译代码
使用方法:catin_make
示例:catin_make
备注:注意,这个命令需要在二级目录下使用,使用后会自动去寻找cmakelists文件并根据其中的规则构建

3. roscore

功能:启动ROS主节点
使用方法:roscore
示例:roscore
备注:要启动ROS节点,必须先启动主节点,否则会运行失败

4. rosrun

功能:运行节点
使用方法: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为包中的节点名,因为一个包中可能有多个不同节点,所以需要指定具体节点

5. rosnode

功能:查看节点相关信息
使用方法:rosnode
示例:rosnode list、rosnode ping server等
ROS操作系统_第2张图片
备注:这里用到的节点名称默认为我们程序调用ros:init时的参数名,如果我们指定了运行名,比如在roslaunch中指定了name,那么节点名就是指定的名字,roslaunch下面介绍

6. 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文件的内容

launch 文件中使用的是xml格式来表示信息,xml以标签的形式展示数据,以标签开始 标签结尾(或者把与开始的标签合并),中可以包含多个kv对,类似下面这个结构(…表示kv对的数据信息),详细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等方式传递,见下文
  • param
    作用是为了给节点传递不同的参数,类似与我们在运行ls -l时,指定的-l参数,param可以在.cpp源文件中使用ros::param::get()来捕获设置的参数,来运行对应的业务逻辑。
<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

  • rosparam
    功能与param类似,区别是rosparam可以使用command指令,如:


表示可以加载file.yaml中的配置,功能上与param的launch文件中添加多个param标签没什么区别,使用rosparam方便多个节点同用一个配置

  • include
    与cpp的include预处理功能类似,可以引入其他的launch文件到当前位置,实现复用功能
<include file="$(find package_name)/launch_file_name">
  • remap
    作用是重新映射节点中topic的名字
<remap from="/pub_data" to="/talker" /> 
表示把pub_data这个topic名字映射为talker供别人使用
from=“xxx”: 你节点中原来发布的主题名字为xxx
to=“yyy”: 重映射的目标名字为yyy

注意当remap这个标签放在不同的位置,作用域是不同的,具体作用域见:remap作用域说明

  • arg
    作用类似与param与rosparam,也是传递给节点参数,但是arg的作用域在这个脚本内,类似与设置脚本中的一个变量,各个节点都可以调用这个变量获取内容,并且支持launch文件的动态传参
<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 时包含其标签内部
-->

7. rostopic

  • 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
    查看话题的频率

你可能感兴趣的:(ROS,linux,机器人)