ROS Costmap 插件
admin
2024-04-13 23:03:24
0

背景

公司 AVP demo 需要把 Tof 得到的点云放到 costmap 里避障用,但是出现了“残影”, 研究了一下应该是 raytrace 部分出现了问题,ROS 自带的 obtacle layer 里的 raytrace 不适合我们的 Tof,需要自己弄一个插件,又重新看了一下 navigation 的代码。

继承关系

move_base 里会有一个成员
costmap_2d::Costmap2DROS* planner_costmap_ros_, *controller_costmap_ros_;
这是一个 ROS 节点,这个节点的主要工作就是维护这个成员
LayeredCostmap* layered_costmap_;
而 LayeredCostmap 里有一个成员,用来装各种插件
std::vector > plugins_;
这些插件用来更新这个成员
Costmap2D costmap_;
这个就是我们最后要用到的代价地图
接下啦就是看每一个插件是怎么运行的, 这里以 obstaclelayer 为例
class ObstacleLayer : public CostmapLayer
他是继承自 CostmapLayer, ros 自己带的几个插件都是继承自这个类,这个类又继承自两个东西
class CostmapLayer : public Layer, public Costmap2D
其中 layer 规定了层的行为, Costmap2D 是存数据的

调用关系

Costmap2DROS 实例化以后会跑一个线程 mapUpdateLoop,这个线程里不断执行 updateMap() 这个函数
这个函数会首先更新机器人的位姿,然后调用 layered_costmap_ 的 updateMap
layered_costmap_ 里的 updateMap 就是遍历所有插件,
首先进行 updateBounds
这个过程中每个插件都会计算出自己要更新的 grid 的值(有的还会进行 raytrace 之类的操作)。和要更新的范围(不是整个地图都要更新,只更新一块 min max xy)
之后对综合所有插件得到的需要更新的 bound 里的代价值进行更新 updateCosts
把上一步里插件算出来的值更新到 costmap_ 成员里, 这里还可以选择更新方式,例如overwrite等,这些都是可以自定义的

修改

之前的雷达更新逻辑是观测数据末端到观测点连线之间的所有 grid 都会被置为 FREE, 但是障碍物移动出我们的 Tof 的 FOV 以后就没有观测数据了,就没法 raytrace 了。
这里直接把这个 raytrace 改成 FOV 的左右两条线和 localcostmap 外围框的围成的区域都 raytrace 掉, 然后在把当前帧看到的物体放到代价地图里。
比较绕的就是怎么算这个区域,自己整理了一个 FOV 小于 90° 时可以用的方法。
代码里用了许多 magic number,根据自己情况修改。

