ROS机器人操作系统最佳实践指南



ROS机器人操作系统最佳实践指南

----ROS Best Practices:https://github.com/ethz-asl/ros_best_practices/wiki----

这是使用机器人操作系统(ROS)的最佳实践、惯例和技巧的松散集合。它建立在官方ROS文档和其他资源上,并且作为总结和概括。


ROS最佳实践指南ROS Best Practices

这是使用机器人操作系统(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éterFankhauserpfankhauser@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

必做要求Todo

在你开始之前(预备基础--查阅功能包清单)Before You Begin

研究已经开发出其他产品:http : //www.ros.org/browse/list.php

Research other products out there already: http://www.ros.org/browse/list.php

编程指南Coding Guidelines

请参阅ROS C ++格式指南。在自动控制系统实验室,我们使用Google风格指南

Refer to the ROS C++ Style Guide. At the Autonomous Systems Lab we use the Google style-guide.

单位和规范公约Units and Coordinate Conventions

参考标准测量单位和坐标公约

Refer to Standard Units of Measure and Coordinate Conventions.

坐标框架Coordinate Frames

测试Testing

请参阅http://wiki.ros.org/UnitTesting

Refer to http://wiki.ros.org/UnitTesting.

功能包组织Package Organization

  • ROS包的开销不大。当需要只用时,定义单独的包。通常,代码可以在除了编译它们之外的上下文中使用。
  • 避免组合引入相互不必要的依赖关系的节点,并且经常单独使用(以消除不必要的编译开销)。
  • 包依赖图必须是非循环的,即没有包可能依赖于直接或间接依赖于另一个的包。
  • 如果具有相似依赖关系的程序通常一起使用,请考虑将它们组合成一个包。
  • 如果某些节点对共享代码具有公共依赖性,您不希望公开导出,那么它们可以在一个包内部组合。
  • 创建单独的包,仅包含消息,服务和操作(分离接口和实现)。单独消息包的示例是ros / common_msgs包。
  • 堆栈中的组包。

  • The overhead of a ROS package is not large. Define separate packages wherever they make sense. Often, code can be useful in contexts other than those for which it was built.
  • Avoid combining nodes that pull in mutually unneeded dependencies and are often used separately (to eliminate unnecessary build overhead).
  • The package dependency graph must be acyclic, i.e. no package may depend on another that directly or indirectly depends on it.
  • If programs with similar dependencies are generally used together, consider combining them into a single package.
  • If some nodes have common dependencies on shared code that you do not wish to export publicly, they can be combined internally within a single package.
  • Create separate packages that contain only messages, services and actions (separation of interface and implementation). Examples for separate message packages are the ros/common_msgs packages.
  • Group packages in stacks.

资料来源:

参考文献:

Sources:

References:

功能包命名Package Naming

请参阅命名ROS资源REP-144:ROS包命名(草案)。

Refer to Naming ROS Resources and REP-144: ROS Package Naming (draft).

选择名称:

  • 他们后来会变得很乱。
  • 软件包名称对于整个ROS生态系统是全球性的。
  • 尝试选择对可能希望使用您的代码的其他人有意义的名称。
  • 软件包名称应具体到足以识别软件包的功能。不要超过范围,例如,计划器是一个不好的名称,请改用wavefront_planner。
  • 不要使用“utils”或其他产品。
  • 前缀包名建议当并不意味着包装的情况下更广泛地使用(例如,包是特定于StlarETH机器人使用“starleth_”前缀)。

Choose the name carefully:

  • They are messy to change later.
  • Package names are global to the entire ROS ecosystem.
  • Try to pick names that will make sense to others who may wish to use your code.
  • Package names should be specific enough to identify what the package does. Do not over scope, e.g. planner is a bad name, use wavefront_planner instead.
  • Do not use “utils” or other catchalls.
  • Prefixing a package name is recommended only when the package is not meant to be used more widely (e.g., packages that are specific to the StlarETH robot use the ‘starleth_’ prefix).

功能包、节点、主题、服务、TF等的命名约定

Naming Conventions for Packages, Nodes, Topics, Services, TF etc.

改编自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:

  • Package names are lower case.
  • Packages must not contain dashes (“-”), only underscores (“_”).
  • Nodes, topics, services, actions, parameters are all lower case with underscores as separator.
  • 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.
  • Do not use the word “action” in an action definition: Foo.action, not FooAction.action.

自定义ROS消息和服务Custom ROS Message and Services

  • 尽可能使用标准数据类型(尽量防止.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.srvSaveMapToFile.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

      string file_path
      —
    

    定义一种类型的“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.

参考文献:

文档Documentation

  • 为每个软件包创建一个简短的README.md/Wiki:
    • 记录节点的作用,
    • 记录所需和提供的主题,服务和行动,
    • 文档ROS参数及其默认值,这里README.md提供一个模板
  • 提供启动文件,
  • 提供一个rosinstall文件。

  • Create a short README.md/Wiki for each package:
    • Document what the node does,
    • Document topics, services and actions that are required and provided,
    • Document ROS parameters and their default values, A template for the README.md is provided here
  • Provide launch files,
  • Provide a rosinstall file.

软件包的文件/文件夹结构File/Folder Structure for Packages

使用此文件/文件夹结构进行常规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:

主题vs服务vs Actionlib vs参数vs动态参数

Topics vs Services vs Actionlib vs Parameters vs Dynamic Parameters

参考ROS模式 - 通信

概要:

  • 使用主题发布连续数据流,例如传感器数据,连续检测结果...  topic
  • 仅使用服务进行短期计算。  service
  • 对所有更长的运行过程使用操作,例如抓握,导航,感知,...  action
  • 对启动时已知的值使用参数,并且在运行时不会更改。  parameters
  • dynamic_reconfigure对运行时可能会发生变化的参数使用动态参数(dynamic_reconfigure)。  dynamic_reconfigure

Refer to ROS Patterns - Communication.

Summary:

  • Use topics for publishing continuous streams of data, e.g. sensor data, continuous detection results, …
  • Use services only for short calculations.
  • Use actions for all longer running processes, e.g. grasping, 
navigation, perception, …
  • Use parameters for values which are known at launch and are not likely to change during run time.
  • Use dynamic parameters (dynamic_reconfigure) for parameter which are likely to change during run time.

出版空间/几何数据Publishing Spatial / Geometric Data

请参阅http://wiki.ros.org/ROS/Patterns/Communication

节点句柄Node Handles

有四种主要类型的节点句柄:

  1. 默认(public)节点句柄: nh_ = ros::NodeHandle();
  2. 私有节点句柄: nh_private_ = ros::NodeHandle(“~”);
  3. 命名空间节点句柄: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. 全局节点句柄:( nh_global_ = ros::NodeHandle(“/“);你可能不应该使用这个。)

一般情况下,您只能使用前2个 - 您也可以使用命名空间节点句柄来分离出许多节点的发布者。

要解释这些操作以及如何使用它们,我们假设您的ROS节点ros_node在命名空间中被命名blah,并且您正在尝试查找该名称topic。下面是他们将如何解决使用所有4个节点句柄:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

如果你试图解决/topic,这将跳过节点的命名空间并解决/topic

There are four main types of node handles:

  1. Default (public) node handle: nh_ = ros::NodeHandle();
  2. Private node handle: nh_private_ = ros::NodeHandle(“~”);
  3. Namespaced node handle: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. Global node handle: 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:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

If, instead, your try to resolve /topic, this will skip the namespace of the node and resolve to /topic.

何时使用哪个节点句柄When to Use Which Node Handle

这些只是一般的指导原则,但是如有可能,在每种情况下都喜欢使用以下内容:

  • 订阅者 - 通常是公共节点句柄。
  • 发布者 - 通常是用于大多数输出​​/可视化的私有节点句柄,有时候需要使用public来进行全局使用的数据(即/odom主题)。
  • 参数 - 几乎总是私有节点句柄。

不要使用全局名称。这是因为当您将节点推送到命名空间中时,它们无法正确解析,并且不允许您一次正常运行多个节点。或者在同一个主机上使用多个机器人。定义相对于节点命名空间的已发布主题和参数:

推荐odometrygrid_mapcam0/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:

  • Subscribers - usually public node handles.
  • Publishers - usually private node handles for most output/visualization, occasionally necessary to use public for globally-used data (i.e., /odom topic).
  • Parameters - almost always private node handle.

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:

Goododometrygrid_mapcam0/camera_info
Bad/odometry/grid_map/helicopter/cam0/camera_info

主题命名Topic Naming

主题应该在节点的上下文中命名。非常简单明了的名称是易于理解的“ROS API”的首选。主题名称只要在节点的命名空间中发布,就不会引起冲突(请参阅名称空间中的主题和参数)。

为了告诉另一个节点在哪里订阅,请将主题名称设置为ROS参数(首选)。或者,对于第三方节点,您可以使用remaproslaunch中的标记。

参考文献:

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:

参数命名Parameter Naming

对于参数使用分层方案,例如

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

参考文献:

Parameter Naming

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:

参数组织Parameter Organisation

如果您的节点只有一个或两个参数,您可以使用<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:

使用第三方库Using Third-Party Libraries

鼓励没有ROS依赖关系的独立库。不要将ROS依赖关系放在算法的核心中!

如果您可以开发一个ROS独立的库并且释放一个并行的ROS功能包

http://courses.csail.mit.edu/6.141/spring2012/pub/lectures/Lec06-ROS.pdf

请参阅使用第三方库

  • 如果可能,尝试使用Debian软件包中的库。
  • 指定rosdep依赖关系(用于安装系统包的工具)。
  • 如果您需要从源代码编译库,则可以创建一个下载和编译包的ROS包装包。
  • 不要在包装包中使用sudo。
  • 不需要手动系统的安装。
  • 不要将库复制到需要它们的包中。

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.

  • If possible, try to use libraries from Debian packages.
  • Specify rosdep dependencies (tool for installing system packages).
  • If you need to compile a library from source create a ROS wrapper package that downloads and compiles the package.
  • Don’t use sudo in wrapper packages.
  • Don’t require manual system wide installations.
  • Don’t copy libraries into packages that need them.

编译Building

不要使用cmake手动打包。

Never call cmake by hand in a package.

依赖Dependencies

保持您的依赖性:

  • 只依靠你所需要的,
  • 指定所有依赖关系,
  • 不要使用隐式依赖关系。

如果需要多次运行catkin_make来构建工作空间,那么有些不合适的!

Keep your dependencies clean:

  • Only depend on what you need,
  • Specify all dependencies,
  • Do not use implicit dependencies.

If multiple runs of catkin_make are required for your workspace to be built, something is fishy!

启动顺序Startup Order

不需要节点的特定启动顺序。使用waitForServicewaitForTransformwaitForServer,...

Do not require a specific startup order for nodes. Use waitForServicewaitForTransformwaitForServer, …

Roslaunch组织Roslaunch Organization

参考Roslaunch的大型项目提示。Refer to Roslaunch tips for large projects.

<include file=“$(find package_name)/launch/another.launch”/>

打印消息/记录Printing Messages/Logging

  • 使用rosconsole实用程序日志(ROS_INFOROS_DEBUG,...)。
  • 使用适当的控制台日志:调试,信息,警告,错误,致命。
  • 提供内省/调试主题。

  • Use rosconsole utilities for logging(ROS_INFO,ROS_DEBUG, …).
  • Use appropriate console logging: Debug, info, warn, error, fatal.
  • Provide introspection/debug topics.

调试Debugging

待补充。

检查订阅者数量Checking the Number of Subscribers

为了避免没有节点订阅的主题的计算开销,请检查订阅者的数量

To avoid computational overhead for topics which no nodes are subscribed to, check the number of subscribers with

if (publisher.getNumSubscribers() < 1) return;

ROS消息记录文件ROS Bag Files

  • 记录Bag:Recording of a bag:

      rosbag record <topic> <topic> … 
    
  • 播放一个Bag:Play a bag:

      rosbag play foo.bag 
    
  • 使用记录时间播放一个Bag(记录打印数据和TF时重要):Play a bag using recorded time (important when stamped data and TF was recorded):

      rosbag play --clock foo.bag
    

    注意:在/use_sim_time初始化节点之前,该参数必须设置为true。Note: The /use_sim_time parameter must be set to true before the node is initialized.

      rosparam set use_sim_time true
    

参考文献:References:

时间Time

使用ros::Timeros::Durationros::Rate替代系统时间。

Use ros::Time, ros::Duration, and ros::Rate instead of 
system time.

在ROS消息和其他类型之间进行转换Converting Between ROS Messages and Other Types

Eigen

要转换消息,请使用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”));

