1、ros-综述

1.1. ros node

Node 

  • 在ROS的世界里, 最小的进程单元就是节点( node) 。
  • 一个软件包里可以有多个可执行文件, 可执行文件在运行之后就成了一个进程(process), 这个进程在ROS中就叫做节点。

Master

  • 由于机器人的元器件很多, 功能庞大, 因此实际运行时往往会运行众多的node。
  • ROS提供节点管理器master, master在整个网络通信架构里相当于管理中心, 管理着各个node。
  •  node首先在master处进行注册, 之后master会将该node纳入整个ROS程序中。
  • node之间的通信也是先由master进行“牵线”, 才能两两的进行点对点通信。
  • 当ROS程序启动时, 第一步首先启动master, 由节点管理器处理依次启动node。

启动master和node

  • 当我们要启动ROS时, 首先输入命令:  roscore

  • 此时ROS master启动, 同时启动的还有 rosout  parameter server 
  • 其中 rosout 是负责日志输出的一个节点, 其作用是告知用户当前系统的状态, 包括输出系统的error、 warning等,并且将log记录于日志文件中。
  •  parameter server 即是参数服务器, 它并不是一个node,而是存储参数配置的一个服务

rosnode 终端命令

  • - rosnode ping 测试与节点的连接
  • - rosnode list
  • - rosnode machine 列出在特定计算机上运行的节点
  • - rosnode kill
  • - rosnode clean 清除不可达节点的注册信息

1.2 ros spin/spinOnce

ros spin()

  • 循环且监听反馈函数(callback)。循环就是指程序运行到这里,就会一直在这里循环了。监听反馈函数的意思是,如果这个节点有callback函数,那写一句ros::spin()在这里,就可以在有对应消息到来的时候,运行callback函数里面的内容。

  •  写在这句话后面的代码不会被执行),适用于订阅节点,且订阅速度没有限制的情况

spinOnce()

  • 监听反馈函数(callback)。只能监听反馈,不能循环。所以当你需要监听一下的时候,就调用一下这个函数。 

小结

  1. 在使用ros::spin()的情况下,一般来说在初始化时已经设置好所有消息的回调,并且不需要其他背景程序运行。这样一来,每次消息到达时会执行用户的回调函数进行操作,相当于程序是消息事件驱动的;
  2. 而在使用ros::spinOnce()的情况下,一般来说仅仅使用回调不足以完成任务,还需要其他辅助程序的执行:比如定时任务、数据处理、用户界面等。

1.3 ros callback

  1. 关于消息接收回调机制在ROS官网上略有说明 (callbacks and spinning)。总体来说其原理是这样的:除了用户的主程序以外,ROS的socket连接控制进程会在后台接收订阅的消息,所有接收到的消息并不是立即处理,而是等到spin()或者spinOnce()执行时才集中处理。所以为了保证消息可以正常接收,需要尤其注意spinOnce()函数的使用 (对于spin()来说则不涉及太多的人为因素)。
  2. 对于速度较快的消息,需要注意合理控制消息队列及spinOnce()的时间。例如,如果消息到达的频率是100Hz,而spinOnce()的执行频率是10Hz,那么就要至少保证消息队列中预留的大小大于10。
  3. 如果对于用户自己的周期性任务,最好和spinOnce()并列调用。即使该任务是周期性的对于数据进行处理,例如对接收到的IMU数据进行Kalman滤波,也不建议直接放在回调函数中:因为存在通信接收的不确定性,不能保证该回调执行在时间上的稳定性。

  • 注意:回调函数的排队和轮转,不会对内部的网路通信造成影响,它们仅仅会影响到用户的回调函数何时发生。它们会影响到订阅者队列。因为处理你回调函数的速度,你消息到来的速度,将会决定以前的消息会不会被丢弃。

