0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于MinBox障碍物边框构建的干货!

YB7m_Apollo_Dev 来源:lq 2018-12-06 16:23 次阅读

Apollo感知模块具有识别障碍物和交通灯的能力,LiDAR利用点云感知,可以了解障碍物的位置、大小、类别、朝向、轨迹和速度等信息

Apollo解决的障碍物感知问题有:

·高精地图ROI过滤器(HDMap ROI Filter)

·基于卷积神经网络分割(CNNSegmentation)

·MinBox障碍物边框构建(MinBoxBuilder)

·HM对象跟踪(HM Object Tracker)等

Apollo视觉感知输出的第一步是做detection(目标检测)和分割,第二步是要把结果2D-to-3D,同时还有一个tracking(追踪)的过程后,即完成了感知的主要输出步骤。

其中,tracking是做时序,2D和3D结果也会做tracking,这是无人车做感知常有的pipeline(线性模型),有这样的结果以后,就会输出被感知物体的位置和速度等信息。

在障碍物边框构建这一环节,开发者使用CNN Seg DL学习的方法,得到障碍物信息后,可使用MinBox的方法得到物体的2D框(最终边界框),结合tracking结合相机图像进行学习得到的物体边界框,则可得到物体3D的边界框。

以下是Apollo社区开发者朱炎亮在Github-Apollo-Note上分享的《如何构建MinBox障碍物边框》,感谢他为我们在MinBox Builder这一步所做的详细注解和释疑。

面对复杂多变、快速迭代的开发环境,只有开放才会带来进步,Apollo社区正在被开源的力量唤醒。

对象构建器组件为检测到的障碍物建立一个边界框。因为LiDAR传感器的遮挡或距离,形成障碍物的点云可以是稀疏的,并且仅覆盖一部分表面。因此,盒构建器将恢复给定多边形点的完整边界框。即使点云稀疏,边界框的主要目的还是预估障碍物(例如,车辆)的方向。同样地,边框也用于可视化障碍物。

算法背后的想法是找到给定多边形点边缘的所有区域。在以下示例中,如果AB是边缘,则Apollo将其他多边形点投影到AB上,并建立具有最大距离的交点对,这是属于边框的边缘之一。然后直接获得边界框的另一边。通过迭代多边形中的所有边,如下图所示,Apollo确定了一个6边界边框,将选择具有最小面积的方案作为最终的边界框。

我们从代码入手,一步步解析障碍物边框构建的流程。

上一步CNN分割与后期处理,可以得到LiDAR一定区域内的障碍物集群。接下来,我们将对这些障碍物集群建立其标定框。标定框的作用除了标识物体,还有一个作用就是标记障碍物的长length、宽width、高height。其中规定长length大于宽width,障碍物方向就是长的方向direction。

MinBox构建过程如下:

·计算障碍物2D投影(高空鸟瞰XY平面)下的多边形polygon(如下图B)。

·根据上述多边形,计算最适边框(如下图C)。

大致的代码框架如下:

1 /// file in apollo/modules/perception/obstacle/onboard/lidar_process_subnode.ccvoid LidarProcessSubnode::OnPointCloud(const sensor_msgs::PointCloud2& message) { /// call hdmap to get ROI 2 ... /// call roi_filter 3 ... /// call segmentor 4 ... /// call object builder 5 if (object_builder_ != nullptr) { 6 ObjectBuilderOptions object_builder_options; if (!object_builder_->Build(object_builder_options, &objects)) { 7 ... 8 } 9 } 10 }/// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccbool MinBoxObjectBuilder::Build(const ObjectBuilderOptions& options, std::vector* objects) { for (size_t i = 0; i < objects->size(); ++i) { if ((*objects)[i]) { BuildObject(options, (*objects)[i]); 11 } 12 } 13 }void MinBoxObjectBuilder::BuildObject(ObjectBuilderOptions options, ObjectPtr object) { ComputeGeometricFeature(options.ref_center, object); 14 }void MinBoxObjectBuilder::ComputeGeometricFeature(const Eigen::Vector3d& ref_ct, ObjectPtr obj) { // step 1: compute 2D xy plane's polygen15 ComputePolygon2dxy(obj); // step 2: construct box16 ReconstructPolygon(ref_ct, obj); 17 }

