使用激光雷达传感器感知障碍物,建立动态地图,实时的在地图上添加和清除障碍物。有时,清除障碍物机制会不符合预期:
当障碍物出现在机器人面前时,local_costmap会出现障碍物,障碍物离开后,某些障碍物在地图上不消失,或者会残留障碍物的某些点。
当物体消失后,若该方向上的探测距离大于激光雷达量程,则返回数据为大于max_range的值。例如,量程位30m的HOKUYO激光雷达,超量程后会返回65.33m;仿真环境中,量程为3.5m的激光雷达,超量程后的返回数据位inf;某些激光雷达,超量程后返回数据可能为0;具体情况,需要打开激光雷达后,测试其数据方可确定。
而move_base中对不同的激光雷达的超量程数据,没有很好的兼容处理,导致超量程数据没有正常使用,这就是问题根源。
在costmap_common_params.yaml文件中设置:
obstacle_layer:
...
scan:
{
...
inf_is_valid: true,
...
}
文件:navigation-kinetic-devel/costmap_2d/plugins/obstacle_layer.cpp
函数:ObstacleLayer::laserScanValidInfCallback
修改内容:
sensor_msgs::LaserScan message = *raw_message;
for (size_t i = 0; i < message.ranges.size(); i++)
{
float range = message.ranges[ i ];
// changed by kaikai.gao:将判断无穷修改为判断超出范围;
// 若是超范围后数据为零,则可以判断小于epsilon;
// if (!std::isfinite(range) && range > 0)
if (range > message.range_max)
{
message.ranges[ i ] = message.range_max - epsilon;
}
}
当设置地图的分辨率位0.01时,地图上的最小颗粒度位1cmX1cm的矩形。当设置局部地图的范围是5mX5m时,局部地图上距离机器人中心最远距离为5*1.414=7.07m(对角线)。最远距离出相邻两颗粒地图的角度为:弧度0.01/7.07,角度0.081。若要求激光可以扫描每一颗粒,则激光360度分辨率位360/0.081=4444,对于1圈4000一下的激光就无法达到。
当局部地图上障碍物消失后,由于某些颗粒处激光扫描不到,导致此处障碍物无法消失,只有机器人移动时,扫描的原点发生变化,才有概率扫描到那个位置,从而达到清扫障碍物的目的。
如何在不增加精光分辨率的情况下,改变这一现象呢,需要修改清除障碍物的机制。
思想:将激光扫描末端点加大,相当于激光变粗了。
文件:navigation-kinetic-devel/costmap_2d/plugins/obstacle_layer.cpp
函数:ObstacleLayer::raytraceFreespace
修改内容:
void VoxelLayer::raytraceFreespace(const Observation& clearing_observation, double* min_x, double* min_y,
double* max_x, double* max_y)
{
if (clearing_observation.cloud_->points.size() == 0)
return;
double sensor_x, sensor_y, sensor_z;
// 原点
double ox = clearing_observation.origin_.x;
double oy = clearing_observation.origin_.y;
double oz = clearing_observation.origin_.z;
if (!worldToMap3DFloat(ox, oy, oz, sensor_x, sensor_y, sensor_z))
{
ROS_WARN_THROTTLE(
1.0,
"The origin for the sensor at (%.2f, %.2f, %.2f) is out of map bounds. So, the costmap cannot raytrace for it.",
ox, oy, oz);
return;
}
bool publish_clearing_points = (clearing_endpoints_pub_.getNumSubscribers() > 0);
if (publish_clearing_points)
{
clearing_endpoints_.points.clear();
clearing_endpoints_.points.reserve(clearing_observation.cloud_->points.size());
}
// 边界
// we can pre-compute the enpoints of the map outside of the inner loop... we'll need these later
double map_end_x = origin_x_ + getSizeInMetersX();
double map_end_y = origin_y_ + getSizeInMetersY();
for (unsigned int i = 0; i < clearing_observation.cloud_->points.size(); ++i)
{
// 激光点
double wpx = clearing_observation.cloud_->points[i].x;
double wpy = clearing_observation.cloud_->points[i].y;
double wpz = clearing_observation.cloud_->points[i].z;
// kaikai.gao:
// 在这里增加激光雷达数据
//在检测到的点周围生成点
// 方法一:十字交叉点
// double inflate_dx = 0.01, inflate_dy = 0.01; //在原来点的位置膨胀的尺度
// std::vector< std::pair > inflate_pts;
// inflate_pts.push_back(std::make_pair(wpx + 0 , wpy + 0 ));
// inflate_pts.push_back(std::make_pair(wpx - 0 , wpy - inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx - inflate_dx, wpy - 0 ));
// inflate_pts.push_back(std::make_pair(wpx + 0 , wpy + inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx + inflate_dx, wpy + 0 ));
// inflate_pts.push_back(std::make_pair(wpx - 0 , wpy - 2*inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx - 2*inflate_dx, wpy - 0 ));
// inflate_pts.push_back(std::make_pair(wpx + 0 , wpy + 2*inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx + 2*inflate_dx, wpy + 0 ));
// inflate_pts.push_back(std::make_pair(wpx - 0 , wpy - 3*inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx - 3*inflate_dx, wpy - 0 ));
// inflate_pts.push_back(std::make_pair(wpx + 0 , wpy + 3*inflate_dy));
// inflate_pts.push_back(std::make_pair(wpx + 3*inflate_dx, wpy + 0 ));
// 方法二:圆弧
const double lidar_reslution = 0.25*3.1415926/180.0; // 0.25(实际分辨率),4*0.25仿真分辨率
const int half_div_N = 5;
const double dth = lidar_reslution/2.0/(double)half_div_N;
// 距离与角度
double lx = wpx - ox;
double ly = wpy - oy;
double l = sqrt(lx*lx + ly*ly);
double theta_center = atan2(ly, lx);
// 生成角度序列
std::vector theta_s;
theta_s.push_back(theta_center);
double theta_t = 0.0;
for (int i = 0; i < half_div_N; i++)
{
theta_t = theta_center + (double)i*dth;
theta_s.push_back(theta_t);
theta_t = theta_center - (double)i*dth;
theta_s.push_back(theta_t);
}
// 生成点序列
std::vector< std::pair > inflate_pts;
for (int i = 0; i < theta_s.size(); i++)
{
theta_t = theta_s[i];
double x = ox + l*cos(theta_t);
double y = oy + l*sin(theta_t);
inflate_pts.push_back(std::make_pair(x, y));
}
std::vector< std::pair >::iterator inflate_iter;
for(inflate_iter = inflate_pts.begin(); inflate_iter != inflate_pts.end(); inflate_iter++)
{
wpx = (*inflate_iter).first;
wpy = (*inflate_iter).second;
double distance = dist(ox, oy, oz, wpx, wpy, wpz);
double scaling_fact = 1.0;
scaling_fact = std::max(std::min(scaling_fact, (distance - 2 * resolution_) / distance), 0.0);
wpx = scaling_fact * (wpx - ox) + ox;
wpy = scaling_fact * (wpy - oy) + oy;
wpz = scaling_fact * (wpz - oz) + oz;
double a = wpx - ox;
double b = wpy - oy;
double c = wpz - oz;
double t = 1.0;
// we can only raytrace to a maximum z height
if (wpz > max_obstacle_height_)
{
// we know we want the vector's z value to be max_z
t = std::max(0.0, std::min(t, (max_obstacle_height_ - 0.01 - oz) / c));
}
// and we can only raytrace down to the floor
else if (wpz < origin_z_)
{
// we know we want the vector's z value to be 0.0
t = std::min(t, (origin_z_ - oz) / c);
}
// the minimum value to raytrace from is the origin
if (wpx < origin_x_)
{
t = std::min(t, (origin_x_ - ox) / a);
}
if (wpy < origin_y_)
{
t = std::min(t, (origin_y_ - oy) / b);
}
// the maximum value to raytrace to is the end of the map
if (wpx > map_end_x)
{
t = std::min(t, (map_end_x - ox) / a);
}
if (wpy > map_end_y)
{
t = std::min(t, (map_end_y - oy) / b);
}
wpx = ox + a * t;
wpy = oy + b * t;
wpz = oz + c * t;
double point_x, point_y, point_z;
if (worldToMap3DFloat(wpx, wpy, wpz, point_x, point_y, point_z))
{
unsigned int cell_raytrace_range = cellDistance(clearing_observation.raytrace_range_);
// voxel_grid_.markVoxelLine(sensor_x, sensor_y, sensor_z, point_x, point_y, point_z);
voxel_grid_.clearVoxelLineInMap(sensor_x, sensor_y, sensor_z, point_x, point_y, point_z, costmap_,
unknown_threshold_, mark_threshold_, FREE_SPACE, NO_INFORMATION,
cell_raytrace_range);
updateRaytraceBounds(ox, oy, wpx, wpy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);
if (publish_clearing_points)
{
geometry_msgs::Point32 point;
point.x = wpx;
point.y = wpy;
point.z = wpz;
clearing_endpoints_.points.push_back(point);
}
}
}
}
if (publish_clearing_points)
{
clearing_endpoints_.header.frame_id = global_frame_;
clearing_endpoints_.header.stamp = pcl_conversions::fromPCL(clearing_observation.cloud_->header).stamp;
clearing_endpoints_.header.seq = clearing_observation.cloud_->header.seq;
clearing_endpoints_pub_.publish(clearing_endpoints_);
}
}