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

公众号|沐洒(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) 关注我,带你学点有用的

相关推荐
周三有雨7 分钟前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
木古古1820 分钟前
使用chrome 访问虚拟机Apache2 的默认页面,出现了ERR_ADDRESS_UNREACHABLE这个鸟问题
前端·chrome·apache
爱米的前端小笔记29 分钟前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
loey_ln1 小时前
webpack配置和打包性能优化
前端·webpack·性能优化
建群新人小猿1 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
爱上语文1 小时前
HTML和CSS 表单、表格练习
前端·css·html
djk88881 小时前
Layui Table 行号
前端·javascript·layui
今天啥也没干1 小时前
使用 Sparkle 实现 macOS 应用自定义更新弹窗
前端·javascript·swift
NightCyberpunk2 小时前
HTML、CSS
前端·css·html