参考文献:

Eigen

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:

OpenCV图像OpenCV Image

使用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编译标志Catkin Build Flags

这些是用于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 ++发行模式下编译

      -DCMAKE_BUILD_TYPE=Release
    
  • 使用C ++ 11 编译

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • 编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • 使用C ++ 11索引编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L

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

      -DCMAKE_BUILD_TYPE=Release
    
  • Build with C++11

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • Build Eclipse projects

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • Build Eclipse projects with C++11 indexing

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L


----

I'm going to build a small robot system, and it seems like that ROS serves a nice framework to control and program the system.

However, I am wondering which is the best practice to manage the components of my robot.

  • Does it make sense to put all the sensors in one node?

  • Should I only put the sensors of the same type in one node or is it better to have one node for one sensor?

  • Is it a good practice to have some kind of handler node, which takes input from sensors and steers the corresponding actuators or should the actuator nodes and sensor nodes communicate directly?


  1. Fused sensor nodes and actuator nodes with handler 1. Fused sensor nodes and actuator nodes with handler

  2. Single sensor and actuator nodes with handler enter image description here

  3. Direct communication enter image description here

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.

share improve this question
 

1 Answer

up vote 8 down vote accepted

Very short answer: 2


Sensors

Regarding whether reading from sensors all in one node or each separately, you should ask yourself this question:

Are the sensors meaningless without the other?

This question asks if the sensors are tightly coupled or not. For example, say you have a sensor that is sensitive to temperature (and you need to compensate for it). You add a temperature sensor primarily to fix the value of the other sensor. In this scenario, it makes sense to read both values at the same time, since they are tightly coupled. In fact, without the readings from the temperature sensor, the readings from the original sensor is useless.

On the other hand, if the sensors are individually useful, by all means keep them in separate nodes. This has many benefits:

  • The nodes can be run on separate processors
  • The nodes can be reused in future robots
  • Failure in communication with one node doesn't bring the whole system down
  • Restart of acquisition from a faulty sensor board can be done separately from the others

In fact, if you need any of the above benefits, you would have to go with separate nodes, even if the sensors are tightly coupled, but that usually doesn't happen.

Actuators

This is a analogous.

Are the actuators meaningless without the other?

