ROS专题----导航功能包navigation基础汇总
资料来源ROS官网:http://wiki.ros.org/cn/navigation
概述
概念层面上讲,导航功能包集是相当简单的。 它从里程计和传感器数据流获取信息,并将速度命令发送给移动基站(比如你的机器人)。但是,想要在任意机器人上使用导航功能包集可能有点复杂。使用导航功能包集的先决条件是,机器人必须运行ROS,有一个tf变换树,使用正确的ROS Message types发布传感器数据。而且,我们需要在高层为一个具有一定形状和动力学特点的机器人配置导航功能包集。本手册指导配置一个典型的导航功能包集。
硬件需求
- 虽然导航功能包集被设计成尽可能的通用,在使用时仍然有三个主要的硬件限制:
- 它是为差分驱动的完全约束的轮式机器人设计的。它假设移动基站受到理想的运动命令的控制并可实现预期的结果,命令的格式为:x速度分量,y速度分量,角速度(theta)分量。
- 它需要在移动基站上安装一个平面二维激光。这个激光用于构建地图和定位。
- 导航功能包集是为正方形的机器人开发的,所以方形或圆形的机器人将是性能最好的。 它也可以工作在任意形状和大小的机器人上,但是较大的矩形机器人将很难通过狭窄的空间,例如门道。
----配置机器人的TF----
Transform Configuration(变换配置)
许多ROS功能包,都要求利用tf软件库,以机器人识别的变换树的形式进行发布。抽象层面上,变换树其实就是一种“偏移”,代表了不同坐标系之间的变换和旋转。更具体点来说,设想一个简单的机器人,只有一个基本的移动机体和挂在机体上方的扫描仪。基于此,我们定义了两个坐标系:一个对应于机体中心点的坐标系,一个对应于扫描仪中心的坐标系。分别取名为“base_link”和“baser_laser”。关于坐标系的命名习惯,参考REP 105.
此时,可以假设,我们已经从传感器获取了一些数据,以一种代表了物体到扫描仪中心点的距离的形式给出。换句话说,我们已经有了一些“base_laser”坐标系的数据。现在,我们期望通过这些数据,来帮助机体避开物理世界的障碍物。成功的关键是,我们需要一种方式,把传感器扫描的数据,从“base_laser”坐标系转换到“base_link”坐标系中去。本质上,就是定义一种两个坐标系的“关系”。
为了定义这种关系,假设我们知道,传感器是挂在机体中心的前方10cm,高度20cm处。这就等于给了我们一种转换的偏移关系。具体来说,就是,从传感器到机体的坐标转换关系应该为(x:0.1m,y:0.0m, z:0.2m),相反的转换即是(x:-0.1m,y:0.0m,z:0.2m)。
我们可以选择去自己管理这种变换关系,意味着需要自己去保存,以及在需要的时候调用。但是,这种做法的缺陷即是随着坐标转换关系数量的增加,而愈加麻烦。幸运的是,我们也没有必要这么干。相反,我们利用tf定义了这么一种转换关系,那么就让它来帮我们管理这种转换关系吧。
利用tf来管理这种关系,我们需要把他们添加到转换树(transform tree)中。一方面来说,转换树中的每一个节点都对应着一类坐标系,节点之间的连线即是两个坐标相互转换的一种表示,一种从当前节点到子节点的转换表示。Tf利用树结构的方式,保证了两个坐标系之间的只存在单一的转换,同时假设节点之间的连线指向是从parent到child。
基于我们简单的例子,我们需要创建两个节点,一个“base_link”,一个是“base_laser”。为了定义两者的关系,首先,我们需要决定谁是parent,谁是child。时刻记得,由于tf假设所有的转换都是从parent到child的,因此谁是parent是有差别的。我们选择“base_link”坐标系作为parent,其他的传感器等,都是作为“器件”被添加进robot的,对于“base_link”和“base_laser”他们来说,是最适合的。这就意味着转换关系的表达式应该是(x:0.1m,y0.0m,z:0.2m)。关系的建立,在收到“base_laser”的数据到“base_link”的转换过程,就可以是简单的调用tf库即可完成。我们的机器人,就可以利用这些信息,在“base_link”坐标系中,就可以推理出传感器扫描出的数据,并可安全的规划路径和避障等工作。
Writing Code(代码编写)
希望上面的例子,一定程度上可以帮助大家理解tf。现在,我们可以建立通过代码来实现转换树。对于这个例子来说,前提是熟悉ROS,所以如果不熟悉,先预习下ROS Documentation吧。
假定,我们以上层来描述“base_laser”坐标系的点,来转换到"base_link"坐标系。首先,我们需要创建节点,来发布转换关系到ROS系统中。下一步,我们必须创建一个节点,来监听需要转换的数据,同时获取并转换。在某个目录创建一个源码包,同时命名“robot_setup_tf”。添加依赖包roscpp,tf,geometry_msgs。
$ cd %TOP_DIR_YOUR_CATKIN_WS%/src $ catkin_create_pkg robot_setup_tf roscpp tf geometry_msgs
至此,你必须运行上面的命令,当然你必须有必要的权限(例如,~/ros目录下,你可能在之前的文档中操作过这个目录)
Alternative in fuerte, groovy and hydro: there is a standard robot_setup_tf_tutorial package in the navigation_tutorialsstack. You may want to install by following (%YOUR_ROS_DISTRO% can be { fuerte, groovy } etc.):
$ sudo apt-get install ros-%YOUR_ROS_DISTRO%-navigation-tutorials
Broadcasting a Transform(广播变换)
至此,我们已经创建了package。我们需要创建对于的节点,来实现广播任务base_laser->base_link。在robot_setup_tf包中,用你最喜欢的编辑器打开,然后将下面的代码粘贴到src/tf_broadcaster.cpp文件中去。
1 #include <ros/ros.h> 2 #include <tf/transform_broadcaster.h> 3 4 int main(int argc, char** argv){ 5 ros::init(argc, argv, "robot_tf_publisher"); 6 ros::NodeHandle n; 7 8 ros::Rate r(100); 9 10 tf::TransformBroadcaster broadcaster; 11 12 while(n.ok()){ 13 broadcaster.sendTransform( 14 tf::StampedTransform( 15 tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0.1, 0.0, 0.2)), 16 ros::Time::now(),"base_link", "base_laser")); 17 r.sleep(); 18 } 19 }
现在,让我们来针对上面的代码,作更细节的解释。
Error: No code_block found Tf功能包提供了一种实现tf::TransformBroadcaster ,使任务发布变换更容易。为了调用TransformBroadcaster, 我们需要包含 tf/transform_broadcaster.h 头文件.
Error: No code_block found 我们创建一个TransformBroadcaster对象,之后我们可以利用他来发送变换关系,即base_link→ base_laser。
Error: No code_block found 这部分是关键部分。通过TransformBroadcaster来发送转换关系,需要附带5个参数。第1个参数,我们传递了旋转变换,在两个坐标系的发送的任意旋转,都必须通过调用btQuaternion.现在情况下,我们不想旋转,所以我们在调用btQauternion的时候,将pitch,roll,yaw的参数都置0.第2个参数,btVector3,任何变换过程都需要调用它。无论怎样,我们确实需要做一个变换,所以我们调用了btVector3,相应的传感器的x方向距离机体基准偏移10cm,z方向20cm。第3个参数,我们需要给定转换关系携带一个时间戳,我们标记为ros::Time::now()。第4个参数,我们需要传递parent节点的名字。第5个参数,传递的是child节点的名字。
Using a Transform(调用变换)
上面的代码,我们创建了一个节点来发布转换关系,baser_laser->base_link。现在,我们需要利用转换关系,将从传感器获取的数据转换到机体对应的数据,即是“base_laser”->到“base_link”坐标系的转换。下面的代码,后边会紧根更详细的解析。在robot_setup_if功能包中,在src目录下创建tf_listener.cpp,并将下面的代码粘贴到里面:
1 #include <ros/ros.h> 2 #include <geometry_msgs/PointStamped.h> 3 #include <tf/transform_listener.h> 4 5 void transformPoint(const tf::TransformListener& listener){ 6 //we'll create a point in the base_laser frame that we'd like to transform to the base_link frame 7 geometry_msgs::PointStamped laser_point; 8 laser_point.header.frame_id = "base_laser"; 9 10 //we'll just use the most recent transform available for our simple example 11 laser_point.header.stamp = ros::Time(); 12 13 //just an arbitrary point in space 14 laser_point.point.x = 1.0; 15 laser_point.point.y = 0.2; 16 laser_point.point.z = 0.0; 17 18 try{ 19 geometry_msgs::PointStamped base_point; 20 listener.transformPoint("base_link", laser_point, base_point); 21 22 ROS_INFO("base_laser: (%.2f, %.2f. %.2f) -----> base_link: (%.2f, %.2f, %.2f) at time %.2f", 23 laser_point.point.x, laser_point.point.y, laser_point.point.z, 24 base_point.point.x, base_point.point.y, base_point.point.z, base_point.header.stamp.toSec()); 25 } 26 catch(tf::TransformException& ex){ 27 ROS_ERROR("Received an exception trying to transform a point from \"base_laser\" to \"base_link\": %s", ex.what()); 28 } 29 } 30 31 int main(int argc, char** argv){ 32 ros::init(argc, argv, "robot_tf_listener"); 33 ros::NodeHandle n; 34 35 tf::TransformListener listener(ros::Duration(10)); 36 37 //we'll transform a point once every second 38 ros::Timer timer = n.createTimer(ros::Duration(1.0), boost::bind(&transformPoint, boost::ref(listener))); 39 40 ros::spin(); 41 42 }
Ok... now we'll walk through the important bits step by step.
好的,现在让我们将上面代码的重点步步分解:
Error: No code_block found 这里,我们包含tf/transform_listener.h头文件,是为了后边创建tf::TransformListener。一个TransformListener目标会自动的订阅ROS系统中的变换消息主题,同时管理所有的该通道上的变换数据。
Error: No code_block found创建一个函数,参数为TransformListener,作用为将“base_laser”坐标系的点,变换到“base_link”坐标系中。这个函数将会以ros::Timer定义的周期,作为一个回调函数周期调用。目前周期是1s。
Error: No code_block found 此处,我们创建一个虚拟点,作为geometry_msgs::PointStamped。消息名字最后的“Stamped”的意义是,它包含了一个头部,允许我们去把时间戳和消息的frame_id相关关联起来。我们将会设置laser_point的时间戳为ros::time(),即是允许我们请求TransformListener取得最新的变换数据。对于header里的frame_id,我们设置为“base_laser”,因为我们是创建的是扫描仪坐标系里的虚拟点。最后,我们将会设置具体的虚拟点,比如x:1.0,y:0.2,z:0.0
Error: No code_block found 至此,我们已经有了从“base_laser”到“base_link”变换的点数据。进一步,我们通过TransformListener对象,调用transformPoint(),填充三个参数来进行数据变换。第1个参数,代表我们想要变换的目标坐标系的名字。第2个参数填充需要变换的原始坐标系的点对象,第3个参数填充,目标坐标系的点对象。所以,在函数调用后,base_point里存储的信息就是变换后的点坐标。
Error: No code_block found 如果某些其他的原因,变换不可得(可能是tf_broadcaster 挂了),调用transformPoint()时,TransformListener调用可能会返回异常。为了体现代码的优雅性,我们将会截获异常并把异常信息呈现给用户。
Building the Code代码构建
至此,根据我们写的例子,接下来我们需要构建编译。打开CMakeList.txt,在文件末尾添加下面的几行:
add_executable(tf_broadcaster src/tf_broadcaster.cpp) add_executable(tf_listener src/tf_listener.cpp) target_link_libraries(tf_broadcaster ${catkin_LIBRARIES}) target_link_libraries(tf_listener ${catkin_LIBRARIES})
Next, make sure to save the file and build the package.
$ cd %TOP_DIR_YOUR_CATKIN_WS% $ catkin_make
Running the Code(代码运行)
好的,万事俱备只欠东风!让我恩试着实际运行下吧。这部分,你需要开三个终端窗口
第一个窗口,运行core
roscore
第二个,运行 tf_broadcaster
rosrun robot_setup_tf tf_broadcaster
好的。现在,我们会在第三个窗口运行tf_listener,将从传感器坐标系获取的虚拟点,变换到机体坐标系。
rosrun robot_setup_tf tf_listener
如果一切顺利,应该会看到类似的结果。每次打印间隔1s。
[ INFO] 1248138528.200823000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138528.19 [ INFO] 1248138529.200820000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138529.19 [ INFO] 1248138530.200821000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138530.19 [ INFO] 1248138531.200835000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138531.19 [ INFO] 1248138532.200849000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138532.19
祝贺你,你已经成功的编写了一个针对平面传感器的坐标变换。下一步就是替换PointStamped,来使用真正的传感器进行操作。幸运的是,已经有相关的指导文档了here。
----在机器人上配置并使用导航综合功能包----
机器人配置
假定我们已经以特定方式配置机器人,导航功能包集将使其可以运动。上图概述了这种配置方式。白色的部分是必须且已实现的组件,灰色的部分是可选且已实现的组件,蓝色的部分是必须为每一个机器人平台创建的组件。以下章节将介绍使用导航功能包集的先决条件以及如何满足不同平台。
ROS
导航功能包集假定机器人使用ROS系统。请查阅ROS documentation以了解如何在你的机器人上安装ROS。
TF变换配置(其他变换)
导航功能包集需要机器人不断使用tf发布有关坐标系之间的关系的信息。详细的配置教程请查阅:Tf配置.
传感器信息(sensor source)
导航功能包集使用来自传感器的信息避开现实环境中的障碍物,它假定这些传感器在ROS上不断发布sensor_msgs/LaserScan消息或者sensor_msgs/PointCloud消息。有关在ROS上发布这些消息的教程,请查阅在ROS上发布传感器数据流。此外,一些已经有了ROS上的驱动的传感器亦满足这一教程。一下是部分ROS支持的传感器以及相关驱动链接:
SCIP2.2-compliant Hokuyo Laser Devices as well as the Hokuyo Model 04LX, 30LX - urg_node(英文)
SICK LMS2xx Lasers - sicktoolbox_wrapper(英文)
里程信息(odometry source)
导航功能包集需要使用tf和nav_msgs/Odometry消息发布的里程信息。这里有一篇发布里程信息的教程:在ROS上发布里程信息. 以下列出部分支持里程计的平台以及可用的驱动:
Videre Erratic: erratic_player(英文)
PR2: pr2_mechanism_controllers(英文)
基座控制器(base controller)
导航功能包集假定它可以通过话题"cmd_vel"发布geometry_msgs/Twist类型的消息,这个消息基于机器人的基座坐标系,它传递的是运动命令。这意味着必须有一个节点订阅"cmd_vel"话题, 将该话题上的速度命令(vx, vy, vtheta转换为电机命令(cmd_vel.linear.x, cmd_vel.linear.y, cmd_vel.angular.z)发送给移动基座。以下列出部分支持基座控制器的平台以及可用的驱动:
Videre Erratic: erratic_player(英文)
PR2: pr2_mechanism_controllers(英文)
地图 (map_server)
导航功能包集的配置并不需要有一张地图以供操作,但基于本教程的目的,我们假定你有一张地图。请查阅教程创建一张地图了解在你的系统环境下创建一张地图的细节。
导航功能包集配置
本节介绍如何配置导航功能包集。假设上述所有需要的环境都已满足。特别的,这意味着,机器人必须使用tf发布坐标帧,并从所有的传感器接收 sensor_msgs/LaserScan 或者 sensor_msgs/PointCloud 消息用于导航,同时需要使用 tf 和nav_msgs/Odometry 消息发布导航消息,消息会以命令的形式下发给机器人底座。如果所需要的配置你都没有,请参见机器人配置。
创建一个软件包
首先,我们创建一个软件包,用来保存我们所有的配置文件和启动文件。这个软件包需要包含所有用于实现 机器人配置小节所述以来,就如其以依赖导航功能包集高级接口 move_base 软件包一样。因此, 为你的软件包选好位置,执行以下命令:
catkin_create_pkg my_robot_name_2dnav move_base my_tf_configuration_dep my_odom_configuration_dep my_sensor_configuration_dep
这个指令会创建一个包含运行导航功能包集所需依赖的软件包。
创建机器人启动配置文件
现在,我们有了一个存放所有配置文件和启动文件的工作空间,我们会创建一个roslaunch文件来启动所有的硬件以及发布机器人所需的tf。打开你喜欢的编辑器,粘贴一下内容到my_robot_configuration.launch。你可以自由的将 "my_robot" 改成你的机器人的名字。以后,我们会对launch文件做相似的更改,确保你阅读了本节其余内容。
<launch> <node pkg="sensor_node_pkg" type="sensor_node_type" name="sensor_node_name" output="screen"> <param name="sensor_param" value="param_value" /> </node> <node pkg="odom_node_pkg" type="odom_node_type" name="odom_node" output="screen"> <param name="odom_param" value="param_value" /> </node> <node pkg="transform_configuration_pkg" type="transform_configuration_type" name="transform_configuration_name" output="screen"> <param name="transform_configuration_param" value="param_value" /> </node> </launch>
好了,现在我们有了一个launch文件模板,但是,我们需要根据自己的机器人去完善它。以下章节,我们会逐步的改变它。
<node pkg="sensor_node_pkg" type="sensor_node_type" name="sensor_node_name" output="screen"> <param name="sensor_param" value="param_value" /> </node>
这里,我们会启动机器人运行导航功能包所需的所有传感器。用你的传感器对应的ROS驱动包替换"sensor_node_pkg",用你的传感器类型替换"sensor_node_type"(通常与节点名一致),用你的传感器节点名替换"sensor_node_name","sensor_param"包含所有必需的参数。注意,如果你有多个传感器,在这里一起启动它们。
<node pkg="odom_node_pkg" type="odom_node_type" name="odom_node" output="screen"> <param name="odom_param" value="param_value" /> </node>
这里,我们启动基座(底盘)的里程计。同样,替换相应的pkg, type, name,并根据实际指定相关参数。
<node pkg="transform_configuration_pkg" type="transform_configuration_type" name="transform_configuration_name" output="screen"> <param name="transform_configuration_param" value="param_value" /> </node>
这里,我们启动相应的tf变换。同样,替换相应的pkg, type, name,并根据实际指定相关参数。
配置代价地图 (local_costmap) & (global_costmap)
导航功能包集需要两个代价地图来保存世界中的障碍物信息。一张代价地图用于 规划,在整个环境中创建长期的规划,另一个用于局部规划与避障。有一些参数两个地图都需要,而有一些则各不相同。因此,对于代价地图,有三个配置项: common配置项, global配置项和local配置项。
注意: 接下来的内容只是代价地图的基本配置项。想要查看完整的配置,参看costmap_2d文档.
共同配置(local_costmap) & (global_costmap)
导航功能包集使用代价地图存储障碍物信息。为了使这个过程更合理,我们需要指出要监听的传感器的话题,以更新数据。我们创建一个名为costmap_common_params.yaml的文件,内容如下:
obstacle_range: 2.5 raytrace_range: 3.0 footprint: [[x0, y0], [x1, y1], ... [xn, yn]] #robot_radius: ir_of_robot inflation_radius: 0.55 observation_sources: laser_scan_sensor point_cloud_sensor laser_scan_sensor: {sensor_frame: frame_name, data_type: LaserScan, topic: topic_name, marking: true, clearing: true} point_cloud_sensor: {sensor_frame: frame_name, data_type: PointCloud, topic: topic_name, marking: true, clearing: true}
好,现在我们分解以上代码。
obstacle_range: 2.5 raytrace_range: 3.0
这些参数设置放入代价地图的障碍信息的阈值。 “obstacle_range”参数决定了引入障碍物到代价地图的传感器读书的最大范围。 在这里,我们把它设定为2.5米,这意味着机器人只会更新以其底盘为中心半径2.5米内的障碍信息。 “raytrace_range”参数确定的空白区域内光线追踪的范围。 设置为3.0米意味着机器人将试图根据传感器读数清除其前面3.0米远的空间。
footprint: [[x0, y0], [x1, y1], ... [xn, yn]] #robot_radius: ir_of_robot inflation_radius: 0.55
这里我们设置机器人的footprint或机器人半径(如果是圆形的)。 指定的footprint时,机器人的中心被认为是在(0.0,0.0),顺时针和逆时针规范都支持。 我们还将设置代价地图膨胀半径。膨胀半径应该设置为障碍物产生代价的最大距离。 例如,膨胀半径设定在0.55米意味着机器人所有路径与障碍物保持0.55米或更的远离(具有同样的成本)。
observation_sources: laser_scan_sensor point_cloud_sensor
“observation_sources”参数定义了一系列传递空间信息给代价地图的传感器。每个传感器定义在下一行。
laser_scan_sensor: {sensor_frame: frame_name, data_type: LaserScan, topic: topic_name, marking: true, clearing: true}
这一行设置“observation_sources”中提到的传感器。这个例子定义了 laser_scan_sensor。 “frame_name”参数应设置为传感器坐标帧的名称,“data_type”参数应设置为LaserScan或PointCloud,这取决于主题使用的消息,“topic_name”应该设置为发布传感器数据的主题的名称。 “marking”和“clearing”参数确定传感器是否用于向代价地图添加障碍物信息,或从代价地图清除障碍信息,或两者都有。
全局配置(global_costmap)
下面我们将创建一个存储特定的全局代价地图配置选项的文件。新建一个文件:global_costmap_params.yaml并粘贴以下内容:
global_costmap: global_frame: /map robot_base_frame: base_link update_frequency: 5.0 static_map: true
“global_frame”参数定义了代价地图运行所在的坐标帧。在这种情况下,我们会选择/map frame。 “robot_base_frame”参数定义了代价地图参考的的机器地毯的坐标帧。“update_frequency”参数决定了代价地图更新的频率。 “static_map”参数决定代价地图是否根据map_server提供的地图初始化。如果你不使用现有的地图,设为false。
本地配置(local_costmap)
下面我们将创建一个存储特定的本地代价地图配置选项的文件。新建一个文件:localal_costmap_params.yaml并粘贴以下内容:
local_costmap: global_frame: odom robot_base_frame: base_link update_frequency: 5.0 publish_frequency: 2.0 static_map: false rolling_window: true width: 6.0 height: 6.0 resolution: 0.05
“global_frame”,“robot_base_frame”,“update_frequency”,“static_map”参数与全局配置意义相同。“publish_frequency”参数决定了代价地图发布可视化信息的频率。将“rolling_window”参数设置为true,意味着随着机器人在限时世界里移动,代价地图会保持以机器人为中心。“width”、“height”,“resolution”参数分别设置代价地图的宽度(米、)高度(米)和分辨率(米/单元)。 注意,这里的分辨率和你的静态地图的分辨率可能不同,但我们通常把他们设成一样的。
完整的配置选项
这里是用于启动和运行的最简单的配置,更多的细节请参阅costmap_2d 文档.
=== Base Local Planner 配置=== Base_local_planner负责根据高层规划计算速度命令并发送给机器人基座。 我们需要根据我们的机器人规格配置一些选项使其正常启动与运行。新建一个名为base_local_planner_params.yaml的文件,内容如下:
注意: 本节只涵盖TrajectoryPlanner的基本配置选项。 文档的全部选项,请参阅base_local_planner 文档.
TrajectoryPlannerROS: max_vel_x: 0.45 min_vel_x: 0.1 max_vel_theta: 1.0 min_in_place_vel_theta: 0.4 acc_lim_theta: 3.2 acc_lim_x: 2.5 acc_lim_y: 2.5 holonomic_robot: true
上面的第一部分参数定义机器人的速度限制。 第二部分定义了机器人的加速度的限制。
为导航功能包创建一个Launch启动文件
现在我们已经有了所有的配置文件,我么需要在一个启动文件中一起启动他们,创建一个名为move_base.launch的文件,内容如下:
<launch> <master auto="start"/> <!-- Run the map server --> <node name="map_server" pkg="map_server" type="map_server" args="$(find my_map_package)/my_map.pgm my_map_resolution"/> <!--- Run AMCL --> <include file="$(find amcl)/examples/amcl_omni.launch" /> <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen"> <rosparam file="$(find my_robot_name_2dnav)/costmap_common_params.yaml" command="load" ns="global_costmap" /> <rosparam file="$(find my_robot_name_2dnav)/costmap_common_params.yaml" command="load" ns="local_costmap" /> <rosparam file="$(find my_robot_name_2dnav)/local_costmap_params.yaml" command="load" /> <rosparam file="$(find my_robot_name_2dnav)/global_costmap_params.yaml" command="load" /> <rosparam file="$(find my_robot_name_2dnav)/base_local_planner_params.yaml" command="load" /> </node> </launch>
唯一需要修改的地方是更改地图服务器使指向你的已有的地图,并且,如果你有一台差分驱动的机器人,将"amcl_omni.launch"改为"amcl_diff.launch"。对于如何创建一张地图,请查阅 创建一张地图.
AMCL 配置(amcl)
AMCL有许多配置选项将影响定位的性能。 有关AMCL的更多信息请参阅amcl文档.
运行导航功能包集
现在我们配置结束,我们可以运行导航功能包了。为此我们需要在机器人上启动两个终端人。 在一个终端上,我们将启动 my_robot_configuration.launch 文件,在另一个终端上我们将启动我们刚刚创建的move_base.launch
终端1:
roslaunch my_robot_configuration.launch
终端2:
roslaunch move_base.launch
祝贺你,导航功能包集现在应该运行了。关于如何通过图形化界面给导航功能包集发送一个目标信息,请参阅rviz和导航教程。 如果你想使用代码给导航功能包集发送导航目标,请参阅发送简单导航目标教程。
故障排除
关于运行导航功能包集时遇到的常见问题,请参阅导航功能包集故障排除页面。
#keywords 移动平台配置,机器人配置,设置机器人,getting started with mobile robot
----在ROS上发布传感器数据----
在ROS上发布传感器数据流
在ROS上正确地发布从传感器获取的数据对导航功能包集的安全运行很重要。 如果导航功能包集无法从机器人的传感器接收到任何信息,那么它就会盲目行事,最有可能的是发生碰撞。 有许多传感器可用于为导航功能包集提供信息:激光、摄像头、声纳、红外线、碰撞传感器等等。然而,目前导航功能包集只接受使用sensor_msgs/LaserScan或sensor_msgs/PointCloud消息类型发布的传感器数据。下面的教程将提供典型的设置和使用这两种类型的消息的例子。 相关: TF配置
目录
- 在ROS上发布传感器数据流
- ROS消息头
- 在ROS上发布LaserScans
- LaserScan消息
- 编写代码发布一个LaserScan消息
- 在ROS上发布点云 PointClouds
- 点云消息
- 编写代码发布 PointCloud 消息
ROS消息头
消息类型 sensor_msgs/LaserScan和 sensor_msgs/PointCloud跟其他的消息一样,包括tf帧和与时间相关的信息。为了标准化发送这些信息,消息类型Header被用于所有此类消息的一个字段。
类型Header包括是哪个字段。字段seq对应一个标识符,随着消息被发布,它会自动增加。字段stamp存储与数据相关联的时间信息。以激光扫描为例,stamp可能对应每次扫描开始的时间。字段frame_id存储与数据相关联的tf帧信息。以激光扫描为例,它将是激光数据所在帧。
#Standard metadata for higher-level flow data types #sequence ID: consecutively increasing ID uint32 seq #Two-integer timestamp that is expressed as: # * stamp.secs: seconds (stamp_secs) since epoch # * stamp.nsecs: nanoseconds since stamp_secs # time-handling sugar is provided by the client library time stamp #Frame this data is associated with # 0: no frame # 1: global frame string frame_id
在ROS上发布LaserScans
LaserScan消息
对于机器人的激光扫描仪,ROS提供了一个特殊的消息类型LaserScan来存储激光信息,它位于包sensor_msgs。LaserScan消息方便代码来处理任何激光,只要从扫描仪获取的数据可以格式化为这种类型的消息。我们谈论如何生成和发布这些消息之前,让我们来看看消息本身的规范:
# # Laser scans angles are measured counter clockwise, with 0 facing forward # (along the x-axis) of the device frame # Header header float32 angle_min # start angle of the scan [rad] float32 angle_max # end angle of the scan [rad] float32 angle_increment # angular distance between measurements [rad] float32 time_increment # time between measurements [seconds] float32 scan_time # time between scans [seconds] float32 range_min # minimum range value [m] float32 range_max # maximum range value [m] float32[] ranges # range data [m] (Note: values < range_min or > range_max should be discarded) float32[] intensities # intensity data [device-specific units]
正如所期望的,上面的名字/注释明确表述了消息里的各个字段。为了更具体的说明,我们来写一个简单的激光数据发布器来展示他们是如何工作的。
编写代码发布一个LaserScan消息
在ROS上发布一个LaserScan消息是相当简单的。我们先提供下面的示例代码,然后将代码分解逐行。
1 #include <ros/ros.h> 2 #include <sensor_msgs/LaserScan.h> 3 4 int main(int argc, char** argv){ 5 ros::init(argc, argv, "laser_scan_publisher"); 6 7 ros::NodeHandle n; 8 ros::Publisher scan_pub = n.advertise<sensor_msgs::LaserScan>("scan", 50); 9 10 unsigned int num_readings = 100; 11 double laser_frequency = 40; 12 double ranges[num_readings]; 13 double intensities[num_readings]; 14 15 int count = 0; 16 ros::Rate r(1.0); 17 while(n.ok()){ 18 //generate some fake data for our laser scan 19 for(unsigned int i = 0; i < num_readings; ++i){ 20 ranges[i] = count; 21 intensities[i] = 100 + count; 22 } 23 ros::Time scan_time = ros::Time::now(); 24 25 //populate the LaserScan message 26 sensor_msgs::LaserScan scan; 27 scan.header.stamp = scan_time; 28 scan.header.frame_id = "laser_frame"; 29 scan.angle_min = -1.57; 30 scan.angle_max = 1.57; 31 scan.angle_increment = 3.14 / num_readings; 32 scan.time_increment = (1 / laser_frequency) / (num_readings); 33 scan.range_min = 0.0; 34 scan.range_max = 100.0; 35 36 scan.ranges.resize(num_readings); 37 scan.intensities.resize(num_readings); 38 for(unsigned int i = 0; i < num_readings; ++i){ 39 scan.ranges[i] = ranges[i]; 40 scan.intensities[i] = intensities[i]; 41 } 42 43 scan_pub.publish(scan); 44 ++count; 45 r.sleep(); 46 } 47 }
现在我们将上面的代码一步一步分解。
2 #include <sensor_msgs/LaserScan.h> 3
在这里,我们include我们想要发布的sensor_msgs/LaserScan消息。
8 ros::Publisher scan_pub = n.advertise<sensor_msgs::LaserScan>("scan", 50);
这段代码创建了一个ros::Publisher用于使用ROS发送 LaserScan 消息。
10 unsigned int num_readings = 100; 11 double laser_frequency = 40; 12 double ranges[num_readings]; 13 double intensities[num_readings];
这里我们创建一组存储虚拟数据的变量,用来模拟激光扫描(其中填充所扫描到的障碍物信息)。实际的程序应从他们的激光驱动程序获取这些数据。
18 //generate some fake data for our laser scan 19 for(unsigned int i = 0; i < num_readings; ++i){ 20 ranges[i] = count; 21 intensities[i] = 100 + count; 22 } 23 ros::Time scan_time = ros::Time::now();
填充激光数据,填充值每秒加1.
25 //populate the LaserScan message 26 sensor_msgs::LaserScan scan; 27 scan.header.stamp = scan_time; 28 scan.header.frame_id = "laser_frame"; 29 scan.angle_min = -1.57; 30 scan.angle_max = 1.57; 31 scan.angle_increment = 3.14 / num_readings; 32 scan.time_increment = (1 / laser_frequency) / (num_readings); 33 scan.range_min = 0.0; 34 scan.range_max = 100.0; 35 36 scan.ranges.resize(num_readings); 37 scan.intensities.resize(num_readings); 38 for(unsigned int i = 0; i < num_readings; ++i){ 39 scan.ranges[i] = ranges[i]; 40 scan.intensities[i] = intensities[i]; 41 }
创建一个 scan_msgs::LaserScan 消息,并使用我们预先生成的数据填充,准备发布它。
43 scan_pub.publish(scan);
在ROS上发布这个消息。
在ROS上发布点云 PointClouds
点云消息
为了存储与分享世界中的一系列点, ROS 提供了 sensor_msgs/PointCloud 消息。正如前文所述,这个消息支持将三维空间中的点的数组以及任何保存在一个信道中的相关数据。 例如,一条带有"intensity"信道的 PointCloud 可以保持点云数据中每一个点的强度。接下来我们使用ROS发布一个 PointCloud 来探索这个过程。
#This message holds a collection of 3d points, plus optional additional information about each point. #Each Point32 should be interpreted as a 3d point in the frame given in the header Header header geometry_msgs/Point32[] points #Array of 3d points ChannelFloat32[] channels #Each channel should have the same number of elements as points array, and the data in each channel should correspond 1:1 with each point
编写代码发布 PointCloud 消息
使用ROS发布 PointCloud 相当简单.接下来,我们给出一个完整的例子,并详细的讨论他的每一个细节.
1 #include <ros/ros.h> 2 #include <sensor_msgs/PointCloud.h> 3 4 int main(int argc, char** argv){ 5 ros::init(argc, argv, "point_cloud_publisher"); 6 7 ros::NodeHandle n; 8 ros::Publisher cloud_pub = n.advertise<sensor_msgs::PointCloud>("cloud", 50); 9 10 unsigned int num_points = 100; 11 12 int count = 0; 13 ros::Rate r(1.0); 14 while(n.ok()){ 15 sensor_msgs::PointCloud cloud; 16 cloud.header.stamp = ros::Time::now(); 17 cloud.header.frame_id = "sensor_frame"; 18 19 cloud.points.resize(num_points); 20 21 //we'll also add an intensity channel to the cloud 22 cloud.channels.resize(1); 23 cloud.channels[0].name = "intensities"; 24 cloud.channels[0].values.resize(num_points); 25 26 //generate some fake data for our point cloud 27 for(unsigned int i = 0; i < num_points; ++i){ 28 cloud.points[i].x = 1 + count; 29 cloud.points[i].y = 2 + count; 30 cloud.points[i].z = 3 + count; 31 cloud.channels[0].values[i] = 100 + count; 32 } 33 34 cloud_pub.publish(cloud); 35 ++count; 36 r.sleep(); 37 } 38 }
接下来,我们一句一句来看.
2 #include <sensor_msgs/PointCloud.h> 3
包含 sensor_msgs/PointCloud 消息头文件.
8 ros::Publisher cloud_pub = n.advertise<sensor_msgs::PointCloud>("cloud", 50);
创建我们用来发布Creat PointCloud 消息的 ros::Publisher .
15 sensor_msgs::PointCloud cloud; 16 cloud.header.stamp = ros::Time::now(); 17 cloud.header.frame_id = "sensor_frame";
填充 PointCloud 消息的头:frame 和 timestamp.
19 cloud.points.resize(num_points);
设置点云的数量.
21 //we'll also add an intensity channel to the cloud 22 cloud.channels.resize(1); 23 cloud.channels[0].name = "intensities"; 24 cloud.channels[0].values.resize(num_points);
增加信道 "intensity" 并设置其大小,使与点云数量相匹配.
26 //generate some fake data for our point cloud 27 for(unsigned int i = 0; i < num_points; ++i){ 28 cloud.points[i].x = 1 + count; 29 cloud.points[i].y = 2 + count; 30 cloud.points[i].z = 3 + count; 31 cloud.channels[0].values[i] = 100 + count; 32 }
使用虚拟数据填充 PointCloud 消息.同时,使用虚拟数据填充 intensity 信道.
34 cloud_pub.publish(cloud);
使用 ROS 发布 PointCloud 消息.
----附----
概述
导航堆栈在概念级别上是相当简单的。它接收来自里程计和传感器流的信息,并输出速度命令以发送到移动基站。但是,在任意机器人上使用导航堆栈有点复杂。作为导航堆栈使用的先决条件,机器人必须运行ROS,具有tf变换树,并使用正确的ROS消息类型发布传感器数据。此外,导航堆栈需要被配置为机器人的形状和动力学在高水平执行。为了帮助这个过程,本手册旨在作为典型的导航堆栈设置和配置的指南。
硬件要求
虽然导航堆栈设计为尽可能通用,但有三个主要硬件要求限制其使用:
- 它仅适用于差速驱动和完整轮式机器人。它假定移动基站通过发送期望的速度命令来控制,以便以下列形式实现:x速度,y速度,θ速度。
- 它需要安装在移动基座上某处的平面激光器。该激光器用于地图构建和本地化。
- 导航堆栈是在方形机器人上开发的,因此其性能将是最接近正方形或圆形的机器人。它在任意形状和尺寸的机器人上工作,但是在狭窄空间(如门口)中的大型矩形机器人可能有困难。
文档
以下文档假定熟悉机器人操作系统。关于ROS的文档可以在这里找到:ROS文档
导航堆栈设置
发布传感器流
发布Odometry信息
变换配置
构建地图
报告错误
列出活动票证
打开新票证
例子
使用导航堆栈的机器人列表
教程
- 在模拟中导航
这个pagge描述真棒模拟
- 用真正的机器人导航
本页描述了真实机器人的导航
- 为TurtleBot设置导航堆栈
提供了机器人导航配置的第一瞥,参考其他更全面的教程。
- 为TurtleBot设置导航堆栈
提供了机器人导航配置的第一瞥,参考其他更全面的教程。
- 赫斯基移动基地演示
使用基本move_base设置运行Husky ,没有映射或本地化。
- 探索周边地区并制作地图
从机器人的视觉探索真实的环境,并保存地图。
- 使用已知地图导航
在已知区域中使用先前保存的地图来淹没
- 赫斯基AMCL演示
使用move_base设置运行Husky ,使用amcl进行本地化。
- 赫斯基Gmapping演示
使用move_base设置运行Husky ,使用gmapping映射和本地化(SLAM)。
- 赫斯基边境勘探演示
使用move_base设置运行Husky ,使用frontier_exploration进行勘探规划,gmapping用于映射和本地化(SLAM)。
- 探索周边地区并制作地图
从机器人的视野探索环境,并保存地图。
- 使用已知地图导航
在已知区域中使用先前保存的地图来淹没。
- SLAM地图大厦与TurtleBot
如何使用gmapping生成地图
- 使用TurtleBot自动导航已知地图
本教程介绍如何使用TurtleBot与以前已知的地图。
- SLAM地图大厦与TurtleBot
如何使用gmapping生成地图
- 使用TurtleBot自动导航已知地图
本教程介绍如何使用TurtleBot与以前已知的地图。
- 使用tf设置您的机器人
本教程提供了设置您的机器人开始使用tf的指南。
- Evarobot的导航在凉亭的
如何使用以前已知的地图导航在凉亭的evarobot。
- 基本导航调整指南
本指南旨在给出一些关于如何调整机器人上的ROS导航堆栈的标准建议。本指南并不全面,但应该给出一些洞察过程。我也鼓励人们,确保他们已经阅读ROS导航教程之前,这篇文章,因为它提供了一个很好的概述设置导航堆栈在机器人wheras本指南只是提供建议的过程。
- 编写全局路径规划器作为ROS中的插件
在本教程中,我将介绍在ROS中编写和使用全局路径规划器的步骤。首先要知道的是,为ROS添加一个新的全局路径规划器,新的路径规划器必须遵守在nav_core包中定义的nav_core :: BaseGlobalPlanner C ++接口。一旦编写了全局路径规划器,它必须作为插件添加到ROS中,以便它可以被move_base包使用。在本教程中,我将提供从编写路径规划器类开始直到将其部署为插件的所有步骤。我将使用Turtlebot作为机器人的一个例子来部署新的路径规划器。有关如何将真实GA计划程序集成为ROS插件的教程,请参阅在ROS中添加遗传算法全局路径规划器作为插件。
- 无题
没有说明
- 无题
没有说明
- 机器人上的导航堆栈的设置和配置
本教程提供了如何获取在机器人上运行的导航堆栈的分步说明。涵盖的主题包括:使用tf发送转换,发布里程计信息,通过ROS从激光器发布传感器数据,以及基本导航堆栈配置。
- Gazebo'da Evarobot Navigasyonu
ÇıkartılmışharitaüzerindenGazebo'da otonom Evarobot navigasyonu。
- Bilinen Bir Haritada Otonom Evarobot Navigasyonu
Dahaöncedençıkartılmışharitada otonom robot navigasyonu。
- 在导航堆栈中使用rviz
本教程提供了使用rviz与导航堆栈初始化本地化系统,发送目标到机器人,以及查看导航堆栈通过ROS发布的许多可视化的指南。
- 通过ROS发布Odometry信息
本教程提供了一个为导航堆栈发布里程测量信息的示例。它包括通过ROS发布nav_msgs / Odometry消息,以及通过tf从“odom”坐标框架到“base_link”坐标框架的转换。
- 在ROS上发布传感器流
本教程提供了通过ROS 发送两种类型的传感器流(sensor_msgs / LaserScan消息和sensor_msgs / PointCloud消息)的示例。
- 将目标发送到导航堆栈
导航堆栈用于将移动基座从一个位置驱动到另一个位置,同时安全地避开障碍物。通常,机器人被赋予使用诸如rviz的预先存在的工具结合地图移动到目标位置的任务。例如,为了告诉机器人去特定的办公室,用户可以在地图中点击办公室的位置,并且机器人将试图去那里。然而,能够发送机器人目标以使用代码移动到特定位置也是重要的,很像rviz在引擎盖下。例如,用于插入机器人的代码可以首先检测插座,然后告诉机器人驱动到距离墙壁一英尺的位置,然后尝试使用臂将插头插入插座。本教程的目标是提供一个从用户代码发送导航堆栈简单目标的示例。
- 安装
安装和编译此软件包的说明
- Evarobot探索
如何使用frontier_exploration自动生成SLAM映射与Evarobot
- 使用Evarobot自动导航已知地图
如何自主导航Evarobot与已知地图。
相关应用
2D导航PR2平台
相关刊物
办公室马拉松:在室内办公环境中的鲁棒导航
机器人使用ROS
下面机器人的硬件平台使用,或者可以与ROS软件一起使用。为了你的机器人添加到该页面,您可以按照该指令。
目录
- 机器人使用ROS
- 门户
- 视频蒙太奇
- 完整列表
- 移动机械臂
- 自定义移动机械臂
- 移动机器人
- 定制的移动机器人
- 机械手
- 自动驾驶汽车
- 社交机器人
- 人形
- 无人机
- 水下机器人
- UWVs
- 其他
- 移动机械臂
门户
src="http://player.vimeo.com/video/146183080" width="480" height="270" frameborder="0" allowfullscreen="" style="box-sizing: border-box;">
还有的ROS.org博客了一系列利用ROS机器人。
完整列表
移动机械臂
阿西莫夫机器人的X Terrabot
Austrobotics低成本臂
科雷尔实验室的草原土拨鼠
DARPA / RE2 ARM机器人
Dataspeed公司:移动基地
机器人捷豹博士4×4轮式用机械手臂
机器人捷豹V4博士与机械手臂
取机器人:取
弗劳恩霍夫IPA护理-O-BOT 3
Hekateros臂(RoadNarrows)
川田HRP2-V
谁Youbot
MEKA M1移动机械手
神网SentiBotics
PAL机器人REEM
Willow Garage的PR2
自定义移动机械臂
博世RTC的机器人
CMU /英特尔的HERB
康奈尔熊猫,极地蓝
DFKI-不来梅的AILA
佐治亚理工学院的EL-E和科迪
英特尔西雅图超音速队的马文
麦克斯韦(Kinect的+ Dynamixel)
丕机器人
斯坦福大学的STAIR 1
恩/ E的朋友
TUM-罗西
移动机器人
所有AdeptMobileRobots(ActivMedia)机器人:先锋2/3先锋(DX和AT),AmigoBot,PowerBot,PeopleBot,PatrolBot,小的Seekur,的Seekur,先锋LX。
Austrobotics室外机器人
Bilibot(创建+ Kinect的)
蓝机器人BlueROV
的ClearPath机器人赫斯基A100和A200赫斯基
大族电机机器人
CoroWare Corobot
iRobot公司创建:科雷尔实验室,布朗,create_autonomy
蟹(六足机器人)
凯斯西储大学的HARLIE
机器人捷豹博士4×4轮式平台(加拿大)
机器人捷豹博士精简版平台(加拿大)
机器人捷豹博士V2平台(加拿大)
博士 捷豹V4的平台论坛机器人(加拿大)
Eddiebot =视差埃迪平台+ API的Turtlebot
Enova中机器人 - 彩扩
EvArobot
费斯托说教Robotino
取机器人:货运
Gosti爵士
HSR(丰田,人力支持机器人)
Ingeniarius ForteRC
Innok英雄
iRobot公司B21r(洗-U)
RoboTiCan犰狳
RoboTiCan科莫多
RoboTiCan Lizi
Mecanumbot
梅林miabotPro
MetraLabs Scitos G5(UUISRC)
鸢属ATR机器人
鸢属机器人MRP2
NEATO XV-11(奥尔巴尼大学)
Neobotix MP-500
Neobotix和珍贵-500
Neobotix和珍贵-700
QBO
Rhoeby(导航功能的昆虫)
RoadNarrows“厉
机器人制造商VIONA
RoboSavvy自平衡平台
Robotnik AGVS
Robotnik GUARDIAN
Robotnik SUMMIT XL
RoombaRobin = iRobot公司的Roomba +自定义堆栈+ Turtlebot的API
斯坦利创新V3与赛格威RMP
VIDERE不稳定:亚利桑那大学,华盛顿-U
Willow Garage的Texai
WowWee公司罗维奥
Xaxxon魔环总理
定制的移动机器人
Andruino
犰狳(ArbotiX /钒实验室)
ELEKTRON机器人(华沙理工大学)
Enova中机器人 - 彩扩
ETH蟹
ETH Magnebike
ETH重新调零
ETH RoBoxer
MyzharBot -履带式机器人研究传感器融合算法
丕机器人
罗格斯伊尔玛
TraxBot(MRL-ISR,科英布拉大学)
TUM LSR的自治城市资源管理器(ACE)
舍布鲁克大学AZIMUT
舍布鲁克大学遥操作机器人
WheeledRobin =两轮平台+ API的Turtlebot
机械手
快板手(SIMLAB):16DOF电流控制机器人手
巴雷特WAM
巴克斯特(反思机器人)
ClamArm - CU博尔德
Cyton伽玛
Eddiebot =视差埃迪平台+ API的Turtlebot
Hekateros臂(RoadNarrows)
基于M3 MEKA机器人:A2 ARM兼容,H2标准手和T2人形躯干
开放式机组机器人:我们的
Aubo机器人I5:aubo_robot
ABB IRB 2400,IRB 5400,IRB 6640:ROS-ABB工业
善于蝰蛇650:ROS-工业娴熟
FANUC M-10iA,M-16iB,M-20iA,M-430iA,LR伴侣200iC,LR伴侣200ID:ROS-FANUC工业
莫托曼MH5,SIA5D,SIA10D,SIA20D:ROS-工业莫托曼
通用机器人UR5,UR10:ROS-工业通用机器人
Neuronics卡塔纳450手臂:SAIL,UOS
崇德ACT 3
雄克SDH
崇德WSG 50夹具(与魏斯机器人手指FMF)
暗影灵巧手
CrustCrawler AX-12:亚利桑那大学,斯坦福大学
ROBOTIS机械臂-H
自动驾驶汽车
技术除雪/ Autorally平台
CPFL Autoware
宝马研究
U-亚利桑那/ CAT车辆
Dataspeed公司:ADAS开发车载工具包
斯坦福大学的少年(唯一障碍分类)
美国西南研究院/马蒂
德克萨斯大学奥斯汀分校/ ART的马文
社交机器人
Ingeniarius ForteRC
张曼玉(机器人实验室,马德里卡洛斯三世大学)
鸢属机器人罗宾
伦锡LAM
人形
毕宿五垴(布朗,弗莱堡大学)
辣椒艾尔帕兰
毕宿五罗密欧
BipedRobin
吕(开源)
PAL机器人REEM-C
Roboard W /近藤KHR类人生物(Veltrop)
RoboMotio雷迪(伍斯特理工学院)
Robonaut 2(NASA-GM)
TU / E TULIP
ROBOTIS THORMANG3
无人机
AscTec鹈鹕和蜂鸟Quadrotors
伯克利STARMAC
Bitcraze Crazyflie
DJI M100 / M600板载SDK支持ROS
厄尔 - 直升机
ETH sFly
鹦鹉AR-无人机
鹦鹉波普无人机
宾夕法尼亚大学的AscTec蜂鸟Quadrotors
PIXHAWK微型飞行器
Skybotix哄直升机
水下机器人
赫瑞 - 瓦特大学的尼斯湖水怪V
赫罗纳大学,赫罗纳500 AUV
南加州大学的SeaBee3
雾气IITK
UWVs
的ClearPath机器人翠鸟
ETH Hyraii
ETH Limnobotics
其他
Ardros
CSIRO山猫
厄尔脑
慧鱼机器人浏览器
K'nex黑客
乐高EV3
乐高NXT
佩恩Modlab的CKBots
QBO
TECNALIA / Pukas SurfSens冲浪板
UWU_Bot
----End----
ROS专题----导航功能包navigation基础汇总相关推荐
- ROS示例----导航功能包Husky_exploration
ROS导航功能包示例husky amcl gmapping slam exploration 此功能包包含如下文件: 结构如下: $ tree -L 2 . ├── CMakeLists.txt -& ...
- ROS联合webots实战案例(五)导航功能包入门2
导航功能包入门2 为了能和读者进一步讨论问题,建立了一个微信群,方便给大家解答问题,也可以一起讨论问题. 加群链接 注意: 再学习本系列教程时,应该已经安装过ROS了并且需要有一些ROS的基本知识 本 ...
- ROS专题----tf和tf2坐标变换
ROS专题----tf和tf2坐标变换 ---- 工作区设置 如果您尚未创建用于完成教程的工作区, 请单击此处查看一些简要说明 . 从tf1迁移到tf2 转换数据类型 这是对转换数据类型的语法更改的快 ...
- 对Navigation基础的了解
对Navigation基础的了解 在尝试完成用AMCL来导航和避障后,下面对Navigation的理解作下总结.本篇的主要内容包括:控制架构,三个接口,tf变换,AMCL,move_base参数解析. ...
- 安装ROS、gazebo、PX4基础细节及offboard控制
新手参考教程安装ROS.gazebo.PX4基础细节及offboard控制 1.安装ROS 参考教程 2.安装PX4 参考教程 注: 1.在编译px4_Firmware前会经过安装步骤,安装需要去gi ...
- 【电子健康档案专题】电子健康档案基础知识及问答精华
电子健康档案相关知识问答汇总: 电子健康档案的为什么? 建立电子健康档案有什么好处? 拥有电子健康档案有什么好处? 为什么要建立健康档案? 电子健康档案如何建立? 社区卫生健康档案如何建立电子档案? ...
- ROS学习手记 - 2.1: Create and Build ROS Package 生成包(Python)
ROS学习手记 - 2.1: Create and Build ROS Package 生成包(Python) ROS学习手记 - 2.1: Create and Build ROS Package ...
- Python编程基础:第六节 math包的基础使用Math Functions
第六节 math包的基础使用 前言 实践 前言 我们通常会对数值型变量进行计算,这里我们给出一些常用的函数用于辅助你的计算过程.常用的数学计算函数均在math包. 实践 首先我们导入math包,并定义 ...
- 华为HCNE专题一:网络基础知识
华为HCNE专题一:网络基础知识 对应章节:第一章 重点:理解网络结构,掌握OSI参考模型的基本结构 难点:OSI各层次名字及其概念的理解 亮点/应用/重要性:网络入门的基础,重中之重. 主要内容:针 ...
- 创建ros的程序包--3
创建ros的程序包(原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/) 1.一个catkin程序包由什么组成? 一个程序包要想称为catkin程序 ...
最新文章
- js实现给html固定区域增加水印
- Webserver管理系列:11、注意默认的隐含共享
- Json and Go
- mysql数据库存储过程及调用方法
- 【bfs】WZK旅游(jzoj 1996)
- 2299 Ultra-QuickSort(归并)
- 学生管理系统Java版
- html文件打开多出很多数字,【求助】页面上显示几个数字,打开html的时候希望能滚动起来...
- ThinkPHP-保存生成的二维码
- Python标准库base64用法简介
- java知识总结-25
- 你还在 Docker 中跑 MySQL?恭喜你,好下岗了!
- ercharts一个页面能放几个_echarts 一个页面多个节点共用一个图表实例
- 【算法】路径规划中的Dijkstra(狄克斯特拉)与A星算法
- 实时单目物体SLAM Real-time Monocular Object SLAM
- 解决爱快+openwrt双软路由,爱快设备列表中mac地址全部为openwrt的mac的问题
- php-fpm master 关系,php-fpm master worker 关系介绍
- 腾讯2016实习生招聘后台研发面试经
- 为什么要参加hadoop培训
- 深入剖析https原理——加密,完整与端点鉴别
热门文章
- java sql语句中文乱码_PL/SQL执行语句中文乱码
- google开发者大会的倒计时动画,没有用Flash
- 微软 Access数据库操作 中文教程
- Java重命名文件的方法_java重命名文件(附道客巴巴文档下载方法)
- c语言小游戏编程弹珠游戏,C/C++知识点之c语言 弹弹球小游戏
- 这应该是把计算机网络五层模型讲的最好是文章了,看不懂你打我
- html5微博个人资料页面,仿新浪微博个人主页html网站模板
- 偶尔文艺-关于想要转录MIDI从YAMAHA到MAC
- html右下角图片广告,HTML580用纯JS实现右下角广告代码
- acdsee免费版跳过注册账户_加快Win 10启动速度,直接跳过锁屏登录界面