目录
1、ros-综述
1.1. ros node
1.2 ros spin/spinOnce
1.3 ros callback
1.4 topic /server
1.5 rosparam
2、ros 小知识
2.1. 使用rosed编辑ROS中的文件
2.2. 理解 ROS节点
2.3.理解ROS服务和参数
2.4.录制与回放数据
2.5 工具
2.6 ROS 分布部署环境变量的配置
3. ros 技巧
3.1 rostopic pub array
3.2 urdf
3.3 xacro
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)。只能监听反馈,不能循环。所以当你需要监听一下的时候,就调用一下这个函数。
小结
- 在使用ros::spin()的情况下,一般来说在初始化时已经设置好所有消息的回调,并且不需要其他背景程序运行。这样一来,每次消息到达时会执行用户的回调函数进行操作,相当于程序是消息事件驱动的;
- 而在使用ros::spinOnce()的情况下,一般来说仅仅使用回调不足以完成任务,还需要其他辅助程序的执行:比如定时任务、数据处理、用户界面等。
1.3 ros callback
- 关于消息接收回调机制在ROS官网上略有说明 (callbacks and spinning)。总体来说其原理是这样的:除了用户的主程序以外,ROS的socket连接控制进程会在后台接收订阅的消息,所有接收到的消息并不是立即处理,而是等到spin()或者spinOnce()执行时才集中处理。所以为了保证消息可以正常接收,需要尤其注意spinOnce()函数的使用 (对于spin()来说则不涉及太多的人为因素)。
- 对于速度较快的消息,需要注意合理控制消息队列及spinOnce()的时间。例如,如果消息到达的频率是100Hz,而spinOnce()的执行频率是10Hz,那么就要至少保证消息队列中预留的大小大于10。
- 如果对于用户自己的周期性任务,最好和spinOnce()并列调用。即使该任务是周期性的对于数据进行处理,例如对接收到的IMU数据进行Kalman滤波,也不建议直接放在回调函数中:因为存在通信接收的不确定性,不能保证该回调执行在时间上的稳定性。
- 注意:回调函数的排队和轮转,不会对内部的网路通信造成影响,它们仅仅会影响到用户的回调函数何时发生。它们会影响到订阅者队列。因为处理你回调函数的速度,你消息到来的速度,将会决定以前的消息会不会被丢弃。
单线程下的轮转
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核心使用一个线程。
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使用如下:
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.命令行工具
- Complete list: ROS command line tools.
- ROS Cheatsheet
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 | rospack, roscd |
Stacks | rosstack, roscd |
2.6 ROS 分布部署环境变量的配置
首先可看下关于本机的ROS环境变量配置
- $ printenv | grep ROS
//显示类似如下
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的环境变量了。
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
rostopic pub /client_array std_msgs/UInt32MultiArray "layout:
dim:
- label: ''
size: 0
stride: 0
data_offset: 0
data: [500,500,200]"
3.2 urdf
启动
<?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
<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
<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
<?xml version="1.0"?>
<robot name="lio" xmlns:xacro="http://tixiaoshan.github.io/">
<xacro:property name="PI" value="3.1415926535897931" />
<link name="chassis_link"></link>
<link name="base_link"></link>
<joint name="base_link_joint" type="fixed">
<parent link="base_link"/>
<child link="chassis_link" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
<link name="imu_link"> </link>
<joint name="imu_joint" type="fixed">
<parent link="chassis_link" />
<child link="imu_link" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
<link name="velodyne"> </link>
<joint name="velodyne_joint" type="fixed">
<parent link="chassis_link" />
<child link="velodyne" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
<link name="navsat_link"> </link>
<joint name="navsat_joint" type="fixed">
<parent link="chassis_link" />
<child link="navsat_link" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
</robot>