E2E Self-Driving:3000行的端到端导航tinynav+tinybev
PUBLIC人形机器人最近吸引了非常多的目光。

在 具身智能 的热潮下,可能首先需要做的,就是分清本体控制能力和智能能力。
它们不仅定义不同,技术路线也已经分道扬镳。
我更关注智能,而其中,相比于手上的能力,我也更关注腿上的能力,也就是导航的能力。
我计划动手写一套导航软件,可以:
- 多本体:四足狗,人形,小车
- 代码精简:选择这个领域的一些最佳实践,并且适合作为各种想法的试验田
- 容易上手:尽量少地需要调参,至少针对特定的本体和计算平台,开箱即用。
场景
在开始之前,我介绍几个场景,这样在理解下面的技术选择的时候,脑中有一个画面,可能更便于进一步讨论。
- 题图中的马拉松,基本是给定地图的固定路线巡线,但是希望带有局部避障能力,工厂机器狗巡检类似。
- 无人机的APAS功能,用户推杆大概告诉机器人的方位,然后机器人选择平滑安全的轨迹去执行,没有特定的终点。
- POI控制。在地图中打下若干个POI后,接收到达某点的指令去执行。比如扫地机回仓充电,或者酒店机器人送快递。
平台配置和定位
先从定位说起,选择的传感器配置是双目+IMU,第一等支持的模组就是:

可能对比几个其他的方法,更好理解这个选择:
为什么不用Lidar 确实要承认,现在似乎Lidar的定位非常稳定。从找简历的经历来看,能做Lidar定位的人至少是3倍多于能做Camera定位的人。
但是,我依旧选择了双目为主。
一个原因是,我觉得我能搞定,我不觉得相机在什么场景下会在理论上限上低于Lidar。
第二个原因是,我还是希望这一套算法栈是用在2C产品上的,那么Lidar依旧是有明显成本劣势。
第三个原因是,我希望能用算力换来性能。
为什么不用轮子,或者关节数据,或者GPS做紧耦合 应该是可以提高性能的。但是从过去的经验来看,每多一个传感器,都要多很多段故事。
或者说如果我希望这套算法栈能被人用起来,传感器上的“紧密”程度越低越好。
这里的权衡就是到双目模组。如果更低一步,变成单目+imu,感觉又太难了。
实现技巧
VIO本身并不复杂:最小化投影误差+最小化IMU积分误差即可。用的优化方法也是非常经典的高斯牛顿。
但是我上学时候写的VINS有一万五千行C++代码。

对比之下,tinynav的感知模块,带上双目,也只有639行。
VIO比较复杂的地方在于:
- 初始化比较难,pose依赖point cloud,point cloud依赖pose。不仅难实现,还很容易影响性能。
- bookkeeping比较复杂,给图查点,给点查图。
与之对应,简化实现的方法也主要有两个:
- 有双目之后,初始化变得很简单。也会更加稳定。
- Disjoint Set + memorized pure function,基本实现了等价的代码变换: 最终伪代码可以写成
x = argmin sum(cost_function(track) for track in disjointset())
轨迹算法
非常high-level地介绍轨迹算法,可以把它看作一个搜索问题。对比上面提到的定位更像一个优化问题。
顺便插入一句,这里的说法非常粗糙,如果说不关心问题的连续性,那么也可以说大家都是优化问题。
以搜索的视角,轨迹算法是在评价一系列选择之间的孰优孰劣。
这个评价,可以用判别的思路来做:给定若干条轨迹,对每一个进行打分,选择分数最高的。
也可以用生成式的方法来做:根据条件或者先验,随机生成若干轨迹,或者干脆选择概率最大的轨迹。
tinynav在这里选择的是判别式的方法,生成式的方法放在tinybev中用flow matching conditioned on BEV来做。另外再做介绍。
候选轨迹的表示,最直接的定义是控制信号,这里的选择是线速度和角速度,共3个自由度。
而轨迹的平滑分数中,最重要的是碰撞分数,由轨迹和双目深度raycasting得到的地图做碰撞计算得到。
模型推理
第一等支持的计算平台是Nvidia Jetson Orin Nano。所以对应的模型优化也变得标准化了。
在小电脑和大电脑中,提到过对于计算的分析,可以归结到roofline model。
这里遗留一个todo分析一下这几个模型的特征。
对于一个跑在edge的网络,其实也是一个有限的搜索问题,特别是给定输入尺寸之后。
有这么几个重要的工作:
- 等价的计算图变化,或者说最基础的消除掉对于output来说没有贡献的节点。
- 算子融合:conv+relu和flashattention大体都算这一类,两个算子的中间结果online到sram而不是写到内存里。
- kernel选择,针对特定的算子和特定的平台,可能不同的tile策略对性能的影响很大。同时根据实际测速,来选择最快的kernel实现。
- 量化。
有了tensorrt之后。。能做的东西就很少了。
这也启发我做了另外一个项目,pi.cpp: Robot Foundation Models inference in C/C++。
地图,数据闭环
在前面提到的三个场景中,第二个场景和其他场景有一个最大的不同,就是有地图。
这里不是讨论技术的先进与否,而是在某些场景下,有一个地图,确实是更好的交互界面。
给定一张照片,知道自己在地图的位置。其实是一个挺难的任务。
用现在的说法,他是一个image domain的reasoning任务。可以回忆一下我们是不是有过,到了一个地方,觉得眼熟,然后各种找细节,才能知道这里自己来过没有。
这个问题的最经典的例子是:

这也是一个更好的我身边的例子,基础模型的发展,解决了垂直领域的问题。
简单来说,dino的CLS token已经是一个足够好的global feature。
所以我们使用这个feature做检索,再配合robust pose graph,建图就足够稳定了。
在AI的时代,依旧重视代码精炼
一方面,我觉得代码少,还是体现了某种审美和判断,对于系统中哪些是重要的,哪些是辅助性质的。
另一方面,确实也看到了一些代码,用几十万行甚至上百万行来实现我觉得可以几千行实现的功能。
面对这些代码,我试着去理解,试着用AI去改,但是都陷入了某种困境。
希望在不远的将来,AI能解决这些问题吧。