For example, if you are designing a wrist with robotic tendons where for each tendon (for whatever reason) two motors are responsible to simultaneously work to move a joint in one or the other direction, then having them served in the same node makes much more sense than separate.

On the other hand, where actuators are independent (common case), it makes sense to have one node for each actuator. In that case, each could be put in a different node. Besides the exact same benefits as with sensors, there is this added benefit:

  • If an actuators is stalled (for whatever reason), the other actuators still function. If there is redundant degrees of freedom, they could even completely compensate for it.

This has one implication. If you need the actuators to work in harmony, then put them in the same node. This is not just because of failure in communication, but because different nodes means different delays; on a distributed system each node is on a different part of the network and hence the difference in delays, on a centralized system different delays happen on high CPU loads due to each process's luck in scheduling.

Should There Be a Handler?

Even though the answer is "it depends", there is a common approach with many advantages. Let's change the name and call it "controller". The approach is "yes, there should be a controller".

The advantages of having a controller are (among many):

  • Decoupled processing: each node is responsible for one thing which means:
    • Simplicity: which implies
      • Easier development
      • Easier debugging
      • Fewer errors
      • Less chance of failure
    • Reusability: because the same controller can be used with different sensor nodes if they have the same functionality (i.e. message and service formats).
  • Execution on separate hardware: each node can be moved in the network. For example, sensor and actuator nodes may be moved to a dedicated microcontroller (Arduino for example (not that I recommend)) and the controller on a PC.
  • Avoid extreme ugliness: if the sensors wanted to directly influence the actuators, the result is simply a mess. Assuming no controller, let's look at each case:
    • One sensor node: basically this means the sensor node and the controller are put together in the same node. Not too bad, but very unnecessary.
    • Many sensor nodes: this is the mess. This means the controller is distributed among the sensor nodes. Therefore all the sensor nodes have to talk with each other for each to know how to control its associated actuator(s). Imagine a failure in communication or various kinds of delays and you'll see how difficult it becomes. Given that this is utterly unnecessary, there is no reason for doing it!