单线程下的轮转


    1. ros::init(argc, argv, "my_node"); //初始化节点
      ros::NodeHandle nh;           //创建节点句柄
      ros::Subscriber sub = nh.subscribe(...);  //创建消息订阅者
      ...
      ros::spin();                 //调用spin(),统一处理消息
       
      //---------------------------------------------------------
      ros::Rate r(10); // 10 hz
      while (should_continue)
      {
        //... do some work, publish some messages, etc. ...
        ros::spinOnce();     //轮转一次,返回
        r.sleep();        //休眠
      }

多线程轮转

roscpp库提供了一些内嵌的支持来从多线程中调用回调函数.

1) ros::MultiThreadedSpiner

它是一个阻塞型轮转器,类似于ros::spin().

可以使用它的构造器来设定线程的个数,如果不设置或设成0,它将为每个cpu核心使用一个线程。


    1. ros::MultiThreadedSpinner spinner(4);// Use 4 threads<br>spinner.spin(); 
      // spin() will not return until the node has been shutdown

2)ros::AsyncSpinner

API : http://docs.ros.org/api/roscpp/html/classros_1_1AsyncSpinner.html

更实用的多线程轮转是异步轮转器(AsyncSpiner),相对于阻塞的spin()调用,它有自己的start()和stop()调用

并且在销毁后将自动停止。

对上述MultiThreadedSpiner等效的AsyncSpiner使用如下:


    1. ros::AsyncSpinner spinner(4); // Use 4 threads
      spinner.start();
      ros::waitForShutdown();

1.4 topic /server

  话题 服务
同步性 异步 同步
通信模式 发布/订阅 服务器/客户端
底层协议 ROSTCP/ROSUDP ROSTCP/ROSUDP
反馈机制
缓存区
实时性
节点关系 多对多 一对多(一个server)
使用场景 数据传输 逻辑处理

 

1.5 rosparam

  • rosparam list 列出当前所有参数
  • rosparam get param_key显示某个参数
  • rosparam set param_key param_value设置某个参数
  • rosparam dump file_name 保存参数到文件
  • rosparam load file_name 从文件读取参数
  • rosparam delete param_key 删除参数

2、ros 小知识

2.1. 使用rosed编辑ROS中的文件

rosed 是 rosbash 的一部分。利用它可以直接通过package名来获取到待编辑的文件而无需指定该文件的存储路径了。

  • $ rosed [package_name] [filename]

使用Tab键补全文件名  $ rosed [package_name] <tab>

1.1 编辑器修改 

  •  export EDITOR=’gedit’  

rosed默认的编辑器是vim。如果想要将其他的编辑器设置成默认的,你需要修改你的 ~/.bashrc 文件,增加如下语句:

2.2. 理解 ROS节点

  • Nodes:节点,一个节点即为一个可执行文件,它可以通过ROS与其它节点进行通信。

  • Messages:消息,消息是一种ROS数据类型,用于订阅或发布到一个话题。

  • Topics:话题,节点可以发布消息到话题,也可以订阅话题以接收消息。

  • Master:节点管理器,ROS名称服务 (比如帮助节点找到彼此)。

  • rosout: ROS中相当于stdout/stderr。

  • roscore: 主机+ rosout + 参数服务器 (参数服务器会在后面介绍)。

rosnode

打开一个新的终端, 可以使用 rosnode 像运行 roscore 一样看看在运行什么…  

  • $ rosnode list

rosnode info 命令返回的是关于一个特定节点的信息。 

  •  $ rosnode info /rosout

rosrun

rosrun 允许你使用包名直接运行一个包内的节点(而不需要知道这个包的路径)。

  • $ rosrun [package_name] [node_name]

2.3.理解ROS服务和参数

  • rosservice list         输出可用服务的信息

  • rosservice call         调用带参数的服务

  • rosservice type         输出服务类型

  • rosservice find         依据类型寻找服务find services by service type

  • rosservice uri          输出服务的ROSRPC uri

2.4.录制与回放数据

  • rosbag record -a