以上部分是MinBox障碍物边框构建的主题框架代码,构建的两个过程分别在ComputePolygon2dxy

和ReconstructPolygon函数完成,下面我们就具体深入这两个函数,详细了解一下Apollo对障碍物构建的一个流程,和其中一些令人费解的代码段。

Step 1. MinBox构建--计算2DXY平面投影

这个阶段主要作用是障碍物集群做XY平面下的凸包多边形计算,最终得到这个多边形的一些角点。第一部分相对比较简单,没什么难点,计算凸包是调用plc库的ConvexHull组件(具体请参考pcl::ConvexHull)。

Apollo的凸包计算代码如下:

1 /// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccvoid MinBoxObjectBuilder::ComputePolygon2dxy(ObjectPtr obj) { 2 ... 3 ConvexHull2DXY hull; 4 hull.setInputCloud(pcd_xy); 5 hull.setDimension(2); 6 std::vector poly_vt; 7 PointCloudPtr plane_hull(new PointCloud); 8 hull.Reconstruct2dxy(plane_hull, &poly_vt); if (poly_vt.size() == 1u) { 9 std::vector ind(poly_vt[0].vertices.begin(), poly_vt[0].vertices.end()); TransformPointCloud(plane_hull, ind, &obj->polygon); 10 } else { 11 ... 12 } 13 }/// file in apollo/modules/perception/common/convex_hullxy.htemplate class ConvexHull2DXY : public pcl::ConvexHull {public:14 void Reconstruct2dxy(PointCloudPtr hull, std::vector *polygons) { PerformReconstruction2dxy(hull, polygons, true); 15 } void PerformReconstruction2dxy(PointCloudPtr hull, std::vector *polygons, bool fill_polygon_data = false) { 16 coordT *points = reinterpret_cast(calloc(indices_->size() * dimension, sizeof(coordT))); // step1. Build input data, using appropriate projection17 int j = 0; for (size_t i = 0; i < indices_->size(); ++i, j += dimension) { 18 points[j + 0] = static_cast(input_->points[(*indices_)[i]].x); 19 points[j + 1] = static_cast(input_->points[(*indices_)[i]].y); 20 } // step2. Compute convex hull21 int exitcode = qh_new_qhull(dimension, static_cast(indices_->size()), points, ismalloc, const_cast(flags), outfile, errfile); 22 std::vector, Eigen::aligned_allocator>> idx_points(num_vertices); 23 FORALLvertices { 24 hull->points[i] = input_->points[(*indices_)[qh_pointid(vertex->point)]]; 25 idx_points[i].first = qh_pointid(vertex->point); 26 ++i; 27 } // step3. Sort28 Eigen::Vector4f centroid; pcl::compute3DCentroid(*hull, centroid); for (size_t j = 0; j < hull->points.size(); j++) { 29 idx_points[j].second[0] = hull->points[j].x - centroid[0]; 30 idx_points[j].second[1] = hull->points[j].y - centroid[1]; 31 } std::sort(idx_points.begin(), idx_points.end(), pcl::comparePoints2D); 32 polygons->resize(1); 33 (*polygons)[0].vertices.resize(hull->points.size()); for (int j = 0; j < static_cast(hull->points.size()); j++) { 34 hull->points[j] = input_->points[(*indices_)[idx_points[j].first]]; 35 (*polygons)[0].vertices[j] = static_cast(j); 36 } 37 } 38 }

从以上代码的注释中,我们可以清楚地了解到这个多边形顶点的求解流程,具体函数由PerformReconstruction2dxy函数完成,这个函数其实跟pcl库自带的很像pcl::ConvexHull::performReconstruction2D/Line76,其实Apollo开发人员几乎将pcl库的performReconstruction2D原封不动地搬了过来,仅去掉了一些冗余的信息。

这个过程主要有:

1. 构建输入数据,将输入的点云复制到coordT *points做处理。

2. 计算障碍物点云的凸包,得到的结果是多边形顶点。调用qh_new_qhull函数。

3. 顶点排序,从pcl::comparePoints2D/Line59

可以看到排序是角度越大越靠前,atan2函数的结果是[-pi,pi]。所以就相当于是顺 时针对顶点进行排序。

上图为计算多边形交点的流程示意图

该过程并不繁琐,这里不再深入解释每个模块。

Step 2. MinBox构建--边框构建

边框构建的逻辑,大致是针对过程中得到的多边形的每一条边。将剩下的所有点都投影到这条边上,就可以计算边框Box的一条边长度(最远的两个投影点距离),同时选择距离该条边最远的点计算Box的高,这样就可以得到一个Box(上图case1-7分别是以这个多边形7条边作投影得到的7个Box),最终选择Box面积最小的边框作为障碍物的边框。

上图中case7得到的Box面积最小,所以case7中的Box就是最终障碍物的边框。当边框确定以后,就可以得到障碍物的长度length(大边长),宽度(小边长),方向(大边上对应的方向),高度(点云的平均高度,CNN分割与后期处理阶段得到)。

但是在此过程中,不得不承认有部分代码块相对难理解,而且出现了很多实际问题来优化这个过程。这里我将对这些问题一一进行解释。

根据代码,先简单地将这个过程归结为2步:

1. 投影边长的选择(为什么要选择?因为背对lidar那一侧的点云是稀疏的,那一侧的多边形顶点是不可靠的,不用来计算Box)。

2. 每个投影边长计算Box。

在进入正式的代码详解以前,这里有几个知识点需要了解。

假设向量a=(x0,y0),向量b=(x1,y1),那么有:

·两个向量的点乘, a·b = x0x1 + y0y1\。

·计算向量a在向量b上的投影: v = a·b/(b^2)·b,投影点的坐标就是v+(b.x, b.y)。

·两个向量的叉乘, axb = |a|·|b|sin(theta) = x0y1 - x1y0,叉乘方向与ab平面垂直,遵循右手法则。叉乘模大小另一层意义是: ab向量构成的平行四边形面积。

如果两个向量a,b共起点,那么axb小于0,那么a to b的逆时针夹角大于180度;等于则共线;大于0,a to b的逆时针方向夹角小于180度。

接下来正式解剖

ReconstructPolygon构建的代码:

(1) Step1:投影边长的选择

1 /// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccvoid MinBoxObjectBuilder::ReconstructPolygon(const Eigen::Vector3d& ref_ct, ObjectPtr obj) { // compute max_point and min_point 2 size_t max_point_index = 0; size_t min_point_index = 0; 3 Eigen::Vector3d p; 4 p[0] = obj->polygon.points[0].x; 5 p[1] = obj->polygon.points[0].y; 6 p[2] = obj->polygon.points[0].z; 7 Eigen::Vector3d max_point = p - ref_ct; 8 Eigen::Vector3d min_point = p - ref_ct; for (size_t i = 1; i < obj->polygon.points.size(); ++i) { 9 Eigen::Vector3d p; 10 p[0] = obj->polygon.points[i].x; 11 p[1] = obj->polygon.points[i].y; 12 p[2] = obj->polygon.points[i].z; 13 Eigen::Vector3d ray = p - ref_ct; // clock direction14 if (max_point[0] * ray[1] - ray[0] * max_point[1] < EPSILON) { 15        max_point = ray; 16        max_point_index = i; 17      }    // unclock direction18      if (min_point[0] * ray[1] - ray[0] * min_point[1] > EPSILON) { 19 min_point = ray; 20 min_point_index = i; 21 } 22 } 23 }

以上代码内容为计算min_point和max_point两个角点,该角点到底是什么?其中关于EPSILON的比较条件到底代表什么?

有一个前提我们已经在polygons多边形角点计算中可知:obj的polygon中所有角点都是顺时针按照arctan角度由大到小排序。

此过程可以通过下图了解其作用:

图中叉乘与0(EPSILON)的大小是根据前面提到的,两个向量的逆时针夹角。从上图中可以清晰地看到:max_point和min_point就代表了LiDAR能检测到障碍物的两个极端点。