void TofObstacleLayer::raytraceFreespace(const Observation& clearing_observation, double* min_x, double* min_y,double* max_x, double* max_y, double robot_yaw)
{double ox = clearing_observation.origin_.x;double oy = clearing_observation.origin_.y;  pcl::PointCloud < pcl::PointXYZ > cloud = *(clearing_observation.cloud_);unsigned int x0, y0;if (!worldToMap(ox, oy, x0, y0)) // 观测原点的世界坐标减去局部地图原点的世界坐标 / 分辨率,得到的是局部坐标啊{ROS_WARN_THROTTLE(1.0, "The origin for the sensor at (%.2f, %.2f) is out of map bounds. So, the costmap cannot raytrace for it.",ox, oy);return;}double origin_x = origin_x_, origin_y = origin_y_;double map_end_x = origin_x + size_x_ * resolution_;double map_end_y = origin_y + size_y_ * resolution_;touch(ox, oy, min_x, min_y, max_x, max_y);// for each point in the cloud, we want to trace a line from the origin and clear obstacles along it// printf("getting points num: %d\n", cloud.points.size());// for (unsigned int i = 0; i < cloud.points.size(); ++i)// {//   // 世界坐标//   double wx = cloud.points[i].x;//   double wy = cloud.points[i].y;//   // now we also need to make sure that the enpoint we're raytracing//   // to isn't off the costmap and scale if necessary//   double a = wx - ox; // 点到原点差值//   double b = wy - oy;//   // the minimum value to raytrace from is the origin//   // 这里就是把地图外的点截到地图边界上//   if (wx < origin_x) // origin-x 应该是局部地图的起点(世界坐标)//   {//     double t = (origin_x - ox) / a;//     wx = origin_x;//     wy = oy + b * t;//   }//   if (wy < origin_y)//   {//     double t = (origin_y - oy) / b;//     wx = ox + a * t;//     wy = origin_y;//   }//   // the maximum value to raytrace to is the end of the map//   if (wx > map_end_x)//   {//     double t = (map_end_x - ox) / a;//     wx = map_end_x - .001;//     wy = oy + b * t;//   }//   if (wy > map_end_y)//   {//     double t = (map_end_y - oy) / b;//     wx = ox + a * t;//     wy = map_end_y - .001;//   }//   // now that the vector is scaled correctly... we'll get the map coordinates of its endpoint//   unsigned int x1, y1;//   // check for legality just in case//   if (!worldToMap(wx, wy, x1, y1)) // 这里是要知道到 map 是局部的还是全局的//     continue;unsigned int cell_raytrace_range = cellDistance(clearing_observation.raytrace_range_);MarkCell marker(costmap_, FREE_SPACE);// and finally... we can execute our trace to clear obstacles along that line// raytraceLine(marker, x0, y0, x1, y1, cell_raytrace_range); // 传感器原点x0 y0, 到观测点 x1 y1,进行 raytrace,都是地图坐标(格子)//   updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);// }// 1. 根据 yaw 算出两个交点// float half_fov = horizontal_fov_ / 2;float left_angle = robot_yaw + horizontal_fov_ / 2;float right_angle = robot_yaw - horizontal_fov_ / 2;// 算出left的交点,我的地图用的是 60x60, 29 + 31 = 60, 左下角是 (-29,-29) 右上角是 (31,31)float x1_f, y1_f;float left_line_a = tan(left_angle);if(left_angle > 0.785 && left_angle <2.355){ // case 1 pi/4 至 pi 3/4 与 y = 31 相交x1_f = 31 / left_line_a;y1_f = 31;}else if(left_angle > -0.785 && left_angle < 0.785){ // case 2 -pi/4 至 +pi/4  与 x = 31 相交x1_f = 31;y1_f = 31 * left_line_a;}else if(left_angle < -0.785 && left_angle > -2.355){ // case3  -pi/4 至 -3pi/4 与 y = -29 相交x1_f = -29 / left_angle;y1_f = -29;}else if(left_angle > 2.355 || left_angle < -2.355){// 因为 yaw 计数规则,这个象限被拆开了,与 x = -29 相交 x1_f = -29;y1_f = -29 * left_line_a;}// (注意要平移 + 29)x1_f = x1_f + 29;y1_f = y1_f + 29;unsigned int x1 = (unsigned int)x1_f;unsigned int y1 = (unsigned int)y1_f;// 机器人右边 FOV 边界,不管在哪里都是逆时针去 tracefloat x2_f, y2_f;float right_line_a = tan(right_angle);if(right_angle > 0.785 && right_angle <2.355){ // pi/4 至 pi 3/4 与 y = 31 相交x2_f = 31 / right_line_a;y2_f = 31;}else if(right_angle > -0.785 && right_angle < 0.785){ // -pi/4 至 +pi/4  与 x = 31 相交x2_f = 31 ;y2_f = 31 * right_line_a;}else if(right_angle < -0.785 && right_angle > -2.355){ // -pi/4 至 -3pi/4 与 y = -29 相交x2_f = -29 / right_line_a;y2_f = -29;}else if(right_angle > 2.355 ||  right_angle < -2.355){ // 与 x = -29 相交 x2_f = -29;y2_f = -29 * right_line_a;}// (注意要平移 + 29)x2_f = x2_f + 29;y2_f = y2_f + 29;unsigned int x2 = (unsigned int)((int)x2_f);unsigned int y2 = (unsigned int)y2_f;// 以left和哪条线相交作为判断起点switch (y1){case 60: // left 与上方边框相交switch (y2){case 60: // left right 都与上方边框相交 (y1 == 60 && y2 == 60)// 那么从 x1 trace 到 x2, 因为 x2 在 x1 顺时针方向, x2 大for(unsigned xi = x1; xi < x2; xi ++){raytraceLine(marker, x0, y0, xi, y1, cell_raytrace_range);// 这里还要计算出 xi,y1 对应的世界坐标,替换 wx,wydouble wx = origin_x +  xi * resolution_;double wy = origin_y +  y1 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;default: // left 与上方边框相交,right 与右侧边框相交 (y1 == 60 && x2 == 60)// Fov 分别与两条边框相交,分两段 trace// left 顺时针 trace x 到60for(unsigned xi = x1; xi < 60; xi ++){raytraceLine(marker, x0, y0, xi, y1, cell_raytrace_range);double wx = origin_x +  xi * resolution_;double wy = origin_y +  y1 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}// right 逆时针 trace 意味着 y 增到到 60for(unsigned yi = y2; yi < 60; yi++){raytraceLine(marker, x0, y0, x2, yi, cell_raytrace_range);double wx = origin_x +  x2 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);} break;}break;case 0: // left 与下方边框相交switch (y2){case 0: // left right 都与下方边框相交 ( y1 == 0 && y2 == 0)// 那么从 x1 trace 到 x2, 因为 x2 在 x1 顺时针方向, x2 小for(unsigned xi = x2; xi < x1; xi ++){ // 下方水平线,从左侧端点 traceraytraceLine(marker, x0, y0, xi, y1, cell_raytrace_range);double wx = origin_x +  xi * resolution_;double wy = origin_y +  y1 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;default: // left 与下方边框相交, right 与左侧边框相交 ( y1 == 0 && x2 == 0)// 顺时针 trace 意味着 x 是减小到0的for(unsigned xi = 0; xi < x1; xi ++){ // 下方水平线,从左侧端点 traceraytraceLine(marker, x0, y0, xi, y1, cell_raytrace_range);double wx = origin_x +  xi * resolution_;double wy = origin_y +  y1 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}// 逆时针 trace 意味着 y 是减小到0的for(unsigned yi = 0; yi < y2 ; yi++){ // 左侧竖直线,从下往上 traceraytraceLine(marker, x0, y0, x2, yi, cell_raytrace_range);double wx = origin_x +  x2 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;}break;default: // left 与左右侧边框相交switch (x1){case 60: // left 与右侧边框相交switch (x2){case 60: // left right 都与右侧边框相交 (x1 == 60 && x2 == 60)for(unsigned yi = y2; yi < y1; yi++){raytraceLine(marker, x0, y0, x2, yi, cell_raytrace_range);double wx = origin_x +  x2 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;default: // left 与右侧边框相交,right 与下方边框相交 (x1 == 60 && y2 == 0)// Fov 分别与两条边框相交,分两段 trace// left 顺时针 trace y 到 0for(unsigned yi = 0; yi < y1 ; yi++){ raytraceLine(marker, x0, y0, x1, yi, cell_raytrace_range);double wx = origin_x +  x1 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}// right 逆时针 trace x 增到到 60for(unsigned xi = x2; xi < 60; xi ++){raytraceLine(marker, x0, y0, xi, y2, cell_raytrace_range);double wx = origin_x +  xi * resolution_;double wy = origin_y +  y2 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;}break;case 0: // left 与左侧边框相交switch (x2){case 0: // left right 都与左侧边框相交 (x1 == 0 && x2 == 0)for(unsigned yi = y1; yi < y2 ; yi++){ // 左侧竖直线,从下往上 traceraytraceLine(marker, x0, y0, x2, yi, cell_raytrace_range);double wx = origin_x +  x2 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;default: // left 与左侧边框相交, right 与上方边框相交 (x1 == 0 && y2 == 60)for(unsigned yi = y1; yi < 60 ; yi++){ // 下方水平线,从左侧端点 traceraytraceLine(marker, x0, y0, x1, yi, cell_raytrace_range);double wx = origin_x +  x1 * resolution_;double wy = origin_y +  yi * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}// right 逆时针 trace  意味着 x 减小到 0for(unsigned xi = 0; xi < x2; xi ++){ // 左侧竖直线,从下往上 traceraytraceLine(marker, x0, y0, xi, y2, cell_raytrace_range);double wx = origin_x +  xi * resolution_;double wy = origin_y +  y2 * resolution_;updateRaytraceBounds(ox, oy, wx, wy, clearing_observation.raytrace_range_, min_x, min_y, max_x, max_y);}break;}break;}}
}