在这里我们先建立一个用于录制的临时目录,然后在该目录下运行rosbag record命令,并附加-a选项,该选项表示将当前发布的所有话题数据都录制保存到一个bag文件中。

检查并回放bag文件   

  • rosbag info <your bagfile>     

  •   rosbag play <your bagfile>

录制数据子集

  • rosbag recored /tf /odom …

  • rosbag record -O subset /turtle1/command_velocity /turtle1/pose

上述命令中的-O参数告诉rosbag record将数据记录保存到名为subset.bag的文件中,同时后面的话题参数告诉rosbag record只能录制这两个指定的话题。然后通过键盘控制turtle随处移动几秒钟,最后按Ctrl+C退出rosbag record命令。

2.4.1 rosbag包滤出消息

1.滤出tf中map

rosbag filter data.bag data_filtered.bag “topic != ‘/tf’ or (topic == ‘/tf’ and m.transforms[0].header.frame_id != ‘map’)”

2.4.2 循环播放

  • rosbag play -l <bagfile> # -l == —loop

2.4.3 只播放感兴趣话题

  • rosbag play <bagfile> —topic /topic1 /topic2

2.5 工具

2.5.1. 3D可视化   RVIZ

  • rviz是一个3D可视化环境,可让您将传感器数据,机器人模型和其他3D数据组合到一个组合视图中。 您也可以从软件中将自己的3D标记发送到rviz。

2.5.2. rosbag和rqt_bag

  • rosbag是一个命令行工具,用于将消息录制和回放到“bag”文件中。

  • rqt_bag是一个可视化工具,可让您查看包文件中记录的数据。

2.5.3.实时绘制   rqt_plot

  • rqt_plot允许您可视化发布到ROS主题的标量数据。

2.5.4.系统可视化  rqt_graph

  • rqt_graph显示ROS中运行的进程及其连接的可视图。

2.5.5 日志输出 rqt_console

2.5.5.命令行工具

2.5.6.运行ros系统

Launching/configuring multiple programs

roslaunch

Run a single program

rosrun

Bringup core system

roscore


 

 

 

2.5.7.与运行系统交互和调试

Topics

rostopic

Services

rosservice

Nodes

rosnode

Parameters

rosparam

Messages

rosmsg

Services

rossrv

General debugging

roswtf

 

 

 

 

 

 

 

 

2.5.8.安装,编译和文件系统工具

Build

rosmake

Install from source

rosinstall

Searching for packages/stacks

roslocate

Install thirdparty libraries

rosdep

Packages

rospackroscd

Stacks

rosstackroscd

 

 

 

 

2.6 ROS 分布部署环境变量的配置

首先可看下关于本机的ROS环境变量配置     

  • $ printenv | grep ROS 

//显示类似如下


    1. ROS_ROOT=/opt/ros/kinetic/share/ros
      ROS_PACKAGE_PATH=/opt/ros/kinetic/share
      ROS_MASTER_URI=http://localhost:11311
      ROSLISP_PACKAGE_DIRECTORIES=
      ROS_DISTRO=kinetic
      ROS_ETC_DIR=/opt/ros/kinetic/etc/ros
      其中有一个 ROS_MASTER_URI=http://localhost:11311 

  • 如果我们的所有ROS节点程序都运行在一个主机上,那么当然roscore 也就是所谓的节点管理器的地址可设置为本地地址,端口为11311,我们运行的节点就会根据这个环境变量找到这个节点管理器。

  • 但是我们ROS一般不这么用,如果这么用,就完全失去了ROS的魅力所在了,ROS是松耦合、分布式的,我们的ROS节点可以运行在不同的平台不同机器上,所以这时的分布式的环境,就需要进一步ROS的环境变量了。


    1. export ROS_MASTER_URI=http://192.168.1.4:11311
      export ROS_IP=192.168.1.4
      //这是运行在roscore 就是master node的URI 和所在机器的ip
       
      export ROS_MASTER_URI=http://192.168.1.4:11311
      export ROS_IP=192.168.1.116 
      rosrun pkg-name node-name
      //这是我们运行在另一台机器上的ros节点   指定master的端口和地址   还有自身的地址

 