1 /// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccvoid MinBoxObjectBuilder::ReconstructPolygon(const Eigen::Vector3d& ref_ct, ObjectPtr obj) { // compute max_point and min_point 2 ... // compute valid edge 3 Eigen::Vector3d line = max_point - min_point; double total_len = 0; double max_dis = 0; bool has_out = false; for (size_t i = min_point_index, count = 0; count < obj->polygon.points.size(); i = (i + 1) % obj->polygon.points.size(), ++count) { //Eigen::Vector3d p_x = obj->polygon.point[i] 4 size_t j = (i + 1) % obj->polygon.points.size(); if (j != min_point_index && j != max_point_index) { // Eigen::Vector3d p = obj->polygon.points[j]; 5 Eigen::Vector3d ray = p - min_point; if (line[0] * ray[1] - ray[0] * line[1] < EPSILON) { 6          ... 7        }else { 8          ... 9        } 10      } else if ((i == min_point_index && j == max_point_index) || (i == max_point_index && j == min_point_index)) { 11        ... 12      } else if (j == min_point_index || j == max_point_index) {      // Eigen::Vector3d p = obj->polygon.points[j];13 Eigen::Vector3d ray = p_x - min_point; if (line[0] * ray[1] - ray[0] * line[1] < EPSILON) { 14          ... 15        } else { 16          ... 17        } 18      } 19    } 20  }

当计算得到max_point和min_point后就需要执行这段代码,这段代码可能令人费解的原因为——为什么需要对每条边做一个条件筛选?

请看下图:

上图中,A演示了这段代码对一个汽车的点云多边形进行处理,最后的处理结果可以看到只有Edge45、Edge56、Edge67是有效的,最终会被计入total_len和max_dist。并且,相对应的边都是在line(max_point-min_point)这条分界线的一侧,同时也是靠近LiDAR这一侧。

这说明靠近LiDAR这一侧点云检测效果好,边稳定;而背离LiDAR那一侧,会因为遮挡原因,往往很难(有时候不可能)得到真正的顶点,如上图B所示。

(2) Step2:投影边长Box计算

投影边长Box计算由ComputeAreaAlongOneEdge函数完成,下面我们分析这个函数的代码:

1 /// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccdouble MinBoxObjectBuilder::ComputeAreaAlongOneEdge( 2 ObjectPtr obj, size_t first_in_point, Eigen::Vector3d* center, double* lenth, double* width, Eigen::Vector3d* dir) { // first for 3 std::vector ns; 4 Eigen::Vector3d v(0.0, 0.0, 0.0); // 记录以(first_in_point,first_in_point+1)两个定点为边,所有点投影,距离这条边最远的那个点 5 Eigen::Vector3d vn(0.0, 0.0, 0.0); // 最远的点在(first_in_point,first_in_point+1)这条边上的投影坐标 6 Eigen::Vector3d n(0.0, 0.0, 0.0); // 用于临时存储 7 double len = 0; double wid = 0; size_t index = (first_in_point + 1) % obj->polygon.points.size(); for (size_t i = 0; i < obj->polygon.points.size(); ++i) { if (i != first_in_point && i != index) { // o = obj->polygon.points[i] 8 // a = obj->polygon.points[first_in_point] 9 // b = obj->polygon.points[first_in_point+1]10 // 计算向量ao在ab向量上的投影,根据公式:k = ao·ab/(ab^2), 计算投影点坐标,根据公式k·ab+(ab.x, ab.y)11 double k = ((a[0] - o[0]) * (b[0] - a[0]) + (a[1] - o[1]) * (b[1] - a[1])); 12 k = k / ((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); 13 k = k * -1; 14 n[0] = (b[0] - a[0]) * k + a[0]; 15 n[1] = (b[1] - a[1]) * k + a[1]; 16 n[2] = 0; // 计算由ab作为边,o作为顶点的平行四边形的面积,利用公式|ao x ab|,叉乘的模就是四边形的面积,17 Eigen::Vector3d edge1 = o - b; 18 Eigen::Vector3d edge2 = a - b; double height = fabs(edge1[0] * edge2[1] - edge2[0] * edge1[1]); // 利用公式: 面积/length(ab)就是ab边上的高,即o到ab边的垂直距离, 记录最大的高19 height = height / sqrt(edge2[0] * edge2[0] + edge2[1] * edge2[1]); if (height > wid) { 20 wid = height; 21 v = o; 22 vn = n; 23 } 24 } else { 25 ... 26 } 27 ns.push_back(n); 28 } 29 }

