用尽心思做了个技术方案,却被专业人士啪啪打脸

公众号|沐洒(ID:musama2018) 关注我,带你学点有用的

老粉应该都知道,我是一个不幸闯入自动驾驶领域的前端仔,去年开始陆续在这个领域做了一些事儿,不过后来因为一些变动,调整去支持另外的项目。直到最近,才又回来接手自动驾驶相关的事情。

所以你会发现前阵子我都没怎么写这个领域的东西,主要还是因为暂时离开了这个项目。

Anyway,现在我又回来了!

这次回归自动驾驶话题,自然不会让各位失望,我给大家带来了一篇对技术人而言极其实用的干货,关于「一次小小的技术方案预研」。

对方案细节不感兴趣,只想学如何做技术方案预研的同学,可以直接跳到结尾总结部分。

话不多说,进入正题。

1. 背景概述

Q平台(化名)上的有一个模块,用户可以在这里训练自己的自动驾驶标注能力。而训练必然要有考核,也就是对训练结果进行准确性校验,说白了就是一个自动阅卷能力。

毕竟自动驾驶点云数据长这样:

数据量庞大不说,这事儿本身用人眼分辨准确性也不是那么容易的事。所以咱需要一个「3D点云数据准确性自动化校验」的能力。

2. 需求文档

这部分涉及内部敏感信息,就不展示了。

我们假装这里有一份产品经理给你提的需求文档链接

需要自动校验的数据类型有:

3D框

2D框

车道线

关键点

点云多边形

And more......

3. 技术预研

初步分析,上述N种数据类型,根据实现原理分为2D和3D两种,可以通过一个统一的diff文件向外统一输出校验接口,然后根据入参表达的数据类型分发到不同的diff算法,如下:

arduino 复制代码
diff
|---index.ts  // diff出口文件
|---box.ts    // 3D框
|---rect.ts   // 2D框
|---line.ts   // 车道线
|---point.ts  // 关键点
// 省略更多类型

// index.ts
const diffFunctions = {
  [SomeEnum.box]: xxx,
  [SomeEnum.rect]: xxx,
  // ...略
}

export const diff = (data: SomeType, type: SomeEnum) => {
  return diffFunctions[type](data);
}

入口分发不难,难点在不同类型的数据校验算法逻辑(本文只讲3D校验的部份)。

3.1 初步思考

要校验「用户3D框」和「系统标准框」是否一致,不可以直接粗暴的做框体坐标的比对,于是我换了一个思路:

想办法判断出两个框体的叠加区域(或非叠加区域),然后看这个区域的尺寸是否小于某个系统设定的阈值

就这么干!

3.2 竞品分析

目前开源的3D框体交叉检测代码非常少,而且很多都已经很陈旧了。千辛万苦找到两个看起来可能行的库进行了尝试(box-intersectglaabb3)。

3.2.1 box-intersect

NPM包地址:<www.npmjs.com/package/box...

GITHUB:github.com/mikolalysen...

这个包可以比对多个立体框,每个框都按照扁平化的坐标数组传入([minX, minY, minZ, maxX, maxY, maxZ]),然后输入一个新数组,类似这样:

原以为这个crossings可以返回立方体叠加的区域坐标,然而经过实验后发现并不是,这个仅仅是一个立方体在输入数组里的序号(index)!

也就是说,这个库仅仅只能判断哪些立方体有叠加区域。这个效果显然不符合我的预期,放弃。

3.2.2 glb-aabb3

NPM包地址:www.npmjs.com/package/gl-...

这个库太老旧了,连文档都没有,不知道有哪些API,我只能看源码:

尝试一下其中的「intersection」 方法:

ini 复制代码
import gl from 'gl-aabb3';

const red = [0, 0, 0, 4, 4, 4];  //Format: [minX, minY, minZ, maxX, maxY, maxZ]
const blue = [1, 0, 0, 6, 4, 4];
const vec3 = [];
gl.intersection(vec3, red, blue);
console.log(vec3);

输出:

用THREEJS把这三个立体框画出来,就是这样:(为了直观的看到效果,我把叠加部分vec3往Z轴方向挪了4个单位,与原数据错开)

可以看到效果是不错的,确实拿到了叠加区域。但是我们对蓝色立方体进行45度旋转再看看:

哦嚯!不行,交叉区域并未带入旋转分量。

尝试下使用BoundingBox:

害!这就更加不对了。

至此,两个开源库宣告退赛。

3.3 方案设计(自研)

在大量的尝试和搜索过程中,我接触到一个新的概念,AABB (axis-aligned bounding box),中文直译的话就是"轴对齐包围盒"。

这个AABB在处理3D碰撞检测领域被广泛使用,仔细研究了相关方案,大概心里有点思路了。(AABB相关知识可以参考我之前的文章《这个知识点99%的前端都没有听过,不信你进来看?》)

其中包围盒之间的冲突检测方式如下图:

