----ROS Best Practices:https://github.com/ethz-asl/ros_best_practices/wiki----
这是使用机器人操作系统(ROS)的最佳实践、惯例和技巧的松散集合。它建立在官方ROS文档和其他资源上,并且作为总结和概括。
这是使用机器人操作系统(ROS)的最佳实践、惯例和技巧的松散集合。它建立在官方ROS文档和其他资源上,并且作为总结和概括。
This is a loose collection of best practices, conventions, and tricks for using the Robot Operating System (ROS). It builds up on the official ROS documentation and other resources and is meant as summary and overview.
官方ROS文档:
Official ROS documentation:
其他参考文献
Other References:
在部分内容中,该文件介绍了苏黎世国家自然科学院自动控制系统实验室的Legaged Robotics Group中建立的有意义的最佳实践。
作者:PéterFankhauser,pfankhauser@ethz.ch 所属机构:苏黎世自动控制系统实验室
In parts, the document describes opinionated best practices established within the Legged Robotics Group from the Autonomous Systems Lab, ETH Zurich.
Author: Péter Fankhauser, pfankhauser@ethz.ch Affiliation: Autonomous Systems Lab, ETH Zurich
研究已经开发出其他产品:http : //www.ros.org/browse/list.php
Research other products out there already: http://www.ros.org/browse/list.php
请参阅ROS C ++格式指南。在自动控制系统实验室,我们使用Google风格指南。
Refer to the ROS C++ Style Guide. At the Autonomous Systems Lab we use the Google style-guide.
参考标准测量单位和坐标公约。
Refer to Standard Units of Measure and Coordinate Conventions.
请参阅http://wiki.ros.org/UnitTesting。
Refer to http://wiki.ros.org/UnitTesting.
资料来源:
参考文献:
Sources:
References:
请参阅命名ROS资源和REP-144:ROS包命名(草案)。
Refer to Naming ROS Resources and REP-144: ROS Package Naming (draft).
选择名称:
Choose the name carefully:
改编自ROS最佳实践:LorenzMösenlechner,慕尼黑技术大学,2012年7月:
geometry_msgs/PoseStamped
。geometry_msgs/Pose end_effector
。Foo.action
不要FooAction.action
。Adapted from ROS Best Practices: Lorenz Mösenlechner, Technische Universität München, July 2012:
geometry_msgs/PoseStamped
.geometry_msgs/Pose end_effector
.Foo.action
, not FooAction.action
.尽可能使用标准数据类型(尽量防止.msg
扩散)!例如,EstimatorUpdateTime.msg
使用std_msgs/Time.msg
定义而不是创建自定义。另一个例子是一个空服务调用TriggerComputation.msg
,使用[ std_srvs/Empty.srv
](http://docs.ros.org/api/std_srvs/html/srv/Empty.html)。Use standard data types whenever possible (try to prevent .msg
proliferation)! For example, instead of creating a custom EstimatorUpdateTime.msg
, use the std_msgs/Time.msg
definition. Another example is an empty service call TriggerComputation.msg
, use [std_srvs/Empty.srv
] (http://docs.ros.org/api/std_srvs/html/srv/Empty.html) instead.
不要为每个主题/服务/动作定义新的msg / srv / action定义!例如,而不是创建两个定义LoadMapFromFile.srv
和SaveMapToFile.srv
相同的内容Do not define a new msg/srv/action definition for each topic/service/action! For example, instead of creating two definitions LoadMapFromFile.srv
and SaveMapToFile.srv
with the same content
定义一种类型的“ProcessFile.srv”,可从两种服务中使用,~/load_map
并~/save_map
分别。define one type ‘ProcessFile.srv’ which can be used from both services, ~/load_map
and ~/save_map
, respectively.
复合消息是通过组合(例如geometry_msgs/PoseWithCovarianceStamped
)构建的。
Complex messages are built through composition (e.g. geometry_msgs/PoseWithCovarianceStamped
).
尽量避免建立不完全填写的邮件。
Try to avoid building messages that tend to not get completely filled out.
参考文献:
README.md
提供了一个模板README.md
is provided here使用此文件/文件夹结构进行常规ROS包:
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
对于ROS消息和服务定义,使用:
package_name_msgs
|— action
|— MyAction.action
|— msg
|— MyMessage.msg
|— srv
|— MyService.srv
|— CMakeLists.txt
|— package.xml
参考文献:
Use this file/folder structure for a general ROS package:
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
For ROS message and service definitions use:
package_name_msgs
|— action
|— MyAction.action
|— msg
|— MyMessage.msg
|— srv
|— MyService.srv
|— CMakeLists.txt
|— package.xml
References:
参考ROS模式 - 通信。
概要:
dynamic_reconfigure
对运行时可能会发生变化的参数使用动态参数(dynamic_reconfigure)。 dynamic_reconfigureRefer to ROS Patterns - Communication.
Summary:
dynamic_reconfigure
) for parameter which are likely to change during run time.请参阅http://wiki.ros.org/ROS/Patterns/Communication。
有四种主要类型的节点句柄:
nh_ = ros::NodeHandle();
nh_private_ = ros::NodeHandle(“~”);
nh_aslam_ = ros::NodeHandle(“aslam”);
nh_global_ = ros::NodeHandle(“/“);
你可能不应该使用这个。)一般情况下,您只能使用前2个 - 您也可以使用命名空间节点句柄来分离出许多节点的发布者。
要解释这些操作以及如何使用它们,我们假设您的ROS节点ros_node
在命名空间中被命名blah
,并且您正在尝试查找该名称topic
。下面是他们将如何解决使用所有4个节点句柄:
/blah/topic
/blah/ros_node/topic
/blah/aslam/topic
/topic
如果你试图解决/topic
,这将跳过节点的命名空间并解决/topic
。
There are four main types of node handles:
nh_ = ros::NodeHandle();
nh_private_ = ros::NodeHandle(“~”);
nh_aslam_ = ros::NodeHandle(“aslam”);
nh_global_ = ros::NodeHandle(“/“);
(You probably shouldn’t use this ever.)Generally you will only use the first 2 -- you could also use the namespaced node handle for separating out publishers for nodes that have many.
To explain what these do and how they should be used, let’s assume your ROS node is named ros_node
, in namespace blah
, and you are trying to look up the name topic
. Here is what they will resolve to using all 4 node handles:
/blah/topic
/blah/ros_node/topic
/blah/aslam/topic
/topic
If, instead, your try to resolve /topic
, this will skip the namespace of the node and resolve to /topic
.
这些只是一般的指导原则,但是如有可能,在每种情况下都喜欢使用以下内容:
/odom
主题)。不要使用全局名称。这是因为当您将节点推送到命名空间中时,它们无法正确解析,并且不允许您一次正常运行多个节点。或者在同一个主机上使用多个机器人。定义相对于节点命名空间的已发布主题和参数:
推荐:odometry
,grid_map
,cam0/camera_info
避免:/odometry
,/grid_map
,/helicopter/cam0/camera_info
These are just general guidelines, but when possible, prefer to use the following in each case:
/odom
topic).Never use global names. This is because they do not resolve properly when you push nodes into namespaces, and does not allow you to run more than one of your node at a time properly. Or use multiple robots on the same master. Define published topics and parameters relative to the nodes namespace:
Good: odometry
, grid_map
, cam0/camera_info
Bad: /odometry
, /grid_map
, /helicopter/cam0/camera_info
主题应该在节点的上下文中命名。非常简单明了的名称是易于理解的“ROS API”的首选。主题名称只要在节点的命名空间中发布,就不会引起冲突(请参阅名称空间中的主题和参数)。
为了告诉另一个节点在哪里订阅,请将主题名称设置为ROS参数(首选)。或者,对于第三方节点,您可以使用remap
roslaunch中的标记。
参考文献:
Topics should be named in the context of the node. Very simple and clear names are preferred for a easy to understand “ROS API”. Topic names not cause collision as long as they are published within the namespace of the node (see Namespace for Topics and Parameters).
In order to tell another node where to subscribe, set the topic name as ROS parameter (preferred). Alternatively, for third-party nodes, you can use the remap
tag in roslaunch.
References:
对于参数使用分层方案,例如
camera/left/name: left_camera
camera/left/exposure: 1
camera/right/name: right_camera
camera/right/exposure: 1.1
替换(避免使用如下方案)
camera_left_name: left_camera
这样可以保护参数名称免受冲突,并允许参数单独访问或作为树。在YAML文件中,结构将是
camera:
left:
name: left_camera
exposure: 1
right:
name: right_camera
exposure: 1.1
参考文献:
Use a hierarchical scheme for parameters, such as
camera/left/name: left_camera
camera/left/exposure: 1
camera/right/name: right_camera
camera/right/exposure: 1.1
instead of
camera_left_name: left_camera
etc. This protects parameter names from colliding and allows parameters to be access individually or as a tree. In a YAML-file, the structure would be
camera:
left:
name: left_camera
exposure: 1
right:
name: right_camera
exposure: 1.1
References:
如果您的节点只有一个或两个参数,您可以使用<param>
标记将它们设置在启动文件中:
<launch>
<node pkg="my_package" type="my_node" name="my_name" output="screen">
<param name="my_parameter" value="10" />
</node>
</launch>
一般(首选),组织YAML文件中的参数并通过rosparam
-tag 加载它们:
<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>
不要使用命令行参数,而是使用ROS参数服务器。对于可能在运行时更改的参数,请使用dynamic_reconfigure。
参考文献:
If your node has only one or two parameters, you can set them in a launch file with the <param>
tag:
<launch>
<node pkg="my_package" type="my_node" name="my_name" output="screen">
<param name="my_parameter" value="10" />
</node>
</launch>
In general (preferred), organize the parameters in YAML-files and load them via the rosparam
-tag:
<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>
Do not use command line parameters but the ROS parameter server. For parameters that are likely to change at runtime, use dynamic_reconfigure.
References:
鼓励没有ROS依赖关系的独立库。不要将ROS依赖关系放在算法的核心中!
如果您可以开发一个ROS独立的库并且释放一个并行的ROS功能包
http://courses.csail.mit.edu/6.141/spring2012/pub/lectures/Lec06-ROS.pdf
请参阅使用第三方库。
Encourages standalone libraries with no ROS dependencies. Don’t put ROS dependencies in the core of your algorithm!
If you can develop a ROS independent library and release a parallel ROS wrapper
http://courses.csail.mit.edu/6.141/spring2012/pub/lectures/Lec06-ROS.pdf
Refer to Using Third-Party Libraries.
不要使用cmake手动打包。
Never call cmake by hand in a package.
保持您的依赖性:
如果需要多次运行catkin_make
来构建工作空间,那么有些不合适的!
Keep your dependencies clean:
If multiple runs of catkin_make
are required for your workspace to be built, something is fishy!
不需要节点的特定启动顺序。使用waitForService
,waitForTransform
,waitForServer
,...
Do not require a specific startup order for nodes. Use waitForService
, waitForTransform
, waitForServer
, …
参考Roslaunch的大型项目提示。Refer to Roslaunch tips for large projects.
<include file=“$(find package_name)/launch/another.launch”/>
ROS_INFO
,ROS_DEBUG
,...)。ROS_INFO
,ROS_DEBUG
, …).待补充。
为了避免没有节点订阅的主题的计算开销,请检查订阅者的数量
To avoid computational overhead for topics which no nodes are subscribed to, check the number of subscribers with
if (publisher.getNumSubscribers() < 1) return;
记录Bag:Recording of a bag:
播放一个Bag:Play a bag:
使用记录时间播放一个Bag(记录打印数据和TF时重要):Play a bag using recorded time (important when stamped data and TF was recorded):
注意:在/use_sim_time
初始化节点之前,该参数必须设置为true。Note: The /use_sim_time parameter must be set to true before the node is initialized.
参考文献:References:
使用ros::Time
,ros::Duration
和ros::Rate
替代系统时间。
Use ros::Time, ros::Duration, and ros::Rate instead of system time.
要转换消息,请使用eigen_conversions(或kindr-或minkindr-conversions)。
例:
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);
TF,使用tf_conversions(或还有kindr-或minkindr转换)。
例:
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”));
参考文献:
To convert to/from messages, use eigen_conversions (or kindr- or minkindr-conversions).
Example:
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);
To go to/from TF, use tf_conversions (or also kindr- or minkindr-conversions).
Example:
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”));
References:
使用cv_bridge。这允许从/到ROS消息转换非常简单。
例:
const stereo_msgs::DisparityImageConstPtr& msg; // We got this from a subscription callback.
cv::Mat output_image;
cv_bridge::CvImageConstPtr cv_img_ptr = cv_bridge::toCvShare(msg->image, msg);
// This is a shallow copy.
output_image = cv_img_ptr->image;
cv_bridge::CvImage image_cv_bridge;
image_cv_bridge.header.frame_id = “map”;
image_cv_bridge.image = output_image;
publisher_.publish(image_cv_bridge.toImageMsg());
参考文献:
Use the cv_bridge. This allows very easy conversions to/from ROS messages.
Example:
const stereo_msgs::DisparityImageConstPtr& msg; // We got this from a subscription callback.
cv::Mat output_image;
cv_bridge::CvImageConstPtr cv_img_ptr = cv_bridge::toCvShare(msg->image, msg);
// This is a shallow copy.
output_image = cv_img_ptr->image;
cv_bridge::CvImage image_cv_bridge;
image_cv_bridge.header.frame_id = “map”;
image_cv_bridge.image = output_image;
publisher_.publish(image_cv_bridge.toImageMsg());
References:
这些是用于catkin的一些有用的CMake标志。要将它们与catkin_tools一起使用,请将它们作为参数添加
catkin config [list of your flags]
所以例如
catkin config -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
有用的catkin编译标志:
在C ++发行模式下编译
使用C ++ 11 编译
编译Eclipse项目
使用C ++ 11索引编译Eclipse项目
These are some useful CMake flags for catkin. To use them with catkin_tools, add them as arguments with
catkin config [list of your flags]
So for example
catkin config -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
Useful catkin build flags:
Build in C++ release mode
Build with C++11
Build Eclipse projects
Build Eclipse projects with C++11 indexing
----
For me, I guess the best is to have some kind of handler, which handles the communication between sensors and actuators and have one node for each element of the robot (like in figure 2), because the system is in this way loosely coupled and can be extended easily, but I want to know what your opinion is.