These said, there are disadvantages too. Having more nodes (any nodes, not just the controller) means:

  • More wasted communication: the data have to move around in standard formats (so serialized and deserialized) through network or shared memory, ROS core has to look at them and decide who to deliver them to, etc. In short, some system resources are wasted in communication. If all nodes where in one, that cost could have been zero.
  • Higher chance of failure: if for whatever reason a network link goes down, or a node dies, there is a failure in the system. If you are not prepared for it, it can take down the whole system. Now this is actually a good thing in general to be able to lose part of the system but not all of it (graceful degradation), but there also exist applications where this should be avoided as much as possible. Cutting the communication and putting all code in one node actually helps with system stability. The down side is of course, the system either works fine or suddenly dies completely.
  • Chaotic timings: each node runs on its own. The time it takes for its messages to arrive at others is non-deterministic and varies run by run. Unless your nodes timestamp each message (as a side note: you need to have synchronized clocks to a good degree, which ROS doesn't) and unless each receiving node can take the delay into account and control accordingly (which is a very difficult task on its own) then having multiple nodes means high uncertainty about the age of the data. This is actually one of the reasons (among many) that most robots move so slow; their control loop has to be slow enough to make sure all data correspond to the current period. The larger the delays, the slower the control loop.

In all above disadvantages, the solution is to reduce the number of nodes, preferably to a single node. Wait a minute, that's not using ROS anymore! Exactly.

To summarize:

  • Use ROS for non-realtime systems where delays could sporadically get high. In that case, feel free to have as many ROS nodes as you wish. In fact, it's very good practice to have each ROS node do one and only one thing. That way, they become very simple, and they become highly reusable.
  • On the other hand, for realtime systems, by all means avoid ROS. For that there is orocos and technologies like EtherCAT and more often than not, ad-hoc solutions.

As a final word, in practice ROS does fine. Not great, but fine. Very often the system is not critical and the chance of failure is so small that every now and then a restart is not a big deal. This is the Ostrich algorithm!

ROS机器人操作系统最佳实践指南

----ROS Best Practices:https://github.com/ethz-asl/ros_best_practices/wiki----

这是使用机器人操作系统(ROS)的最佳实践、惯例和技巧的松散集合。它建立在官方ROS文档和其他资源上,并且作为总结和概括。


ROS最佳实践指南ROS Best Practices

这是使用机器人操作系统(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éterFankhauserpfankhauser@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

必做要求Todo

在你开始之前(预备基础--查阅功能包清单)Before You Begin

研究已经开发出其他产品:http : //www.ros.org/browse/list.php

Research other products out there already: http://www.ros.org/browse/list.php

编程指南Coding Guidelines

请参阅ROS C ++格式指南。在自动控制系统实验室,我们使用Google风格指南

Refer to the ROS C++ Style Guide. At the Autonomous Systems Lab we use the Google style-guide.

单位和规范公约Units and Coordinate Conventions

参考标准测量单位和坐标公约

Refer to Standard Units of Measure and Coordinate Conventions.

坐标框架Coordinate Frames

测试Testing

请参阅http://wiki.ros.org/UnitTesting

Refer to http://wiki.ros.org/UnitTesting.

功能包组织Package Organization

  • ROS包的开销不大。当需要只用时,定义单独的包。通常,代码可以在除了编译它们之外的上下文中使用。
  • 避免组合引入相互不必要的依赖关系的节点,并且经常单独使用(以消除不必要的编译开销)。
  • 包依赖图必须是非循环的,即没有包可能依赖于直接或间接依赖于另一个的包。
  • 如果具有相似依赖关系的程序通常一起使用,请考虑将它们组合成一个包。
  • 如果某些节点对共享代码具有公共依赖性,您不希望公开导出,那么它们可以在一个包内部组合。
  • 创建单独的包,仅包含消息,服务和操作(分离接口和实现)。单独消息包的示例是ros / common_msgs包。
  • 堆栈中的组包。

  • The overhead of a ROS package is not large. Define separate packages wherever they make sense. Often, code can be useful in contexts other than those for which it was built.
  • Avoid combining nodes that pull in mutually unneeded dependencies and are often used separately (to eliminate unnecessary build overhead).
  • The package dependency graph must be acyclic, i.e. no package may depend on another that directly or indirectly depends on it.
  • If programs with similar dependencies are generally used together, consider combining them into a single package.
  • If some nodes have common dependencies on shared code that you do not wish to export publicly, they can be combined internally within a single package.
  • Create separate packages that contain only messages, services and actions (separation of interface and implementation). Examples for separate message packages are the ros/common_msgs packages.
  • Group packages in stacks.

资料来源:

参考文献:

Sources:

References:

功能包命名Package Naming

请参阅命名ROS资源REP-144:ROS包命名(草案)。

Refer to Naming ROS Resources and REP-144: ROS Package Naming (draft).

选择名称:

  • 他们后来会变得很乱。
  • 软件包名称对于整个ROS生态系统是全球性的。
  • 尝试选择对可能希望使用您的代码的其他人有意义的名称。
  • 软件包名称应具体到足以识别软件包的功能。不要超过范围,例如,计划器是一个不好的名称,请改用wavefront_planner。
  • 不要使用“utils”或其他产品。
  • 前缀包名建议当并不意味着包装的情况下更广泛地使用(例如,包是特定于StlarETH机器人使用“starleth_”前缀)。

Choose the name carefully:

  • They are messy to change later.
  • Package names are global to the entire ROS ecosystem.
  • Try to pick names that will make sense to others who may wish to use your code.
  • Package names should be specific enough to identify what the package does. Do not over scope, e.g. planner is a bad name, use wavefront_planner instead.
  • Do not use “utils” or other catchalls.
  • Prefixing a package name is recommended only when the package is not meant to be used more widely (e.g., packages that are specific to the StlarETH robot use the ‘starleth_’ prefix).

功能包、节点、主题、服务、TF等的命名约定

Naming Conventions for Packages, Nodes, Topics, Services, TF etc.

改编自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:

  • Package names are lower case.
  • Packages must not contain dashes (“-”), only underscores (“_”).
  • Nodes, topics, services, actions, parameters are all lower case with underscores as separator.
  • 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.
  • Do not use the word “action” in an action definition: Foo.action, not FooAction.action.

自定义ROS消息和服务Custom ROS Message and Services

  • 尽可能使用标准数据类型(尽量防止.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.srvSaveMapToFile.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

      string file_path
      —
    

    定义一种类型的“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.

参考文献:

文档Documentation

  • 为每个软件包创建一个简短的README.md/Wiki:
    • 记录节点的作用,
    • 记录所需和提供的主题,服务和行动,
    • 文档ROS参数及其默认值,这里README.md提供一个模板
  • 提供启动文件,
  • 提供一个rosinstall文件。

  • Create a short README.md/Wiki for each package:
    • Document what the node does,
    • Document topics, services and actions that are required and provided,
    • Document ROS parameters and their default values, A template for the README.md is provided here
  • Provide launch files,
  • Provide a rosinstall file.

软件包的文件/文件夹结构File/Folder Structure for Packages

使用此文件/文件夹结构进行常规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:

主题vs服务vs Actionlib vs参数vs动态参数

Topics vs Services vs Actionlib vs Parameters vs Dynamic Parameters

参考ROS模式 - 通信

概要:

  • 使用主题发布连续数据流,例如传感器数据,连续检测结果...  topic
  • 仅使用服务进行短期计算。  service
  • 对所有更长的运行过程使用操作,例如抓握,导航,感知,...  action
  • 对启动时已知的值使用参数,并且在运行时不会更改。  parameters
  • dynamic_reconfigure对运行时可能会发生变化的参数使用动态参数(dynamic_reconfigure)。  dynamic_reconfigure

Refer to ROS Patterns - Communication.

Summary:

  • Use topics for publishing continuous streams of data, e.g. sensor data, continuous detection results, …
  • Use services only for short calculations.
  • Use actions for all longer running processes, e.g. grasping, 
navigation, perception, …
  • Use parameters for values which are known at launch and are not likely to change during run time.
  • Use dynamic parameters (dynamic_reconfigure) for parameter which are likely to change during run time.

出版空间/几何数据Publishing Spatial / Geometric Data

请参阅http://wiki.ros.org/ROS/Patterns/Communication

节点句柄Node Handles

有四种主要类型的节点句柄:

  1. 默认(public)节点句柄: nh_ = ros::NodeHandle();
  2. 私有节点句柄: nh_private_ = ros::NodeHandle(“~”);
  3. 命名空间节点句柄: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. 全局节点句柄:( nh_global_ = ros::NodeHandle(“/“);你可能不应该使用这个。)

一般情况下,您只能使用前2个 - 您也可以使用命名空间节点句柄来分离出许多节点的发布者。

要解释这些操作以及如何使用它们,我们假设您的ROS节点ros_node在命名空间中被命名blah,并且您正在尝试查找该名称topic。下面是他们将如何解决使用所有4个节点句柄:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

如果你试图解决/topic,这将跳过节点的命名空间并解决/topic

There are four main types of node handles:

  1. Default (public) node handle: nh_ = ros::NodeHandle();
  2. Private node handle: nh_private_ = ros::NodeHandle(“~”);
  3. Namespaced node handle: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. Global node handle: 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:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

If, instead, your try to resolve /topic, this will skip the namespace of the node and resolve to /topic.

何时使用哪个节点句柄When to Use Which Node Handle

这些只是一般的指导原则,但是如有可能,在每种情况下都喜欢使用以下内容:

  • 订阅者 - 通常是公共节点句柄。
  • 发布者 - 通常是用于大多数输出​​/可视化的私有节点句柄,有时候需要使用public来进行全局使用的数据(即/odom主题)。
  • 参数 - 几乎总是私有节点句柄。

不要使用全局名称。这是因为当您将节点推送到命名空间中时,它们无法正确解析,并且不允许您一次正常运行多个节点。或者在同一个主机上使用多个机器人。定义相对于节点命名空间的已发布主题和参数:

推荐odometrygrid_mapcam0/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:

  • Subscribers - usually public node handles.
  • Publishers - usually private node handles for most output/visualization, occasionally necessary to use public for globally-used data (i.e., /odom topic).
  • Parameters - almost always private node handle.

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:

Goododometrygrid_mapcam0/camera_info
Bad/odometry/grid_map/helicopter/cam0/camera_info

主题命名Topic Naming

主题应该在节点的上下文中命名。非常简单明了的名称是易于理解的“ROS API”的首选。主题名称只要在节点的命名空间中发布,就不会引起冲突(请参阅名称空间中的主题和参数)。

为了告诉另一个节点在哪里订阅,请将主题名称设置为ROS参数(首选)。或者,对于第三方节点,您可以使用remaproslaunch中的标记。

参考文献:

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:

参数命名Parameter Naming

对于参数使用分层方案,例如

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

参考文献:

Parameter Naming

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:

参数组织Parameter Organisation

如果您的节点只有一个或两个参数,您可以使用<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:

使用第三方库Using Third-Party Libraries

鼓励没有ROS依赖关系的独立库。不要将ROS依赖关系放在算法的核心中!

如果您可以开发一个ROS独立的库并且释放一个并行的ROS功能包

http://courses.csail.mit.edu/6.141/spring2012/pub/lectures/Lec06-ROS.pdf

请参阅使用第三方库

  • 如果可能,尝试使用Debian软件包中的库。
  • 指定rosdep依赖关系(用于安装系统包的工具)。
  • 如果您需要从源代码编译库,则可以创建一个下载和编译包的ROS包装包。
  • 不要在包装包中使用sudo。
  • 不需要手动系统的安装。
  • 不要将库复制到需要它们的包中。

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.

  • If possible, try to use libraries from Debian packages.
  • Specify rosdep dependencies (tool for installing system packages).
  • If you need to compile a library from source create a ROS wrapper package that downloads and compiles the package.
  • Don’t use sudo in wrapper packages.
  • Don’t require manual system wide installations.
  • Don’t copy libraries into packages that need them.

编译Building

不要使用cmake手动打包。

Never call cmake by hand in a package.

依赖Dependencies

保持您的依赖性:

  • 只依靠你所需要的,
  • 指定所有依赖关系,
  • 不要使用隐式依赖关系。

如果需要多次运行catkin_make来构建工作空间,那么有些不合适的!

Keep your dependencies clean:

  • Only depend on what you need,
  • Specify all dependencies,
  • Do not use implicit dependencies.

If multiple runs of catkin_make are required for your workspace to be built, something is fishy!

启动顺序Startup Order

不需要节点的特定启动顺序。使用waitForServicewaitForTransformwaitForServer,...

Do not require a specific startup order for nodes. Use waitForServicewaitForTransformwaitForServer, …

Roslaunch组织Roslaunch Organization

参考Roslaunch的大型项目提示。Refer to Roslaunch tips for large projects.

<include file=“$(find package_name)/launch/another.launch”/>

打印消息/记录Printing Messages/Logging

  • 使用rosconsole实用程序日志(ROS_INFOROS_DEBUG,...)。
  • 使用适当的控制台日志:调试,信息,警告,错误,致命。
  • 提供内省/调试主题。

  • Use rosconsole utilities for logging(ROS_INFO,ROS_DEBUG, …).
  • Use appropriate console logging: Debug, info, warn, error, fatal.
  • Provide introspection/debug topics.

调试Debugging

待补充。

检查订阅者数量Checking the Number of Subscribers

为了避免没有节点订阅的主题的计算开销,请检查订阅者的数量

To avoid computational overhead for topics which no nodes are subscribed to, check the number of subscribers with

if (publisher.getNumSubscribers() < 1) return;

ROS消息记录文件ROS Bag Files

  • 记录Bag:Recording of a bag:

      rosbag record <topic> <topic> … 
    
  • 播放一个Bag:Play a bag:

      rosbag play foo.bag 
    
  • 使用记录时间播放一个Bag(记录打印数据和TF时重要):Play a bag using recorded time (important when stamped data and TF was recorded):

      rosbag play --clock foo.bag
    

    注意:在/use_sim_time初始化节点之前,该参数必须设置为true。Note: The /use_sim_time parameter must be set to true before the node is initialized.

      rosparam set use_sim_time true
    

参考文献:References:

时间Time

使用ros::Timeros::Durationros::Rate替代系统时间。

Use ros::Time, ros::Duration, and ros::Rate instead of 
system time.

在ROS消息和其他类型之间进行转换Converting Between ROS Messages and Other Types

Eigen

要转换消息,请使用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”));

参考文献:

Eigen

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:

OpenCV图像OpenCV Image

使用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编译标志Catkin Build Flags

这些是用于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 ++发行模式下编译

      -DCMAKE_BUILD_TYPE=Release
    
  • 使用C ++ 11 编译

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • 编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • 使用C ++ 11索引编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L

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

      -DCMAKE_BUILD_TYPE=Release
    
  • Build with C++11

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • Build Eclipse projects

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • Build Eclipse projects with C++11 indexing

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L


----

I'm going to build a small robot system, and it seems like that ROS serves a nice framework to control and program the system.

However, I am wondering which is the best practice to manage the components of my robot.

  • Does it make sense to put all the sensors in one node?

  • Should I only put the sensors of the same type in one node or is it better to have one node for one sensor?

  • Is it a good practice to have some kind of handler node, which takes input from sensors and steers the corresponding actuators or should the actuator nodes and sensor nodes communicate directly?


  1. Fused sensor nodes and actuator nodes with handler 1. Fused sensor nodes and actuator nodes with handler

  2. Single sensor and actuator nodes with handler enter image description here

  3. Direct communication enter image description here

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.

share improve this question
 

1 Answer

up vote 8 down vote accepted

Very short answer: 2


Sensors

Regarding whether reading from sensors all in one node or each separately, you should ask yourself this question:

Are the sensors meaningless without the other?

This question asks if the sensors are tightly coupled or not. For example, say you have a sensor that is sensitive to temperature (and you need to compensate for it). You add a temperature sensor primarily to fix the value of the other sensor. In this scenario, it makes sense to read both values at the same time, since they are tightly coupled. In fact, without the readings from the temperature sensor, the readings from the original sensor is useless.

On the other hand, if the sensors are individually useful, by all means keep them in separate nodes. This has many benefits:

  • The nodes can be run on separate processors
  • The nodes can be reused in future robots
  • Failure in communication with one node doesn't bring the whole system down
  • Restart of acquisition from a faulty sensor board can be done separately from the others

In fact, if you need any of the above benefits, you would have to go with separate nodes, even if the sensors are tightly coupled, but that usually doesn't happen.

Actuators

This is a analogous.

Are the actuators meaningless without the other?

For example, if you are designing a wrist with robotic tendons where for each tendon (for whatever reason) two motors are responsible to simultaneously work to move a joint in one or the other direction, then having them served in the same node makes much more sense than separate.

On the other hand, where actuators are independent (common case), it makes sense to have one node for each actuator. In that case, each could be put in a different node. Besides the exact same benefits as with sensors, there is this added benefit:

  • If an actuators is stalled (for whatever reason), the other actuators still function. If there is redundant degrees of freedom, they could even completely compensate for it.

This has one implication. If you need the actuators to work in harmony, then put them in the same node. This is not just because of failure in communication, but because different nodes means different delays; on a distributed system each node is on a different part of the network and hence the difference in delays, on a centralized system different delays happen on high CPU loads due to each process's luck in scheduling.

Should There Be a Handler?

Even though the answer is "it depends", there is a common approach with many advantages. Let's change the name and call it "controller". The approach is "yes, there should be a controller".

The advantages of having a controller are (among many):

  • Decoupled processing: each node is responsible for one thing which means:
    • Simplicity: which implies
      • Easier development
      • Easier debugging
      • Fewer errors
      • Less chance of failure
    • Reusability: because the same controller can be used with different sensor nodes if they have the same functionality (i.e. message and service formats).
  • Execution on separate hardware: each node can be moved in the network. For example, sensor and actuator nodes may be moved to a dedicated microcontroller (Arduino for example (not that I recommend)) and the controller on a PC.
  • Avoid extreme ugliness: if the sensors wanted to directly influence the actuators, the result is simply a mess. Assuming no controller, let's look at each case:
    • One sensor node: basically this means the sensor node and the controller are put together in the same node. Not too bad, but very unnecessary.
    • Many sensor nodes: this is the mess. This means the controller is distributed among the sensor nodes. Therefore all the sensor nodes have to talk with each other for each to know how to control its associated actuator(s). Imagine a failure in communication or various kinds of delays and you'll see how difficult it becomes. Given that this is utterly unnecessary, there is no reason for doing it!

These said, there are disadvantages too. Having more nodes (any nodes, not just the controller) means:

  • More wasted communication: the data have to move around in standard formats (so serialized and deserialized) through network or shared memory, ROS core has to look at them and decide who to deliver them to, etc. In short, some system resources are wasted in communication. If all nodes where in one, that cost could have been zero.
  • Higher chance of failure: if for whatever reason a network link goes down, or a node dies, there is a failure in the system. If you are not prepared for it, it can take down the whole system. Now this is actually a good thing in general to be able to lose part of the system but not all of it (graceful degradation), but there also exist applications where this should be avoided as much as possible. Cutting the communication and putting all code in one node actually helps with system stability. The down side is of course, the system either works fine or suddenly dies completely.
  • Chaotic timings: each node runs on its own. The time it takes for its messages to arrive at others is non-deterministic and varies run by run. Unless your nodes timestamp each message (as a side note: you need to have synchronized clocks to a good degree, which ROS doesn't) and unless each receiving node can take the delay into account and control accordingly (which is a very difficult task on its own) then having multiple nodes means high uncertainty about the age of the data. This is actually one of the reasons (among many) that most robots move so slow; their control loop has to be slow enough to make sure all data correspond to the current period. The larger the delays, the slower the control loop.

In all above disadvantages, the solution is to reduce the number of nodes, preferably to a single node. Wait a minute, that's not using ROS anymore! Exactly.

To summarize:

  • Use ROS for non-realtime systems where delays could sporadically get high. In that case, feel free to have as many ROS nodes as you wish. In fact, it's very good practice to have each ROS node do one and only one thing. That way, they become very simple, and they become highly reusable.
  • On the other hand, for realtime systems, by all means avoid ROS. For that there is orocos and technologies like EtherCAT and more often than not, ad-hoc solutions.

As a final word, in practice ROS does fine. Not great, but fine. Very often the system is not critical and the chance of failure is so small that every now and then a restart is not a big deal. This is the Ostrich algorithm!



Robot Web Tools:https://github.com/RobotWebTools

----


ROS机器人操作系统最佳实践指南

----ROS Best Practices:https://github.com/ethz-asl/ros_best_practices/wiki----

这是使用机器人操作系统(ROS)的最佳实践、惯例和技巧的松散集合。它建立在官方ROS文档和其他资源上,并且作为总结和概括。


ROS最佳实践指南ROS Best Practices

这是使用机器人操作系统(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éterFankhauserpfankhauser@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

必做要求Todo

在你开始之前(预备基础--查阅功能包清单)Before You Begin

研究已经开发出其他产品:http : //www.ros.org/browse/list.php

Research other products out there already: http://www.ros.org/browse/list.php

编程指南Coding Guidelines

请参阅ROS C ++格式指南。在自动控制系统实验室,我们使用Google风格指南

Refer to the ROS C++ Style Guide. At the Autonomous Systems Lab we use the Google style-guide.

单位和规范公约Units and Coordinate Conventions

参考标准测量单位和坐标公约

Refer to Standard Units of Measure and Coordinate Conventions.

坐标框架Coordinate Frames

测试Testing

请参阅http://wiki.ros.org/UnitTesting

Refer to http://wiki.ros.org/UnitTesting.

功能包组织Package Organization

  • ROS包的开销不大。当需要只用时,定义单独的包。通常,代码可以在除了编译它们之外的上下文中使用。
  • 避免组合引入相互不必要的依赖关系的节点,并且经常单独使用(以消除不必要的编译开销)。
  • 包依赖图必须是非循环的,即没有包可能依赖于直接或间接依赖于另一个的包。
  • 如果具有相似依赖关系的程序通常一起使用,请考虑将它们组合成一个包。
  • 如果某些节点对共享代码具有公共依赖性,您不希望公开导出,那么它们可以在一个包内部组合。
  • 创建单独的包,仅包含消息,服务和操作(分离接口和实现)。单独消息包的示例是ros / common_msgs包。
  • 堆栈中的组包。

  • The overhead of a ROS package is not large. Define separate packages wherever they make sense. Often, code can be useful in contexts other than those for which it was built.
  • Avoid combining nodes that pull in mutually unneeded dependencies and are often used separately (to eliminate unnecessary build overhead).
  • The package dependency graph must be acyclic, i.e. no package may depend on another that directly or indirectly depends on it.
  • If programs with similar dependencies are generally used together, consider combining them into a single package.
  • If some nodes have common dependencies on shared code that you do not wish to export publicly, they can be combined internally within a single package.
  • Create separate packages that contain only messages, services and actions (separation of interface and implementation). Examples for separate message packages are the ros/common_msgs packages.
  • Group packages in stacks.

资料来源:

参考文献:

Sources:

References:

功能包命名Package Naming

请参阅命名ROS资源REP-144:ROS包命名(草案)。

Refer to Naming ROS Resources and REP-144: ROS Package Naming (draft).

选择名称:

  • 他们后来会变得很乱。
  • 软件包名称对于整个ROS生态系统是全球性的。
  • 尝试选择对可能希望使用您的代码的其他人有意义的名称。
  • 软件包名称应具体到足以识别软件包的功能。不要超过范围,例如,计划器是一个不好的名称,请改用wavefront_planner。
  • 不要使用“utils”或其他产品。
  • 前缀包名建议当并不意味着包装的情况下更广泛地使用(例如,包是特定于StlarETH机器人使用“starleth_”前缀)。

Choose the name carefully:

  • They are messy to change later.
  • Package names are global to the entire ROS ecosystem.
  • Try to pick names that will make sense to others who may wish to use your code.
  • Package names should be specific enough to identify what the package does. Do not over scope, e.g. planner is a bad name, use wavefront_planner instead.
  • Do not use “utils” or other catchalls.
  • Prefixing a package name is recommended only when the package is not meant to be used more widely (e.g., packages that are specific to the StlarETH robot use the ‘starleth_’ prefix).

功能包、节点、主题、服务、TF等的命名约定

Naming Conventions for Packages, Nodes, Topics, Services, TF etc.

改编自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:

  • Package names are lower case.
  • Packages must not contain dashes (“-”), only underscores (“_”).
  • Nodes, topics, services, actions, parameters are all lower case with underscores as separator.
  • 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.
  • Do not use the word “action” in an action definition: Foo.action, not FooAction.action.

自定义ROS消息和服务Custom ROS Message and Services

  • 尽可能使用标准数据类型(尽量防止.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.srvSaveMapToFile.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

      string file_path
      —
    

    定义一种类型的“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.

参考文献:

文档Documentation

  • 为每个软件包创建一个简短的README.md/Wiki:
    • 记录节点的作用,
    • 记录所需和提供的主题,服务和行动,
    • 文档ROS参数及其默认值,这里README.md提供一个模板
  • 提供启动文件,
  • 提供一个rosinstall文件。

  • Create a short README.md/Wiki for each package:
    • Document what the node does,
    • Document topics, services and actions that are required and provided,
    • Document ROS parameters and their default values, A template for the README.md is provided here
  • Provide launch files,
  • Provide a rosinstall file.

软件包的文件/文件夹结构File/Folder Structure for Packages

使用此文件/文件夹结构进行常规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:

主题vs服务vs Actionlib vs参数vs动态参数

Topics vs Services vs Actionlib vs Parameters vs Dynamic Parameters

参考ROS模式 - 通信

概要:

  • 使用主题发布连续数据流,例如传感器数据,连续检测结果...  topic
  • 仅使用服务进行短期计算。  service
  • 对所有更长的运行过程使用操作,例如抓握,导航,感知,...  action
  • 对启动时已知的值使用参数,并且在运行时不会更改。  parameters
  • dynamic_reconfigure对运行时可能会发生变化的参数使用动态参数(dynamic_reconfigure)。  dynamic_reconfigure

Refer to ROS Patterns - Communication.

Summary:

  • Use topics for publishing continuous streams of data, e.g. sensor data, continuous detection results, …
  • Use services only for short calculations.
  • Use actions for all longer running processes, e.g. grasping, 
navigation, perception, …
  • Use parameters for values which are known at launch and are not likely to change during run time.
  • Use dynamic parameters (dynamic_reconfigure) for parameter which are likely to change during run time.

出版空间/几何数据Publishing Spatial / Geometric Data

请参阅http://wiki.ros.org/ROS/Patterns/Communication

节点句柄Node Handles

有四种主要类型的节点句柄:

  1. 默认(public)节点句柄: nh_ = ros::NodeHandle();
  2. 私有节点句柄: nh_private_ = ros::NodeHandle(“~”);
  3. 命名空间节点句柄: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. 全局节点句柄:( nh_global_ = ros::NodeHandle(“/“);你可能不应该使用这个。)

一般情况下,您只能使用前2个 - 您也可以使用命名空间节点句柄来分离出许多节点的发布者。

要解释这些操作以及如何使用它们,我们假设您的ROS节点ros_node在命名空间中被命名blah,并且您正在尝试查找该名称topic。下面是他们将如何解决使用所有4个节点句柄:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

如果你试图解决/topic,这将跳过节点的命名空间并解决/topic

There are four main types of node handles:

  1. Default (public) node handle: nh_ = ros::NodeHandle();
  2. Private node handle: nh_private_ = ros::NodeHandle(“~”);
  3. Namespaced node handle: nh_aslam_ = ros::NodeHandle(“aslam”);
  4. Global node handle: 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:

  1. /blah/topic
  2. /blah/ros_node/topic
  3. /blah/aslam/topic
  4. /topic

If, instead, your try to resolve /topic, this will skip the namespace of the node and resolve to /topic.

何时使用哪个节点句柄When to Use Which Node Handle

这些只是一般的指导原则,但是如有可能,在每种情况下都喜欢使用以下内容:

  • 订阅者 - 通常是公共节点句柄。
  • 发布者 - 通常是用于大多数输出​​/可视化的私有节点句柄,有时候需要使用public来进行全局使用的数据(即/odom主题)。
  • 参数 - 几乎总是私有节点句柄。

不要使用全局名称。这是因为当您将节点推送到命名空间中时,它们无法正确解析,并且不允许您一次正常运行多个节点。或者在同一个主机上使用多个机器人。定义相对于节点命名空间的已发布主题和参数:

推荐odometrygrid_mapcam0/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:

  • Subscribers - usually public node handles.
  • Publishers - usually private node handles for most output/visualization, occasionally necessary to use public for globally-used data (i.e., /odom topic).
  • Parameters - almost always private node handle.

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:

Goododometrygrid_mapcam0/camera_info
Bad/odometry/grid_map/helicopter/cam0/camera_info

主题命名Topic Naming

主题应该在节点的上下文中命名。非常简单明了的名称是易于理解的“ROS API”的首选。主题名称只要在节点的命名空间中发布,就不会引起冲突(请参阅名称空间中的主题和参数)。

为了告诉另一个节点在哪里订阅,请将主题名称设置为ROS参数(首选)。或者,对于第三方节点,您可以使用remaproslaunch中的标记。

参考文献:

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:

参数命名Parameter Naming

对于参数使用分层方案,例如

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

参考文献:

Parameter Naming

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:

参数组织Parameter Organisation

如果您的节点只有一个或两个参数,您可以使用<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:

使用第三方库Using Third-Party Libraries

鼓励没有ROS依赖关系的独立库。不要将ROS依赖关系放在算法的核心中!

如果您可以开发一个ROS独立的库并且释放一个并行的ROS功能包

http://courses.csail.mit.edu/6.141/spring2012/pub/lectures/Lec06-ROS.pdf

请参阅使用第三方库

  • 如果可能,尝试使用Debian软件包中的库。
  • 指定rosdep依赖关系(用于安装系统包的工具)。
  • 如果您需要从源代码编译库,则可以创建一个下载和编译包的ROS包装包。
  • 不要在包装包中使用sudo。
  • 不需要手动系统的安装。
  • 不要将库复制到需要它们的包中。

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.

  • If possible, try to use libraries from Debian packages.
  • Specify rosdep dependencies (tool for installing system packages).
  • If you need to compile a library from source create a ROS wrapper package that downloads and compiles the package.
  • Don’t use sudo in wrapper packages.
  • Don’t require manual system wide installations.
  • Don’t copy libraries into packages that need them.

编译Building

不要使用cmake手动打包。

Never call cmake by hand in a package.

依赖Dependencies

保持您的依赖性:

  • 只依靠你所需要的,
  • 指定所有依赖关系,
  • 不要使用隐式依赖关系。

如果需要多次运行catkin_make来构建工作空间,那么有些不合适的!

Keep your dependencies clean:

  • Only depend on what you need,
  • Specify all dependencies,
  • Do not use implicit dependencies.

If multiple runs of catkin_make are required for your workspace to be built, something is fishy!

启动顺序Startup Order

不需要节点的特定启动顺序。使用waitForServicewaitForTransformwaitForServer,...

Do not require a specific startup order for nodes. Use waitForServicewaitForTransformwaitForServer, …

Roslaunch组织Roslaunch Organization

参考Roslaunch的大型项目提示。Refer to Roslaunch tips for large projects.

<include file=“$(find package_name)/launch/another.launch”/>

打印消息/记录Printing Messages/Logging

  • 使用rosconsole实用程序日志(ROS_INFOROS_DEBUG,...)。
  • 使用适当的控制台日志:调试,信息,警告,错误,致命。
  • 提供内省/调试主题。

  • Use rosconsole utilities for logging(ROS_INFO,ROS_DEBUG, …).
  • Use appropriate console logging: Debug, info, warn, error, fatal.
  • Provide introspection/debug topics.

调试Debugging

待补充。

检查订阅者数量Checking the Number of Subscribers

为了避免没有节点订阅的主题的计算开销,请检查订阅者的数量

To avoid computational overhead for topics which no nodes are subscribed to, check the number of subscribers with

if (publisher.getNumSubscribers() < 1) return;

ROS消息记录文件ROS Bag Files

  • 记录Bag:Recording of a bag:

      rosbag record <topic> <topic> … 
    
  • 播放一个Bag:Play a bag:

      rosbag play foo.bag 
    
  • 使用记录时间播放一个Bag(记录打印数据和TF时重要):Play a bag using recorded time (important when stamped data and TF was recorded):

      rosbag play --clock foo.bag
    

    注意:在/use_sim_time初始化节点之前,该参数必须设置为true。Note: The /use_sim_time parameter must be set to true before the node is initialized.

      rosparam set use_sim_time true
    

参考文献:References:

时间Time

使用ros::Timeros::Durationros::Rate替代系统时间。

Use ros::Time, ros::Duration, and ros::Rate instead of 
system time.

在ROS消息和其他类型之间进行转换Converting Between ROS Messages and Other Types

Eigen

要转换消息,请使用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”));

参考文献:

Eigen

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:

OpenCV图像OpenCV Image

使用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编译标志Catkin Build Flags

这些是用于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 ++发行模式下编译

      -DCMAKE_BUILD_TYPE=Release
    
  • 使用C ++ 11 编译

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • 编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • 使用C ++ 11索引编译Eclipse项目

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L

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

      -DCMAKE_BUILD_TYPE=Release
    
  • Build with C++11

      -DCMAKE_CXX_COMPILER_ARG1=-std=c++11
    
  • Build Eclipse projects

      -G"Eclipse CDT4 - Unix Makefiles"
    
  • Build Eclipse projects with C++11 indexing

      -G"Eclipse CDT4 - Unix Makefiles" -D__GXX_EXPERIMENTAL_CXX0X__=1 -D__cplusplus=201103L


----

I'm going to build a small robot system, and it seems like that ROS serves a nice framework to control and program the system.

However, I am wondering which is the best practice to manage the components of my robot.

  • Does it make sense to put all the sensors in one node?

  • Should I only put the sensors of the same type in one node or is it better to have one node for one sensor?

  • Is it a good practice to have some kind of handler node, which takes input from sensors and steers the corresponding actuators or should the actuator nodes and sensor nodes communicate directly?


  1. Fused sensor nodes and actuator nodes with handler 1. Fused sensor nodes and actuator nodes with handler

  2. Single sensor and actuator nodes with handler enter image description here

  3. Direct communication enter image description here

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.

share improve this question
 

1 Answer

up vote 8 down vote accepted

Very short answer: 2


Sensors

Regarding whether reading from sensors all in one node or each separately, you should ask yourself this question:

Are the sensors meaningless without the other?

This question asks if the sensors are tightly coupled or not. For example, say you have a sensor that is sensitive to temperature (and you need to compensate for it). You add a temperature sensor primarily to fix the value of the other sensor. In this scenario, it makes sense to read both values at the same time, since they are tightly coupled. In fact, without the readings from the temperature sensor, the readings from the original sensor is useless.

On the other hand, if the sensors are individually useful, by all means keep them in separate nodes. This has many benefits:

  • The nodes can be run on separate processors
  • The nodes can be reused in future robots
  • Failure in communication with one node doesn't bring the whole system down
  • Restart of acquisition from a faulty sensor board can be done separately from the others

In fact, if you need any of the above benefits, you would have to go with separate nodes, even if the sensors are tightly coupled, but that usually doesn't happen.

Actuators

This is a analogous.

Are the actuators meaningless without the other?

For example, if you are designing a wrist with robotic tendons where for each tendon (for whatever reason) two motors are responsible to simultaneously work to move a joint in one or the other direction, then having them served in the same node makes much more sense than separate.

On the other hand, where actuators are independent (common case), it makes sense to have one node for each actuator. In that case, each could be put in a different node. Besides the exact same benefits as with sensors, there is this added benefit:

  • If an actuators is stalled (for whatever reason), the other actuators still function. If there is redundant degrees of freedom, they could even completely compensate for it.

This has one implication. If you need the actuators to work in harmony, then put them in the same node. This is not just because of failure in communication, but because different nodes means different delays; on a distributed system each node is on a different part of the network and hence the difference in delays, on a centralized system different delays happen on high CPU loads due to each process's luck in scheduling.

Should There Be a Handler?

Even though the answer is "it depends", there is a common approach with many advantages. Let's change the name and call it "controller". The approach is "yes, there should be a controller".

The advantages of having a controller are (among many):

  • Decoupled processing: each node is responsible for one thing which means:
    • Simplicity: which implies
      • Easier development
      • Easier debugging
      • Fewer errors
      • Less chance of failure
    • Reusability: because the same controller can be used with different sensor nodes if they have the same functionality (i.e. message and service formats).
  • Execution on separate hardware: each node can be moved in the network. For example, sensor and actuator nodes may be moved to a dedicated microcontroller (Arduino for example (not that I recommend)) and the controller on a PC.
  • Avoid extreme ugliness: if the sensors wanted to directly influence the actuators, the result is simply a mess. Assuming no controller, let's look at each case:
    • One sensor node: basically this means the sensor node and the controller are put together in the same node. Not too bad, but very unnecessary.
    • Many sensor nodes: this is the mess. This means the controller is distributed among the sensor nodes. Therefore all the sensor nodes have to talk with each other for each to know how to control its associated actuator(s). Imagine a failure in communication or various kinds of delays and you'll see how difficult it becomes. Given that this is utterly unnecessary, there is no reason for doing it!

These said, there are disadvantages too. Having more nodes (any nodes, not just the controller) means:

  • More wasted communication: the data have to move around in standard formats (so serialized and deserialized) through network or shared memory, ROS core has to look at them and decide who to deliver them to, etc. In short, some system resources are wasted in communication. If all nodes where in one, that cost could have been zero.
  • Higher chance of failure: if for whatever reason a network link goes down, or a node dies, there is a failure in the system. If you are not prepared for it, it can take down the whole system. Now this is actually a good thing in general to be able to lose part of the system but not all of it (graceful degradation), but there also exist applications where this should be avoided as much as possible. Cutting the communication and putting all code in one node actually helps with system stability. The down side is of course, the system either works fine or suddenly dies completely.
  • Chaotic timings: each node runs on its own. The time it takes for its messages to arrive at others is non-deterministic and varies run by run. Unless your nodes timestamp each message (as a side note: you need to have synchronized clocks to a good degree, which ROS doesn't) and unless each receiving node can take the delay into account and control accordingly (which is a very difficult task on its own) then having multiple nodes means high uncertainty about the age of the data. This is actually one of the reasons (among many) that most robots move so slow; their control loop has to be slow enough to make sure all data correspond to the current period. The larger the delays, the slower the control loop.

In all above disadvantages, the solution is to reduce the number of nodes, preferably to a single node. Wait a minute, that's not using ROS anymore! Exactly.

To summarize:

  • Use ROS for non-realtime systems where delays could sporadically get high. In that case, feel free to have as many ROS nodes as you wish. In fact, it's very good practice to have each ROS node do one and only one thing. That way, they become very simple, and they become highly reusable.
  • On the other hand, for realtime systems, by all means avoid ROS. For that there is orocos and technologies like EtherCAT and more often than not, ad-hoc solutions.

As a final word, in practice ROS does fine. Not great, but fine. Very often the system is not critical and the chance of failure is so small that every now and then a restart is not a big deal. This is the Ostrich algorithm!


ROS2编程基础课程文档 ROS 2(机器人操作系统2)是用于机器人应用的开源开发套件。ROS 2之目的是为各行各业的开发人员提供标准的软件平台,从研究和原型设计再到部署和生产。 ROS 2建立在ROS 1的成功基础之上,ROS 1目前已在世界各地的无数机器人应用中得到应用。 特色 缩短上市时间 ROS 2提供了开发应用程序所需的机器人工具,库和功能,可以将时间花在对业务非常重要的工作上。因为它 是开源的,所以可以灵活地决定在何处以及如何使用ROS 2,以及根据实际的需求自由定制,使用ROS 2 可以大幅度提升产品和算法研发速度! 专为生产而设计 凭借在建立ROS 1作为机器人研发的事实上的全球标准方面的十年经验,ROS 2从一开始就被建立在工业级 基础上并可用于生产,包括高可靠性和安全关键系统。 ROS 2的设计选择、开发实践和项目管理基于行业利 益相关者的要求。 多平台支持 ROS 2在Linux,Windows和macOS上得到支持和测试,允许无缝开发和部署机器人自动化,后端管理和 用户界面。分层支持模型允许端口到新平台,例如实时和嵌入式操作系统,以便在获得兴趣和投资时引入和推 广。 丰富的应用领域 与之前的ROS 1一样,ROS 2可用于各种机器人应用,从室内到室外、从家庭到汽车、水下到太空、从消费 到工业。 没有供应商锁定 ROS 2建立在一个抽象层上,使机器人库和应用程序与通信技术隔离开来。抽象底层是通信代码的多种实现, 包括开源和专有解决方案。在抽象顶层,核心库和用户应用程序是可移植的。 建立在开放标准之上 ROS 2中的默认通信方法使用IDL、DDS和DDS-I RTPS等行业标准,这些标准已广泛应用于从工厂到航空 航天的各种工业应用中。 开源许可证 ROS 2代码在Apache 2.0许可下获得许可,在3条款(或“新”)BSD许可下使用移植的ROS 1代码。这两个 许可证允许允许使用软件,而不会影响用户的知识产权。 全球社区 超过10年的ROS项目通过发展一个由数十万开发人员和用户组成的全球社区,为机器人技术创建了一个庞大 的生态系统,他们为这些软件做出贡献并进行了改进。 ROS 2由该社区开发并为该社区开发,他们将成为未 来的管理者。 行业支持 正如ROS 2技术指导委员会成员所证明的那样,对ROS 2的行业支持很强。除了开发顶级产品外,来自世界 各地的大大小小公司都在投入资源为ROS 2做出开源贡献。 与ROS1的互操作性 ROS 2包括到ROS 1的桥接器,处理两个系统之间的双向通信。如果有一个现有的ROS 1应用程序, 可 以通过桥接器开始尝试使用ROS 2,并根据要求和可用资源逐步移植应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值