从以上部分代码可以看出,ComputeAreaAlongOneEdge函数接受的输入包括多边形顶点集合,起始边first_in_point。代码将以first_in_point和first_in_point+1两个顶点构建一条边,将集合中其他点都投影到这条边上,并计算顶点距离这条边的高——也就是垂直距离。

最终的结果会保存到ns中。代码中,k的计算利用了两个向量点乘来计算投影点的性质;height的计算利用了两个向量叉乘的模等于两个向量组成的四边形面积的性质。

1 /// file in apollo/modules/perception/obstacle/lidar/object_builder/min_box/min_box.ccdouble MinBoxObjectBuilder::ComputeAreaAlongOneEdge( // first for 2 ... // second for 3 size_t point_num1 = 0; size_t point_num2 = 0; // 遍历first_in_point和first_in_point+1两个点以外的,其他点的投影高,选择height最大的点,来一起组成Box 4 // 这两个for循环是寻找ab边上相聚最远的投影点,因为要把所有点都包括到Box中,所以Box沿着ab边的边长就是最远两个点的距离,可以参考边框构建。 5 for (size_t i = 0; i < ns.size() - 1; ++i) { 6      Eigen::Vector3d p1 = ns[i];    for (size_t j = i + 1; j < ns.size(); ++j) { 7        Eigen::Vector3d p2 = ns[j];      double dist = sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));      if (dist > len) { 8 len = dist; 9 point_num1 = i; 10 point_num2 = j; 11 } 12 } 13 } // vp1和vp2代表了Box的ab边对边的那条边的两个顶点,分别在v的两侧,方向和ab方向一致。14 Eigen::Vector3d vp1 = v + ns[point_num1] - vn; 15 Eigen::Vector3d vp2 = v + ns[point_num2] - vn; // 计算中心点和面积16 (*center) = (vp1 + vp2 + ns[point_num1] + ns[point_num2]) / 4; 17 (*center)[2] = obj->polygon.points[0].z; if (len > wid) { 18 *dir = ns[point_num2] - ns[point_num1]; 19 } else { 20 *dir = vp1 - ns[point_num1]; 21 } 22 *lenth = len > wid ? len : wid; 23 *width = len > wid ? wid : len; return (*lenth) * (*width); 24 }

剩下的代码就是计算Box的四个顶点坐标,以及它的面积Area。

综上所述,Box经过上述(1)(2)两个阶段,可以很清晰地得到每条有效边(靠近lidar一侧,在min_point和max_point之间)对应的Box四个顶点坐标、宽、高。最终选择Box面积最小的作为障碍物预测Box。

整个过程的代码部分相对较难理解,经过本节的学习,相信各位应该对MinBox边框的构建有了一定了解。

自Apollo平台开放已来,我们收到了大量开发者的咨询和反馈,越来越多开发者基于Apollo擦出了更多的火花,并愿意将自己的成果贡献出来,这充分体现了Apollo『贡献越多,获得越多』的开源精神。为此我们开设了『开发者说』板块,希望开发者们能够踊跃投稿,更好地为广大自动驾驶开发者营造一个共享交流的平台!

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 神经网络
    +关注

    关注

    42

    文章

    4762

    浏览量

    100517
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62410
  • 无人车
    +关注

    关注

    1

    文章

    299

    浏览量

    36455

原文标题:开发者说丨Apollo感知解析之MinBox障碍物边框构建