3. ros 技巧

3.1 rostopic pub array

    1. rostopic pub /client_array std_msgs/UInt32MultiArray "layout:
        dim:
        - label: ''
          size: 0
          stride: 0
        data_offset: 0
      data: [500,500,200]"

3.2  urdf

启动

    1. <?xml version="1.0"?>
      <launch>
        <arg name="robot_type" default="$(env ROBOT_TYPE)"/>
       
      <param name="robot_description"
             textfile="$(env MEGBOT_ROOT)/robot_config/$(arg robot_type)/megbot.urdf" />
        <node name="robot_state_publisher" pkg="robot_state_publisher"
          type="robot_state_publisher" >
        <param name="use_tf_static" value="false"/>
        <param name="publish_frequency" value="20"/>
      </node>
      </launch>

urdf 


    1. <robot name="robot">
          <material name="orange">
          <color rgba="1.0 0.5 0.2 1" />
          </material>
          <material name="gray">
          <color rgba="0.2 0.2 0.2 1" />
          </material>
       
          <link name="imu_link">
          <visual>
            <origin xyz="0 0 0" />
            <geometry>
              <box size="0.04 0.04 0.02" />
            </geometry>
            <material name="orange" />
          </visual>
          </link>
       
          <link name="front_laser_link">
          <visual>
            <origin xyz="0 0 0" />
            <geometry>
              <cylinder length="0.05" radius="0.03" />
            </geometry>
            <material name="gray" />
          </visual>
          </link>
       
          <link name="back_laser_link">
          <visual>
            <origin xyz="0 0 0" />
            <geometry>
              <cylinder length="0.05" radius="0.03" />
            </geometry>
            <material name="gray" />
          </visual>
          </link>
       
          <link name="base_link" />
       
          <link name="camera_link">
              <visual>
                  <origin xyz="0 0 0" />
              <geometry>
                  <box size="0.04 0.04 0.02" />
              </geometry>
                  <material name="orange" />
              </visual>
          </link>
       
          <joint name="front_imu_link_joint" type="fixed">
              <parent link="base_link" />
              <child link="imu_link" />
              <origin rpy="0. 0. 0." xyz="0.0 0.0 0.2" />
          </joint>
       
          <joint name="front_laser_link_joint" type="fixed">
          <parent link="base_link" />
          <child link="front_laser_link" />
          <origin rpy="0. 0. 0." xyz="0.261 0.265 0.245" />
          </joint>
       
          <joint name="back_laser_link_joint" type="fixed">
          <parent link="base_link" />
          <child link="back_laser_link" />
          <origin rpy="0. 0. 3.141593" xyz="-0.260 -0.265 0.245" />
          </joint>
       
          <joint name="camera_link_joint" type="fixed">
          <parent link="base_link" />
          <child link="camera_link" />
          <origin rpy="0 0.0 0.0" xyz="0.3 0.0 0.2" />
          </joint>
       
      </robot>

3.3  xacro

启动launch

    1. <launch>
       
      	<arg name="project" default="lio_sam"/>
       
          <param name="robot_description" command="$(find xacro)/xacro $(find lio_sam)/launch/include/config/robot.urdf.xacro --inorder" />
       
          <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" respawn="true">
              <!-- <param name="tf_prefix" value="$(env ROS_HOSTNAME)"/> -->
          </node>
        
      </launch>

xacro


    1. <launch>
       
      	<arg name="project" default="lio_sam"/>
       
          <param name="robot_description" command="$(find xacro)/xacro $(find lio_sam)/launch/include/config/robot.urdf.xacro --inorder" />
       
          <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" respawn="true">
              <!-- <param name="tf_prefix" value="$(env ROS_HOSTNAME)"/> -->
          </node>
        
      </launch>