相关内容

热门资讯

马年AI首战:大模型“入户”没... 来源:市场资讯 (来源:北京商报) 2月23日,港交所一开盘,腾讯和阿里的股价向上45度上涨,收盘价...
日本经济长期疲软,日元购买力跌... 【环球时报特约记者 陈欣】受日本经济长期疲软拖累,反映日元综合实力的实际有效汇率指数创下53年来新低...
美股,全线重挫 美东时间周一,美股三大指数收盘均跌超1%。截至收盘,标普500指数跌1.04%,报6837.75点;...
当反向过年走红 留京的网约车“... 农历马年大年初四(2月20日)晚上十点左右,北京丰台站出站口的网约车候车区,已经有不少节后返程的旅客...
国际观察|美国内各方围绕关税角... 新华社纽约2月23日报道,美国最高法院日前公布裁决,认定美国《国际紧急经济权力法》没有授权总统征收大...
原创 周... 一、周二A股开盘在即,对于今天A股走势,我在周一早上已发文表达了看多的观点,而且我也同时表达了周一晚...
Xbox主机联合创始人:微软游... IT之家 2 月 24 日消息,Xbox 主机联合创始人对其老东家近期的动荡局面发表了看法。在接受 ...
AI早报 | 支付宝“AI付”... 支付宝“AI付”用户破1亿 2月23日,据“支付宝”公众号消息,春节期间,支付宝“AI付”用户数突...
银行首席经济学家,纵论市场大势 兴业银行首席经济学家鲁政委:A股行情由预期驱动转向基本面验证 2026年,海外主要经济体货币政策由...
跨境和行业ETF逆势“吸金” ◎记者 赵明超 进入2026年以来,资金流向呈现分化态势。在宽基ETF遭遇流出的同时,行业ETF和跨...
香港收购宏福苑火灾房公布“明细... 因香港特区政府此次为受灾业主开出了较为优厚的“回收价格”,业主们在全港范围内可供选购的总价较低的公屋...
银行首席经济学家纵论市场大势 本版导读 2026-02-24 2026-02-24 2026-02-24 2026...
AI文章过AIGC检测秘诀,自... 实操教程:如何让AI文章成功通过AIGC检测器? 小李是一名新媒体运营,最近遇到了一个头疼的问题:...
原创 甲... 甲状腺癌是目前发病率较高的恶性肿瘤,但好在预后较好,多数患者经过手术治疗后,能恢复正常生活。不过术后...
原创 新... 长沙晚报掌上长沙2月23日讯(全媒体记者 刘捷萍 通讯员 唐铭宏)2026年马年春节假期,为保障长沙...
国补加持智能眼镜成消费新宠 行... 2026年开年以来,在国家购新补贴政策与技术迭代的双重推动下,智能眼镜市场迎来消费热潮,成为不少消费...
【环球财经】伦敦金属交易所基本... 来源:中国金融信息网 新华财经伦敦2月21日电(记者 张亚东)伦敦金属交易所基本金属价格20日收盘时...
原创 特... 裁决公布后仅仅几个小时,特朗普就在白宫召开了记者会。他愤怒地批评最高法院的裁决“荒谬、措辞拙劣且极端...
原创 四... 浮躁,几乎是每个人都经历过的状态,我自己也不例外。每天,我总会在脑海中幻想,什么时候才能一夜暴富,抛...