文章出处:【微信号:Apollo_Developers,微信公众号:Apollo开发者社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    遇到障碍物自动返回?

    为什么有些东西,遇到障碍物会自动返回,是哪一种传感器吗?哪里有没有这类的电路图介绍下,非常感谢
    发表于 09-10 21:14

    设计完PCB后走线不能躲避障碍物时什么情况

    本帖最后由 gk320830 于 2015-3-8 20:36 编辑 设计完PCB后走线不能躲避障碍物时什么情况?已经设置了躲避障碍物还是不能躲避
    发表于 12-20 08:47

    求教 障碍物感应类开关

    本帖最后由 gk320830 于 2015-3-8 14:20 编辑 急需用到2米内障碍物感应器,带开关功能或者高手帮忙设计个辅助开关电路,24V电源供电,多谢
    发表于 03-07 21:58

    基于单片机的道路障碍物系统,新人求教

    我想要做一个道路障碍物检测系统。就是在路面上安一个检测器,一旦有障碍物挡住,且在路上停留一段时间,就发射信号报警,使二极管发光。现在我想请教一下是不是检测用红外线好一点,延时用单片机程序大概要怎么写?电路什么的怎么连啊?求大神解答
    发表于 03-10 13:33

    障碍物时 语音提示 前方危险,请注意。

    障碍物时语音提示前方危险,请注意。语音模块是isd1820单片机51 求帮忙
    发表于 02-24 21:43

    基于labview机器视觉的障碍物时别

    通过摄像头对周围环境信息的实时采集,如果当镜头前方出现障碍物时候,以一定的方式(声音或振动之类的)反馈出来。主要考虑的是实现盲人室内导盲作用,不需要太过于考虑实际使用,只要能实现判定到障碍物,自动提醒就行啦。怎么判定前方出现障碍物
    发表于 03-14 07:58

    新人求教AD中Pullback(障碍物)到底是一个什么概念或作用?

    ,即如果设置的障碍物值为20mil,那么在板的边框外面有20mil的覆铜,在边框的里面也有20mil的覆铜。”2、“Pullback”,是在内电层边缘设置的一个闭合的去铜边界,以保证内电层边界距离PCB
    发表于 11-04 11:27

    请问Infrared Proximity Sensor如何检测前方是否有障碍物

    Infrared Proximity Sensor如何检测前方是否有障碍物
    发表于 11-06 07:57

    检测障碍物有什么什么传感器?

    检测障碍物有什么什么传感器,用红外反射还是超声波测距,或者还有其他传感器?
    发表于 11-08 06:33

    汇编语言程序设计案例—动态显示/障碍物检测/障碍物方位检测

    汇编语言程序设计案例1—动态显示/障碍物检测/障碍物方位检测 10-1.  LED数码管显示原理10-2.  案例分析1(2位学号显示)10-3.  案例分析2(简易按键抢答)10-4.
    发表于 03-23 12:15 38次下载

    障碍物检测实验

    障碍物检测实验 一、实验目的“旋风”小车在运动过程中要成功避开障碍物必须在一定距离外就探测到障碍物。在小车上探测障碍物
    发表于 03-23 10:47 2796次阅读
    <b class='flag-5'>障碍物</b>检测实验

    障碍物方位检测实验

    障碍物方位检测实验 一、实验目的小车在运动中要成功避开障碍物,除了要检测是否有障碍物外,还需要判断障碍物的方位,以便小
    发表于 03-23 10:48 1987次阅读
    <b class='flag-5'>障碍物</b>方位检测实验

    使用IR模块的障碍物检测器

    电子发烧友网站提供《使用IR模块的障碍物检测器.zip》资料免费下载
    发表于 11-14 11:31 0次下载
    使用IR模块的<b class='flag-5'>障碍物</b>检测器

    如何让Arduino使用雷达避开障碍物

    电子发烧友网站提供《如何让Arduino使用雷达避开障碍物.zip》资料免费下载
    发表于 06-26 14:58 1次下载
    如何让Arduino使用雷达避开<b class='flag-5'>障碍物</b>

    基于点云的3D障碍物检测介绍

    基于点云的3D障碍物检测 主要有以下步骤: 点云数据的处理 基于点云的障碍物分割 障碍物边框构建 点云到图像平面的投影 点云数据的处理 KI
    的头像 发表于 06-26 10:22 918次阅读
    基于点云的3D<b class='flag-5'>障碍物</b>检测介绍