ROS 最佳实践[译]
敲代码前,调研现有 ROS 软件包:http://www.ros.org/browse/list.php
代码风格参考 ROS C++ Style Guide 和 Google style-guide
单位和坐标参考 Standard Units of Measure and Coordinate Conventions
坐标系规范
测试参考 http://wiki.ros.org/UnitTesting
ROS软件包组织
- 一个包不要太大,考虑分解为多个包
- 避免循环依赖
- 把具有相同依赖的程序考虑融合为一个包
- 把私有程序合并为一个包
- 有一个只包含消息、服务和动作的包,例如 ros/common_msgs
- 多个包组成 stack
ROS软件包命名参考 Naming ROS Resources 和 REP-144: ROS Package Naming
- 足够明确,例如
planner
wavefront_planner
包、节点、话题、服务、TF 等命名规范 参考 ROS Best Practices: Lorenz Mösenlechner, Technische Universität München, July 2012
- 包名称用小写字母,勿用
而用-
_
- 用下划线
_
分割 - 消息、服务和动作采用 camelCase 命名方式,例如
geometry_msgs/PoseStamped
- 动作名称中勿含
action
Messages, services and actions are named in camel case:
geometry_msgs/PoseStamped
? Names in a message/service/action definition are all lower case with underscores as separator:geometry_msgs/Pose end_effector
?
自定义消息和服务
- 尽可能用 ROS 提供的
- 不要为每个 topic/service/action 单独定义
msg
/srv
/action
- 参考
- std_msgs
- Bool
- Byte
- ByteMultiArray
- Char
- ColorRGBA
- Duration
- Empty
- Float32
- Float32MultiArray
- Float64
- Float64MultiArray
- Header
- Int16
- Int16MultiArray
- Int32
- Int32MultiArray
- Int64
- Int64MultiArray
- Int8
- Int8MultiArray
- MultiArrayDimension
- MultiArrayLayout
- String
- Time
- UInt16
- UInt16MultiArray
- UInt32
- UInt32MultiArray
- UInt64
- UInt64MultiArray
- UInt8
- UInt8MultiArray
- common_msgs
- actionlib_msgs
- diagnostic_msgs
- geometry_msgs
- nav_msgs
- 消息msg
- GridCells
- MapMetaData
- OccupancyGrid
- Odometry
- Path
- 服务srv
- GetMap
- GetPlan
- SetMap
- 动作action
- GetMap
- 消息msg
- sensor_msgs
- shape_msgs
- stereo_msgs
- trajectory_msgs
- visualization_msgs
- std_msgs
文档规范
- 每个包有 README.md/Wiki
- 说明节点的功能
- 说明提供和依赖的话题、服务和动作
- 说明参数及其默认值
- 提供
launch
文件 - 提供 rosinstall 文件?
- 模版 README.md
ROS包文件结构: 针对package_name(不含msg/srv/action)
package_name
|— config
|— robots
|— my_robot.yaml
|— sensors
|— velodyne.yaml
|— hokuyo_laser_range.yaml
|— include/package_name
|— Class1.hpp
|— Class2.hpp
|— launch
|— node1_name.launch
|— node2_name.launch
|— rviz
|— package_name.rviz
|— scripts
|— my_script.py
|— src
|— Class1.cpp
|— Class2.cpp
|— node1_name_node.cpp
|— node2_name_node.cpp
|— test
|— Class1Test.cpp
|— Class2Test.cpp
|— test_package_name.cpp
|— CMakeLists.txt
|— package.xml
针对 package_name_msgs
package_name_msgs
|— action
|— MyAction.action
|— msg
|— MyMessage.msg
|— srv
|— MyService.srv
|— CMakeLists.txt
|— package.xml
参考 Packages: Common_Files_and_Directories
Topics vs Services vs Actionlib vs Parameters vs Dynamic Parameters
- 参考 ROS Patterns - Communication
- 用话题发布连续的数据流,如传感器数据
- 用服务完成花费时间较短的计算
- 用动作完成花费时间较长的流程,比如抓取、导航……
- 用参数保存运行时不变的数值
- 用动态参数保存运行时可变的数值
发布空间/几何数据(TF),参考 http://wiki.ros.org/ROS/Patterns/Communication
节点具柄 (Node Handles)
- 公有节点具柄: nh_ = ros::NodeHandle();
- 私有节点具柄: nh_private_ = ros::NodeHandle(“~”);
- 命名节点具柄: nh_aslam_ = ros::NodeHandle(“aslam”);
- 全局节点具柄: nh_global_ = ros::NodeHandle(“/“);
- !!!不要使用全局命名
话题名称应简单清晰,参考:
参数名称采用层级结构,例如
camera/left/name: left_camera
camera/left/exposure: 1
camera/right/name: right_camera
camera/right/exposure: 1.1
而不是。YAML文件内容如下:camera_left_name: left_camera
camera:
left:
name: left_camera
exposure: 1
right:
name: right_camera
exposure: 1.1
参数组织方法
<launch>
<node pkg="my_package" type="my_node" name="my_name" output="screen">
<param name="my_parameter" value="10" />
</node>
</launch>
导入YAML文件:
<launch>
<node pkg="my_package" type="my_node" name="my_name" output="screen">
<rosparam command="load" file="$(find my_package)/config/robots/starleth.yaml" />
<rosparam command="load" file="$(find my_package)/config/sensors/default.yaml" />
</node>
</launch>
如何使用第三方库
- 尽可能使用 Debian 包
- 用
rosdep
解决依赖关系
launch文件
<include file=“$(find package_name)/launch/another.launch”/>
打印消息和日志
- 使用
ROS_INFO
,ROS_DEBUG
… - 使用合适的日志等级: Debug, info, warn, error, fatal.
检查订阅者数量节省计算资源
if (publisher.getNumSubscribers() < 1) return;
Bag数据包
rosbag record <topic> <topic> …
rosbag play foo.bag
rosbag play --clock foo.bag
rosparam set use_sim_time true
时间使用ROS提供的接口
ros::Time
ros::Duration
ros::Rate
消息格式转换 Eigen
Eigen::Vector3d my_super_cool_vector(1.0, 2.0, 3.0);
geometry_msgs::Point point_msg;
tf::pointEigenToMsg(my_super_cool_vector, point_msg);
super_cool_publisher_.publish(point_msg);
Eigen::Vector3d my_super_cool_vector(1.0, 2.0, 3.0);
tf::Vector3 my_super_cool_vector_tf;
tf::vectorEigenToTF(my_super_cool_vector, my_super_cool_vector_tf);
tf::Transform transform;
transform.setOrigin(my_super_cool_vector_tf);
transform_broadcaster_.sendTransform(
tf::StampedTransform(transform, ros::Time::now(), “map”, “world”));
catkin 编译参数
catkin config [list of your flags]
如
catkin config -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_ARG1=-std=c++11