简单讲就是,「使用AABB,把一个旋转物体包裹在内,不管物体如何旋转,AABB始终能包裹住物体,那么物体之间的碰撞就可以用AABB碰撞来做检测」。

回到我们这个场景,其实就是判断两个AABB的差异 是否小于某个阈值

看到这里别着急喷,友情提示,后面有彩蛋

4. 最终方案

最终暂定使用AABB来做检测,先分别计算出两个box的AABB:(这里直接用THREEJS的API)

ini 复制代码
cube.geometry.computeBoundingBox();

设定一个误差阈值:

ini 复制代码
const POS_GAP_LIMIT = 0.1; // 这个值可以配置

然后分别对这两个AABB的[minX, minY, minZ, maxX, maxY, maxZ]进行误差判断,若小于阈值,则我们认为两个盒子基本一致。

javascript 复制代码
function positionEquals(a, b) {
  const [axmin,aymin,azmin,axmax,aymax,azmax] = a;
  const [bxmin,bymin,bzmin,bxmax,bymax,bzmax] = b;

  return (Math.abs(axmin - bxmin) <= POS_GAP_LIMIT &&
          Math.abs(aymin - bymin) <= POS_GAP_LIMIT &&
          Math.abs(azmin - bzmin) <= POS_GAP_LIMIT &&
          Math.abs(axmax - bxmax) <= POS_GAP_LIMIT &&
          Math.abs(aymax - bymax) <= POS_GAP_LIMIT &&
          Math.abs(azmax - bzmax) <= POS_GAP_LIMIT;
}

这里还存在一个需要在开发调试中进行探索确认的点,就是是否还需要进一步比对两个box的旋转分量,因为旋转分量会直接影响盒子的方位,而且对称的旋转分量很可能产生相同的AABB

比如这个情况,红蓝两个盒子分别旋转45度和-45度,二者明显不一致,但是二者的AABB是完全一致的。那么这种情况下如果单纯的依赖AABB检测,就会失效,所以我倾向于引入rotation协同检测

也同样设置一个旋转误差阈值:

ini 复制代码
const ANGLE_GAP_LIMIT = 1; // 角度误差可配置

然后比对二者的旋转角度即可:

javascript 复制代码
function rotationEquals(a, b) {
  const [axr, ayr, azr] = a;
  const [bxr, byr, bzr] = b;

  return (Math.abs(axr - bxr) <= ANGLE_GAP_LIMIT &&
          Math.abs(ayr - byr) <= ANGLE_GAP_LIMIT &&
          Math.abs(azr - bzr) <= ANGLE_GAP_LIMIT);
}

通过positionEquals + rotationEquals 协同比对,可以更有效的检测出两个框的方位一致性。

以上是针对3D立方体比对的最终方案。

当然了,实际落地方案比这个要复杂一些。

5. 总结

面对纷繁复杂的业务需求,很多时候方案是不明朗的,甚至是完全未知的,这种时候,就需要一个技术人员勇敢站出来(通常是高职级),先去前边儿探探路,写一份技术预研文档,帮助团队指明方向。

而一份最简版的技术方案预研,可以参考这个框架:

  • 背景

  • 需求

  • 挑战/思路

  • 竞品分析

  • 方案设计

沿着这个脉络写文档,就算是新手,也能写得大差不差了。

6. 彩蛋

作为一个3D开发领域的新人,在无人指导的情况下摸索到了AABB解法,别提多开心了,于是我吭哧吭哧把上面的方案落地了,效果也还不错。

然而,有一天,一个偶然的机会加了一个资深的图形化开发大佬,他一句话,给我整破防了:

怀着将信将疑的心情,于是我去搜了一下,然后惊讶的发现,窝草,这不就是我想要的东西么!!!

AABB和OBB的区别如下图:

用OBB矩阵来解决这个问题,简直完美!天然带入了旋转分量,根本不需要我自己进行拙劣的逻辑判断,直接用线性代数就搞定!

所以说,在未知领域,找到一个引路人是多么重要。


技术交流群

我建了个前端技术交流群,整体氛围非常好,每天大家都会在群里真诚的交流技术问题,从前端到全栈,从尚未毕业的小年轻,到不惑之年的老大哥,大家都在拼命的汲取知识!

最近群里正在组织前端项目实战练习,很快就要开始大干一场了!有想要交流&学习前端知识的老少朋友,欢迎加入我们!

进群方式【重要】:

关注公众号 "沐洒 ",回复"技术群",获取最新群聊二维码。


全文完。

码字不易,如果你还想继续看我写的东西,就关注我吧(记得加星标🌟哦),顺便给个赞👍或点一下在看,你的支持是我继续写下去的动力。

公众号|沐洒(ID:musama2018) 关注我,带你学点有用的

相关推荐
hackeroink8 分钟前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者2 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
睡觉狂魔er4 小时前
自动驾驶控制与规划——Project 3: LQR车辆横向控制
人工智能·机器学习·自动驾驶
m0_748240255 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript