ROS : Navigation 基于碰撞传感器、悬崖传感器的实时避障 [kobuki]

  • 话题消息
    • 碰撞传感器
    • 悬崖传感器
    • 点云数据
    • 传感器详细信息
  • 基于碰撞传感器、悬崖传感器的实时避障
  • 转点云数据源码解读
    • kobuki_bumper2pc.cpp
      • Bumper2PcNodelet::onInit
      • Bumper2PcNodelet::coreSensorCB
    • standalone.launch

话题消息

碰撞传感器

~events/bumper ( kobuki_msgs/BumperEvent ) 提供一个保险杠事件,每当按下或释放保险杠时都会发生此事件。

~$ rostopic info /mobile_base/events/bumper
Type: kobuki_msgs/BumperEventPublishers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)Subscribers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)~$ rosmsg show kobuki_msgs/BumperEvent
uint8 LEFT=0
uint8 CENTER=1
uint8 RIGHT=2
uint8 RELEASED=0
uint8 PRESSED=1
uint8 bumper
uint8 state~$ rostopic echo /mobile_base/events/bumper
bumper: 1  // 左前 0 ,正前 1 ,右前 2
state: 1  // 保险杠按压(撞击)为 1 ,未撞击为 0
---
bumper: 1
state: 0
---

悬崖传感器

~events/cliff ( kobuki_msgs/CliffEvent ) 提供悬崖传感器事件,每当机器人接近或离开悬崖时就会发生。

~$ rostopic info /mobile_base/events/cliff
Type: kobuki_msgs/CliffEventPublishers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)Subscribers: None* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)~$ rosmsg info kobuki_msgs/CliffEvent
uint8 LEFT=0
uint8 CENTER=1
uint8 RIGHT=2
uint8 FLOOR=0
uint8 CLIFF=1
uint8 sensor
uint8 state
uint16 bottom~$ rostopic echo /mobile_base/events/cliff---
sensor: 1
state: 1
bottom: 616
---
sensor: 2
state: 1
bottom: 571
---
sensor: 2
state: 0
bottom: 544
---

点云数据

~sensors/bumper_pointcloud ( sensor_msgs/PointCloud2 ) 触发碰撞传感器、悬崖传感器事件时转 点云数据 ,代码见 kobuki_bumper2pc.cpp 。

# The point cloud data may be organized 2d (image-like) or 1d
# (unordered). Point clouds organized as 2d images may be produced by
# camera depth sensors such as stereo or time-of-flight.# Time of sensor data acquisition, and the coordinate frame ID (for 3d
# points).
Header header# 2D structure of the point cloud. If the cloud is unordered, height is
# 1 and width is the length of the point cloud.
uint32 height
uint32 width# Describes the channels and their layout in the binary data blob.
PointField[] fieldsbool    is_bigendian # Is this data bigendian?
uint32  point_step   # Length of a point in bytes
uint32  row_step     # Length of a row in bytes
uint8[] data         # Actual point data, size is (row_step*height)bool is_dense        # True if there are no invalid points
  • height 与 width 是指点云数据的高和宽,一般无序点云的高为 1 ,宽为点云中激光点的个数;结构化点云的高和宽均大于 1 。

  • sensor_msgs/PointField[] fields 由以下组成:

    # This message holds the description of one point entry in the
    # PointCloud2 message format.
    uint8 INT8    = 1
    uint8 UINT8   = 2
    uint8 INT16   = 3
    uint8 UINT16  = 4
    uint8 INT32   = 5
    uint8 UINT32  = 6
    uint8 FLOAT32 = 7
    uint8 FLOAT64 = 8string name      # Name of field
    uint32 offset    # Offset from start of point struct
    uint8  datatype  # Datatype enumeration, see above
    uint32 count     # How many elements in the field
    
    • name 是指点云包含的域的名称,如 x 、 y 、 z 、 intensity 、 ring 等;
    • offset 是指在 data 的每个数据中该域的偏移量;
    • datatype 是指以上 1~8 的数据类型;
    • count 是指该域有多少个元素,一般为 1 。
    • 每一帧点云均有该项数据。
  • is_bigendian 数据是大端存储还是小端存储的标志。

  • pointstep 表示每个点的字节长度,常见的为 32 。

  • rowstep 表示每行的字节长度。

  • data 表示所有的点的数据,以字节存储。 uint8[] data 代表 vector 类型,有 size 、 push_back 、 clear 等操作。

  • is_dense 若是 true ,代表点云数据中不包含无效点( nan 点);若是 false ,代表点云中包含无效点。

~$ rostopic info /mobile_base/sensors/bumper_pointcloud
Type: sensor_msgs/PointCloud2Publishers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)Subscribers:* /move_base (http://10.42.0.1:42621/)~$ rosmsg show sensor_msgs/PointCloud2
std_msgs/Header headeruint32 seqtime stampstring frame_id
uint32 height
uint32 width
sensor_msgs/PointField[] fieldsuint8 INT8=1uint8 UINT8=2uint8 INT16=3uint8 UINT16=4uint8 INT32=5uint8 UINT32=6uint8 FLOAT32=7uint8 FLOAT64=8string nameuint32 offsetuint8 datatypeuint32 count
bool is_bigendian
uint32 point_step
uint32 row_step
uint8[] data
bool is_dense~$ rostopic echo /mobile_base/sensors/bumper_pointcloud
header:  // 点云的头信息seq: 0stamp:  // 时间戳secs: 1455218508nsecs: 292744004frame_id: "/base_link"
height: 1  // 如果 cloud 是无序的,则 height 是 1
width: 3  // 点云的长度
fields:  // sensor_msgs/PointField[] fields-name: "x"offset: 0datatype: 7count: 1-name: "y"offset: 4datatype: 7count: 1-name: "z"offset: 8datatype: 7count: 1
is_bigendian: False
point_step: 12  // 一个点占的比特数
row_step: 36  // 一行的长度占用的比特数
data: [221, 206, 8, 66, 67, 240, 187, 66, 10, 215, 35, 61, 143, 194, 117, 62, 0, 0, 0, 0, 10, 215, 35, 61, 221, 206, 8, 66, 67, 240, 187, 194, 10, 215, 35, 61]  // Actual point data, size is (row_step*height) 这部分数据是点云的二进制数据流,必须要单独解析,直接读取没有任何意义
is_dense: True  // True 代表没有非法数据点
---
header:seq: 1stamp:secs: 1455218508nsecs: 347700570frame_id: "/base_link"
height: 1
width: 3
fields:-name: "x"offset: 0datatype: 7count: 1-name: "y"offset: 4datatype: 7count: 1-name: "z"offset: 8datatype: 7count: 1
is_bigendian: False
point_step: 12
row_step: 36
data: [221, 206, 8, 66, 67, 240, 187, 66, 10, 215, 35, 61, 143, 194, 117, 62, 0, 0, 0, 0, 10, 215, 35, 61, 221, 206, 8, 66, 67, 240, 187, 194, 10, 215, 35, 61]
is_dense: True
---

传感器详细信息

~sensors/core ( kobuki_msgs/SensorState ) kobuki 整体传感器详细信息, 50Hz 。

note :

kobuki_bumper2pc.cpp 是利用该话题消息将碰撞传感器和悬崖传感器数据转点云数据的。主要利用了 uint8 bumper 和 uint8 cliff , 这两个值在多个传感器触发时会将各个传感器对应的状态值求和得到。

~$ rostopic info /mobile_base/sensors/core
Type: kobuki_msgs/SensorStatePublishers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)Subscribers:* /mobile_base_nodelet_manager (http://10.42.0.1:44834/)~$ rosmsg info kobuki_msgs/SensorState
uint8 BUMPER_RIGHT=1
uint8 BUMPER_CENTRE=2
uint8 BUMPER_LEFT=4
uint8 WHEEL_DROP_RIGHT=1
uint8 WHEEL_DROP_LEFT=2
uint8 CLIFF_RIGHT=1
uint8 CLIFF_CENTRE=2
uint8 CLIFF_LEFT=4
uint8 BUTTON0=1
uint8 BUTTON1=2
uint8 BUTTON2=4
uint8 DISCHARGING=0
uint8 DOCKING_CHARGED=2
uint8 DOCKING_CHARGING=6
uint8 ADAPTER_CHARGED=18
uint8 ADAPTER_CHARGING=22
uint8 OVER_CURRENT_LEFT_WHEEL=1
uint8 OVER_CURRENT_RIGHT_WHEEL=2
uint8 OVER_CURRENT_BOTH_WHEELS=3
uint8 DIGITAL_INPUT0=1
uint8 DIGITAL_INPUT1=2
uint8 DIGITAL_INPUT2=4
uint8 DIGITAL_INPUT3=8
uint8 DB25_TEST_BOARD_CONNECTED=64
std_msgs/Header headeruint32 seqtime stampstring frame_id
uint16 time_stamp
uint8 bumper
uint8 wheel_drop
uint8 cliff
uint16 left_encoder
uint16 right_encoder
int8 left_pwm
int8 right_pwm
uint8 buttons
uint8 charger
uint8 battery
uint16[] bottom
uint8[] current
uint8 over_current
uint16 digital_input
uint16[] analog_input# 碰撞传感器
~$ rostopic echo /mobile_base/sensors/core
---
header:seq: 13711stamp:secs: 1455208898nsecs: 480690468frame_id: ''
time_stamp: 38084
bumper: 4
wheel_drop: 0
cliff: 0
left_encoder: 5062
right_encoder: 5013
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 156
bottom: [1572, 1895, 1633]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---
header:seq: 13754stamp:secs: 1455208900nsecs: 856141130frame_id: ''
time_stamp: 40464
bumper: 6
wheel_drop: 0
cliff: 0
left_encoder: 5046
right_encoder: 5012
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 156
bottom: [1573, 1893, 1653]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---
header:seq: 13757stamp:secs: 1455208900nsecs: 996265722frame_id: ''
time_stamp: 40604
bumper: 7
wheel_drop: 0
cliff: 0
left_encoder: 5044
right_encoder: 5012
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 156
bottom: [1584, 1898, 1634]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---# 悬崖传感器
~$ rostopic echo /mobile_base/sensors/core
---
header:seq: 20113stamp:secs: 1455209221nsecs: 398578496frame_id: ''
time_stamp: 33904
bumper: 0
wheel_drop: 3
cliff: 1
left_encoder: 5333
right_encoder: 5051
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 156
bottom: [383, 636, 666]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---
header:seq: 20111stamp:secs: 1455209221nsecs: 316537941frame_id: ''
time_stamp: 33824
bumper: 0
wheel_drop: 3
cliff: 3
left_encoder: 5333
right_encoder: 5051
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 156
bottom: [213, 519, 1202]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---
header:seq: 20175stamp:secs: 1455209224nsecs: 513620609frame_id: ''
time_stamp: 37024
bumper: 0
wheel_drop: 3
cliff: 7
left_encoder: 5333
right_encoder: 5051
left_pwm: 0
right_pwm: 0
buttons: 0
charger: 0
battery: 155
bottom: [191, 149, 78]
current: [0, 0]
over_current: 0
digital_input: 8
analog_input: [4095, 4095, 4095, 4095]
---

基于碰撞传感器、悬崖传感器的实时避障

通过碰撞传感器、悬崖传感器实时添加障碍物的方式是:通过修改 global_costmap 或者 local_costmap 配置文件,调用 Costmap 的障碍物层(也就是,自定义创建 costmap layer 的最后一步, costmap_2d 功能包已经完成前面几步)。示例如下:

  • 修改 costmap_common_params_nav.yaml 文件,在障碍物层添加障碍数据来源 bump ,指定数据类型是 PointCloud2 ,指定话题为 mobile_base/sensors/bumper_pointcloud 。

    由于触发碰撞传感器、悬崖传感器事件后均转为 topic 为 /mobile_base/sensors/bumper_pointcloud 的点云数据,因此,这里指定的数据来源 bump 不仅仅是碰撞传感器的,还包括悬崖传感器的。

    # ...obstacle_layer:# ...observation_sources:  scan bumpscan:data_type: LaserScantopic: scan# ...bump:data_type: PointCloud2topic: mobile_base/sensors/bumper_pointcloudmarking: trueclearing: truemin_obstacle_height: 0.0max_obstacle_height: 0.1expected_update_rate: 0
    # ...
    
  • 修改 global_costmap_params.yaml 、 local_costmap_params.yaml 文件,调用 Costmap 的障碍物层,这里指定的是 costmap_2d::VoxelLayer 。需要注意的是,插件的调用顺序是从最底层到最顶层。

    local_costmap:# ...plugins:- {name: static_layer,        type: "costmap_2d::StaticLayer"}- {name: obstacle_layer,      type: "costmap_2d::VoxelLayer"}- {name: inflation_layer,     type: "costmap_2d::InflationLayer"}
    
    global_costmap:# ...plugins:- {name: static_layer,            type: "costmap_2d::StaticLayer"}- {name: obstacle_layer,          type: "costmap_2d::VoxelLayer"}- {name: inflation_layer,         type: "costmap_2d::InflationLayer"}
    

转点云数据源码解读

kobuki_bumper2pc 是碰撞传感器和悬崖传感器点云 Nodelet 节点实现。

├── CHANGELOG.rst
├── CMakeLists.txt
├── include
│   └── kobuki_bumper2pc
│       └── kobuki_bumper2pc.hpp  # 碰撞传感器和悬崖传感器点云 Nodelet 类定义
├── launch
│   └── standalone.launch         # 独立启动保险杆和悬崖 Nodelet 节点
├── package.xml
├── plugins
│   └── nodelet_plugins.xml       # Nodelet 插件
└── src└── kobuki_bumper2pc.cpp      # 碰撞传感器和悬崖传感器点云 Nodelet 类实现

kobuki_bumper2pc.cpp

kobuki_bumper2pc.cpp 碰撞传感器和悬崖传感器点云 Nodelet 类实现:

  • 发布导航包可使用的点云话题
  • 在导航中作为 Nodelet 与 kobuki_node 一起工作

Bumper2PcNodelet::onInit

void Bumper2PcNodelet::onInit()
{ros::NodeHandle nh = this->getPrivateNodeHandle();// Bumper/cliff pointcloud distance to base frame; should be something like the robot radius plus// costmap resolution plus an extra to cope with robot inertia. This is a bit tricky parameter: if// it's too low, costmap will ignore this pointcloud (the robot footprint runs over the hit obstacle),// but if it's too big, hit obstacles will be mapped too far from the robot and the navigation around// them will probably fail.std::string base_link_frame;double r, h, angle;nh.param("pointcloud_radius", r, 0.25); pc_radius_ = r;  // 碰撞传感器/悬崖传感器点云到底座半径范围nh.param("pointcloud_height", h, 0.04); pc_height_ = h;  // 碰撞传感器/悬崖传感器点云高度nh.param("side_point_angle", angle, 0.34906585);    // 默认约 20 度nh.param<std::string>("base_link_frame", base_link_frame, "/base_link");  // 发布的点云数据是相对于 /base_link 的,也就是相对于机器人中心位置的// Lateral points x/y coordinates; we need to store float values to memcopy later  // 左右位置的传感器的坐标p_side_x_ = + pc_radius_*sin(angle); // angle degrees from vertical  // 左右位置的碰撞传感器/悬崖传感器发布的点云数据的 x 轴坐标是 pc_radius_*sin(angle)p_side_y_ = + pc_radius_*cos(angle); // angle degrees from vertical  // 左侧碰撞传感器/悬崖传感器发布的点云数据的 y 轴坐标是 pc_radius_*cos(angle)n_side_y_ = - pc_radius_*cos(angle); // angle degrees from vertical  // 右侧碰撞传感器/悬崖传感器发布的点云数据的 y 轴坐标是 -1.0*pc_radius_*cos(angle)// Prepare constant parts of the pointcloud message to be  publishedpointcloud_.header.frame_id = base_link_frame;pointcloud_.width  = 3;  // 点云的宽为 3 (左中右)pointcloud_.height = 1;  // 无序点云的高为 1pointcloud_.fields.resize(3);// Set x/y/z as the only fields  // 点云包含的域的名称pointcloud_.fields[0].name = "x";pointcloud_.fields[1].name = "y";pointcloud_.fields[2].name = "z";int offset = 0;// All offsets are *4, as all field data types are float32for (size_t d = 0; d < pointcloud_.fields.size(); ++d, offset += 4){pointcloud_.fields[d].count    = 1;  // 指该域有多少个元素,一般为 1pointcloud_.fields[d].offset   = offset;  // offset 是在 data 的每个数据中该域的偏移量pointcloud_.fields[d].datatype = sensor_msgs::PointField::FLOAT32;}pointcloud_.point_step = offset;  // 每个点的字节长度pointcloud_.row_step   = pointcloud_.point_step * pointcloud_.width;  // 每行的字节长度pointcloud_.data.resize(3 * pointcloud_.point_step);  // data 表示所有的点的数据,以字节存储pointcloud_.is_bigendian = false;  // 数据是大端存储还是小端存储的标志pointcloud_.is_dense     = true;  // 点云数据中不包含无效点( nan 点)// Bumper/cliff "points" fix coordinates (the others depend on sensor activation/deactivation)// y: always 0 for central bumper  // 中间传感器的点云 y 的值总为 0memcpy(&pointcloud_.data[1 * pointcloud_.point_step + pointcloud_.fields[1].offset], &ZERO, sizeof(float));// z: constant elevation from base frame  // 点云 z 的值为 pc_height_memcpy(&pointcloud_.data[0 * pointcloud_.point_step + pointcloud_.fields[2].offset], &pc_height_, sizeof(float));memcpy(&pointcloud_.data[1 * pointcloud_.point_step + pointcloud_.fields[2].offset], &pc_height_, sizeof(float));memcpy(&pointcloud_.data[2 * pointcloud_.point_step + pointcloud_.fields[2].offset], &pc_height_, sizeof(float));// 这两个话题均做了重映射,见 standalone.launchpointcloud_pub_  = nh.advertise <sensor_msgs::PointCloud2> ("pointcloud", 10);  // 发布到 pointcloud 话题core_sensor_sub_ = nh.subscribe("core_sensors", 10, &Bumper2PcNodelet::coreSensorCB, this);  // 订阅 core_sensors 话题,数据类型是 SensorState ,进入回调函数 Bumper2PcNodelet::coreSensorCBROS_INFO("Bumper/cliff pointcloud configured at distance %f and height %f from base frame", pc_radius_, pc_height_);
}

memcpy()

void *memcpy ( void *destination, const void *source, size_t num );

  • destination 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • source 指向要复制的数据源,类型强制转换为 void* 指针。
  • num 要被复制的字节数。

Bumper2PcNodelet::coreSensorCB

Bumper2PcNodelet::onInit 中已经固定设置好中间传感器的 y 的值以及所有传感器的 z 的值。该回调函数将利用 SensorState 消息类型的数据设置中间传感器 x 的值以及两侧传感器 x y 的值。

void Bumper2PcNodelet::coreSensorCB(const kobuki_msgs::SensorState::ConstPtr& msg)
{if (pointcloud_pub_.getNumSubscribers() == 0)return;// We publish just one "no events" pc (with all three points far away) and stop spamming when bumper/cliff conditions disappearif (! msg->bumper && ! msg->cliff && ! prev_bumper && ! prev_cliff)return;prev_bumper = msg->bumper;prev_cliff  = msg->cliff;// We replicate the sensors order of bumper/cliff event messages: LEFT = 0, CENTER = 1 and RIGHT = 2// For any of {left/center/right} with no bumper/cliff event, we publish a faraway point that won't get usedif ((msg->bumper & kobuki_msgs::SensorState::BUMPER_LEFT) ||(msg->cliff  & kobuki_msgs::SensorState::CLIFF_LEFT))  //左侧传感器{memcpy(&pointcloud_.data[0 * pointcloud_.point_step + pointcloud_.fields[0].offset], &p_side_x_, sizeof(float));memcpy(&pointcloud_.data[0 * pointcloud_.point_step + pointcloud_.fields[1].offset], &p_side_y_, sizeof(float));}else{memcpy(&pointcloud_.data[0 * pointcloud_.point_step + pointcloud_.fields[0].offset], &P_INF_X, sizeof(float));memcpy(&pointcloud_.data[0 * pointcloud_.point_step + pointcloud_.fields[1].offset], &P_INF_Y, sizeof(float));}if ((msg->bumper & kobuki_msgs::SensorState::BUMPER_CENTRE) ||(msg->cliff  & kobuki_msgs::SensorState::CLIFF_CENTRE))  //中间传感器{memcpy(&pointcloud_.data[1 * pointcloud_.point_step + pointcloud_.fields[0].offset], &pc_radius_, sizeof(float));}else{memcpy(&pointcloud_.data[1 * pointcloud_.point_step + pointcloud_.fields[0].offset], &P_INF_X, sizeof(float));}if ((msg->bumper & kobuki_msgs::SensorState::BUMPER_RIGHT) ||(msg->cliff  & kobuki_msgs::SensorState::CLIFF_RIGHT))  //右侧传感器{memcpy(&pointcloud_.data[2 * pointcloud_.point_step + pointcloud_.fields[0].offset], &p_side_x_, sizeof(float));memcpy(&pointcloud_.data[2 * pointcloud_.point_step + pointcloud_.fields[1].offset], &n_side_y_, sizeof(float));}else{memcpy(&pointcloud_.data[2 * pointcloud_.point_step + pointcloud_.fields[0].offset], &P_INF_X, sizeof(float));memcpy(&pointcloud_.data[2 * pointcloud_.point_step + pointcloud_.fields[1].offset], &N_INF_Y, sizeof(float));}pointcloud_.header.stamp = msg->header.stamp;pointcloud_pub_.publish(pointcloud_);  //发布点云数据
}

在 kobuki_bumper2pc.hpp 中定义了 P_INF_X 、 P_INF_Y 、 N_INF_Y 的默认值:

Bumper2PcNodelet(): P_INF_X(+100*sin(0.34906585)),P_INF_Y(+100*cos(0.34906585)),N_INF_Y(-100*cos(0.34906585)),ZERO(0), prev_bumper(0), prev_cliff(0) { }
~Bumper2PcNodelet() { }

standalone.launch

standalone.launch 独立启动保险杆和悬崖 Nodelet 节点:

  • 启动 nodelet_manager Nodelet 管理器
  • 启动碰撞传感器保险杆和悬崖传感器点云 Nodelet
  • 设置 pointcloud_radius 参数,碰撞传感器、悬崖传感器点云到底座半径范围。大概就是机器人半径加上 costmap 识别区用于应对机器人的惯性。 这个参数不容易设置:设置太小 costmap 会忽略点云,导致机器人撞上障碍;设置太大,障碍占据地方太大,围绕他们做导航,就可能失败。
  • 重映射 pointcloud , core_sensors 两个话题
<!--Example standalone launcher for the bumper/cliff to pointcloud nodelet.Parameter pointcloud_radius gives the bumper/cliff pointcloud distance to base frame;should be something like the robot radius plus plus costmap resolution plus an extra tocope with robot inertia. This is a bit tricky parameter: if it's too low, costmap willignore this pointcloud (the robot footprint runs over the hit obstacle), but if it's toobig, hit obstacles will be mapped too far from the robot and the navigation around themwill probably fail.-->
<launch><node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/><node pkg="nodelet" type="nodelet" name="bumper2pointcloud" args="load kobuki_bumper2pc/Bumper2PcNodelet nodelet_manager"><param name="pointcloud_radius" value="0.25"/><remap from="bumper2pointcloud/pointcloud"   to="mobile_base/sensors/bumper_pointcloud"/><remap from="bumper2pointcloud/core_sensors" to="mobile_base/sensors/core"/></node>
</launch>

ROS : Navigation 基于碰撞传感器、悬崖传感器的实时避障 [kobuki]相关推荐

  1. 基于PX4的地面无人车避障系统及路径规划研究

    基于PX4的地面无人车避障系统及路径规划研究 人工智能技术与咨询 来源:<动力系统与控制> ,作者姜琼阁等 关键词: 地面无人车:避障:PX4: 摘要: 地面无人车避障及路径规划是指,无人 ...

  2. 基于单片机智能灯光光控照明系统设计、基于单片机HX711电子秤自动计价系统设计、基于单片机GPRS远程测控系统设计、基于单片机多功能循迹避障无线遥控蓝牙智能小车-设计资料

    基于单片机智能灯光光控照明系统设计 智能光控照明系统设计原理: 本系统采用STC89C52系列的单片机为核心,利用BH1750传感器测量实时光照强度,并将亮度的模拟信号转化成数字信号,大大减少了系统的 ...

  3. c语言智能车跑道检测程序,基于单片机的智能循迹避障小车(附电路原理图,程序清单)...

    基于单片机的智能循迹避障小车(附电路原理图,程序清单)(论文10000字) 摘要:目前,移动机器人的开发和研究越来越令人瞩目,而智能循迹壁障小车作为移动机器人的一个重要分支,非常值得我们探索和讨论.智 ...

  4. 智能循迹避障小车C语言程序编写思路,基于单片机的智能小车红外避障循迹系统设计与制作...

    余秀玲 余秀娟 摘 要:随着科技的高速发展,人们对生活质量的要求越来越高,无人驾驶汽车已经被广为研发和试用,由此智能小车的快速发展也是在情理之中.通过对基于单片机的智能小车的硬件及软件设计分析,实现红 ...

  5. 基于STM32F103的红外循迹避障小车设计(含Proteus仿真)

    基于STM32F103的红外循迹避障小车设计 红外循迹及红外避障实现较简单,无论是51单片机还是STM32单片机,其例程随处可见.但是完全可以运行的Proteus仿真,开源的并不多,更不要说基于STM ...

  6. 基于UWB技术的自动跟随避障系统设计研究

    基于UWB技术的自动跟随避障系统设计研究 摘要:目前我国针对自动跟随避障系统方面的研究较少,同时国内自动跟随避障系统仍然处在未成熟阶段,多数仍停留在应用蓝牙控制或摄像头捕捉这类跟随方式,但这类方式在实 ...

  7. 模糊理论在机器人传感器中的应用_超声波传感器和激光雷达传感器在机器人避障中的应用...

    随着机器人深入人们的生活,例如工厂.仓库.酒店.商场.餐厅等环境中的使用,人们对机器人的移动能力越为重视,市场对智能化设备的需求日益高涨.以至于避障成为一个极为关键且必要的功能. 避障是指移动机器人根 ...

  8. 【路径规划】基于matlab DWA算法机器人局部避障路径规划【含Matlab源码 890期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[路径规划]基于matlab DWA算法机器人局部避障路径规划[含Matlab源码 890期] 获取代码方式2: 通过订阅紫极神光博客付费 ...

  9. 明早10点大讲堂 | 一种新型光幕传感器在机器人避障和无人驾驶中的应用

    雷锋网AI研习社按:像LIDAR这样的3D传感器经常用于今天的道路场景理解.让我们简要地看看它是如何被使用的.在GPS和IMU帮助的情况下,我们将大量的捕获的点云注册到预先构建的或累积的3D地图,然后 ...

最新文章

  1. 如何重装Citrix XenServer不丢失SR数据
  2. 乐源机器人优点跟缺点_机床实现自动化上下料选桁架机械手还是关节机器人好?...
  3. 【GIF动画+完整可运行源代码】C++实现 归并排序——十大经典排序算法之五
  4. 怎么修改谷歌浏览器文件提交按钮样式_使用css自定义input file浏览按钮样式
  5. 揭开不一样的世界,这5部纪录片绝对不能错过!
  6. 03-树1 树的同构 (25 分)
  7. The 2nd AI on Fashion and Textile International Conference 2019
  8. Java 中的PO VO DTO BO
  9. python hook_五分钟内用Python实现GitHook
  10. python-threading.Event实现事件功能--汽车过红绿灯(转载)
  11. *管家病毒查杀模块逆向分析
  12. AMEsim2019.2的安装和matlab2019的联合仿真
  13. Android源码打patch
  14. 如何在win7系统上装苹果雪豹操作系统
  15. vue2百度地图修改比例尺位置
  16. IE浏览器下载中文文件,文件名乱码或变成下划线问题
  17. 将禾赛激光雷达在rviz中采集的点云保存成pcd格式
  18. MySQL——Using Filesort文件排序详解
  19. CTP开发——登录/查询
  20. 交通运输综合管理信息平台建设方案(附下载)

热门文章

  1. 写作是普通人积累财富成本最低最有效的方式
  2. css3 border边框斜线,CSS3 斜线分割布局
  3. 《The Alchemist》阅读笔记
  4. android studio 跳转后保留原页面数据_这些技巧和习惯,让你的原生 Android 手机更好用(上篇)...
  5. C语言浮点数不能取余,C语言浮点型数据能不能取余?
  6. 优秀logo,最基础的设计技巧(二)
  7. Ctone T01双卡双待Android 2.2的3.5英寸电容式多点触摸的GSM智能手机带GPS无线上网
  8. 听说你还不了解微前端?[收藏=学会]
  9. 送你一波运维背锅专用图~
  10. 计算机怎么给硬盘加密,电脑上的硬盘如何加密?几种文件加密的方法