想象你走进一间漆黑的工作室,面前摆着一尊还没完工的巨大雕像。你看不见它,只能伸出手,一寸一寸地抚摸。每一次指尖触到粘土,你都在心里默默记下一个数字:“这一点离表面两根手指远,在雕像外面,记 +2”;“这里已经陷进粘土里,记 −1”。随着你反复触摸,工作室里的每一寸空气都被贴上了这样的“距离标签”。把这些标签集合起来,就是一张有符号距离场(SDF)——它回答两个问题:空间中任意一点离最近的表面有多远?这个点是在表面外面(正号)还是里面(负号)? 问题来了:雕像那么大,空间无穷多,给所有位置都贴标签太费力。聪明的雕刻家只会在表面附近一小层仔细标注,远处统一写“很远”——这就是截断,诞生了 TSDF(截断有符号距离场)。而当雕像完工,要把它搬出狭窄的门洞时,他又需要精确知道每一步的安全距离,于是补全整个空间的完整距离——这就是 ESDF(欧氏有符号距离场)。本文就用这座“黑暗中的雕塑”的故事,把机器人建图的两大招牌技术讲透。
对 AI 从业者来说,TSDF/ESDF 是 3D 视觉感知与自主导航之间的“翻译官”:搞懂它们,你就能理解从 KinectFusion 三维重建到仓储机器人避障的核心原理,快速上手 SLAM 与路径规划岗位。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
有符号距离场
雕刻家每摸到一个点,就相当于给空间的某个位置贴一张便签。便签上只写两件事:离最近表面的距离数值,以及在表面内外的方向——用正负号表示。当整个工作室的地板、墙面、空气中都贴满了这样的便签,就构成了一张“有符号距离场”。它不描述“哪里有东西”,而描述“这里离东西有多远、在东西里面还是外面”,这是它和普通占用地图最大的不同。
在机器人世界里,我们不可能给连续空间贴满便签,而是先把空间切成许多小立方体,这些立方体叫做体素(Voxel),就像把工作室地板铺上方格。每个体素里放一块“小石子”,上面刻着一个数字:它到最近表面的距离。所有体素合起来,就是离散化的距离场。体素越小,方格越密,便能刻画越精细的曲面,但代价也越大——这一点我们留到“工程调优”一节细说。
形式化地写,对于空间中的任意一点 $\mathbf{x}$,设场景中所有表面点的集合为 $\mathcal{S}$,那么该点的有符号距离值是:
就像雕刻工作室的地板被铺上带刻度的方格,机器人想知道自己离墙多远,低头看一眼脚下那块石子上的数字即可;符号则告诉它:此刻是站在粘土外面,还是正身处雕塑肚子里。
- 表面点集合 $\mathcal{S}$所有物体表面的点组成一个集合,就像雕塑表面每一个可以触摸到的位置。
- 最近表面点对每个候选表面点 $\mathbf{p}$ 算距离,取最小值,找到“离我最近的表面点”。
- 欧氏距离$\left\|\mathbf{x}-\mathbf{p}\right\|$ 就是两点间的直线距离,也就是“手指到表面的空间距离”。
- 内外符号分段函数给“表面外”加上正号、“表面内”加上负号,这就是“有符号”三个字的来历。
TSDF原理
雕刻家很快发现,给整个工作室都贴精确便签是白费力气:离雕塑三米远的墙角,贴“3 米”和“3.1 米”毫无区别。于是他只给表面附近一个“手肘长度”(记为 μ)以内的空气贴精确标签,更远的地方统一写“很远”。这个“只在表面附近精雕细琢”的设定,就是截断距离 μ。由此得到的场,叫作 TSDF——Truncated SDF,截断有符号距离场。
TSDF 把每个体素的距离值除以 μ 归一化,并且把结果截断在 $[-1, +1]$ 之间:表面处值为 0,表面外为正(最大 +1),表面内为负(最小 −1),超出截断带的一律记为 ±1。这样做的好处是:内存只花在表面附近,而表面附近恰恰是重建最需要细节的地方。
光有截断还不够。雕刻家每次触摸同一处,由于手抖、角度不同,读数会有误差。多摸几次后,他该信哪一次?答案是做加权平均:可信度高的触摸(正对着表面、距离近)权重大,可信度低的权重小。设第 $i$ 帧在体素 $\mathbf{x}$ 处测得值为 $f_i$、可信权重为 $w_i$,那么融合公式为:
回到工作室:雕刻家每次触摸只更新近表面一层便签,同一处被摸到多次后,便签上的数字取加权平均——手再抖,误差也会在一次又一次的触摸中被“平均”掉,雕塑的轮廓在黑暗中逐渐变得坚实而清晰。
- 新测量值 $f_i$当前帧在体素处算出的归一化截断距离。
- 可信权重 $w_i$权重通常由传感器与表面的夹角、测量距离决定,正对表面时最可信。
- 旧值加权保留旧值乘旧权重、新值乘新权重,再相加,体现“两边都参考”。
- 权重归一除以总权重,得到更新后的平均距离值,同时累积权重 $W_i$。
ESDF原理
雕像完工,要把它搬出狭窄的门洞。这时只有表面附近的便签就不够用了:机械臂旋转时,任何一个凸起的肩部都可能撞上门框。雕刻家需要知道全空间的准确距离——从雕像表面到每一面墙、天花板、地板,每一个位置的直线距离都要心里有数。这种“全场真实距离”的字段,就是 ESDF——Euclidean SDF,欧氏有符号距离场。
ESDF 也建立在体素网格上,但每个体素存的不是“相对表面有多近”,而是“离最近的障碍物有多远”。对自由空间里的点,这个值是正数;障碍物内部的点则为负。由于它保存了全空间的真实距离,查询任意一点到最近障碍的距离都可以在常数时间内完成——这正是路径规划最需要的“体检报告”。假设障碍物区域为 $\mathcal{O}$:
在实际导航中,我们还会把机器人自身的半径考虑进去:查询距离时直接用 $D(\mathbf{x})-r_{\mathrm{robot}}$ 作为有效安全距离,相当于把障碍“膨胀”了一圈。把 ESDF 想象成一张布满“雾”的舒适度地图,越亮的地方离障碍越远;梯度就是雾中的“风向标”,永远指向更空旷的方向。机器人顺着风向标微调身形,就能安然穿过门洞。
- 障碍集合 $\mathcal{O}$所有占据空间的体素集合,相当于雕像和墙壁占住的位置。
- 最近障碍点在障碍集合里找离 $\mathbf{x}$ 最近的点,记为 $\mathbf{p}^{*}$。
- 真实欧氏距离$D(\mathbf{x})$ 就是当前点到最近障碍的直线距离,单位是米。
- 梯度方向$\nabla D$ 是一个单位向量,永远指向“远离最近障碍”的方向。
建图流程
一次完整的建图,就像雕刻家一次完整的“摸骨”:先确定自己的手在哪里(位姿估计),再沿手指方向感受哪里碰到了表面(深度测量),最后更新对应的便签(体素更新)。传感器换成深度相机或激光雷达,“手指”就是一束束射线。
具体来说,TOF 相机或激光雷达发射一束射线,遇到表面后返回深度值 $z_s$。对于同一条射线上的每个体素,设它沿光轴到传感器的深度为 $z(\mathbf{x})$,那么该体素的符号距离近似为“体素深度减去测量深度”:
整个流程串起来就是:深度帧输入 → 位姿对齐 → 体素更新 → 地图输出。其中位姿对齐由里程计或 SLAM 提供,位姿一旦有偏差,地图就会“重影”;体素更新则用前面讲的加权融合把多帧数据揉在一起。雕刻家每次触摸都是一帧“深度图”,他先确认自己站在哪儿,再记录指尖到表面的深度,最后把新便签与旧便签做加权平均——一轮轮循环,一尊立体的雕像就在黑暗中慢慢成形。
- 体素深度 $z(\mathbf{x})$体素中心沿传感器光轴到光心的距离。
- 测量深度 $z_s$射线命中表面后,传感器记录的真实深度读数。
- 作差得符号距离$z(\mathbf{x})-z_s$ 的正负正好对应体素在表面前方还是后方,大小对应离表面远近。
- 截断与归一把算出的 sdf 带入上一节的截断公式,再与旧值做加权融合,就完成了一次体素更新。
SLAM应用
有了 TSDF 地图,雕刻家终于可以“看见”完整的雕像了。但体素里存的是距离值,不是表面本身——该怎么把表面“取”出来?答案很漂亮:表面恰好穿过那些距离值为零的位置。
在体素网格中,表面一侧的体素 TSDF 为正、另一侧为负。只要找到相邻体素符号发生变化的“临界地带”,再用线性插值求出距离恰好为零的亚体素位置,就能拼出光滑的表面网格。这个操作叫表面提取,三维中常用的 Marching Cubes 算法做的正是这件事:
在 RGB-D SLAM 中,TSDF 是稠密重建的“常客”:KinectFusion 把每一帧深度图实时融合进 TSDF 体素场,再用光线投射直接渲染出表面;Open3D 等库则帮助把多帧点云通过 ICP 配准对齐,再统一融进同一张 TSDF 网格。雕刻家凭着上千次触摸零点的位置,把一张张“指纹”(点云帧)严丝合缝地拼在一起,终于拼出雕像完整的肌肉与轮廓。
- 表面集合 $\mathcal{S}$所有满足条件的位置 $x$ 组成的集合,就是提取出的表面。
- 正负跨越在相邻体素间观察 TSDF 值是否从正变负或从负变正,跨越处必有表面。
- 线性插值用两边体素的数值按比例逼近零点,得到亚体素精度的表面坐标。
路径规划
现在轮到助手出场:一个轮式机器人要把雕像搬出门。它手里有三件法宝——目标方向(门的方向)、ESDF 距离表,以及表上的梯度“风向标”。每走一步,它查一下脚下体素的距离和梯度,就能决定该怎么调整。
ESDF 的梯度永远指向“远离最近障碍”的方向,所以机器人把速度拆成两股:一股朝目标前进,一股沿梯度方向“往外顶”,合成后的速度既靠近门,又被障碍“推开”。这个简单而优雅的思路写出来就是:
工程上更成熟的方案是轨迹优化(如 Chomp):把 ESDF 的距离和梯度写进代价函数,算法反复迭代,把一条原本会剐蹭的轨迹“顶”离障碍物,同时保持平滑。动态场景中,还可以比较当前 ESDF 与历史 ESDF 的距离突变,快速发现新出现的障碍。就像助手双手端着雕像,身体感受着四面八方“空气的推力”(梯度),每当靠近门框,推力就轻轻把它推回安全区,最终有惊无险地挪出门洞。
- 目标速度 $\mathbf{v}_{\mathrm{goal}}$指向最终目标点的期望速度,大小由规划层决定。
- 距离梯度 $\nabla D(\mathbf{x})$ESDF 中当前点的梯度,指向远离最近障碍的方向。
- 安全系数 $\lambda$控制“避障力度”的权重,离障碍越近时 $\lambda$ 往往调得越大。
- 速度合成两向量相加,得到兼顾“走向目标”与“远离障碍”的最终速度指令。
工程调优
终于到了落地环节。雕刻家的工作室地板方格该铺多密?格子越小,雕像细节越真,但地板要铺的格子数量按三次方爆炸式增长。机器人建图同样面临这个核心权衡:体素分辨率与内存、算力的取舍。
假设建图区域是边长 $L$ 米的立方体,体素边长是 $r$ 米,每个体素存距离和权重需要 $B$ 个字节,那么总内存为:
工程经验上,截断距离 $\mu$ 通常取 5~10 个体素边长:太小则传感器误差导致空洞、融合不到帧;太大则远处的截断值污染表面细节。内存吃紧时,可以用哈希体素只为表面附近的截断带分配内存(TSDF 天然适合这种“稀疏存储”),或者先粗建图确认轮廓,再在重点区域局部细化。聪明的雕刻家正是这么干的:先粗铺一遍方格确认轮廓,再在雕塑肩膀、脸部换细方格,门框周围加密便签,而不必给整间屋子铺满——该省的地方省,该精细的地方精细,这才是工程的真功夫。
- 地图边长 $L$建图区域的尺寸,比如一个 10 米见方的房间。
- 体素边长 $r$每个小立方体的边长,$r$ 越小分辨率越高。
- 体素总数量每维体素数 $(L/r)$ 的三次方,决定网格的总单元数。
- 每体素字节数 $B$距离值加权重通常 8~16 字节,直接乘上去就是总内存。
AI 应用场景
手持 LiDAR 实时三维重建
阶段:感知与稠密建图(SLAM)
解决的问题:传统多视角照片重建需要数小时离线处理,有空洞且无法实时预览。基于 TSDF 的体素融合把每一帧深度图实时“揉”进同一个网格,边扫描边出模型,多帧互补自动填补空洞。
具体案例:家装设计师用手机上的 LiDAR 应用绕着客厅走一圈,2 分钟内得到“水密”的 TSDF 网格模型,可直接导入 CAD 量尺寸、摆放虚拟家具。Apple RoomPlan、Intel RealSense 的 TSDF 重建均是此类实现。
仓储 AGV 导航避障
阶段:全局与局部路径规划
解决的问题:2D 栅格地图把障碍压扁成“一堵墙”,无法感知悬空货架和低矮包裹,贴边行驶容易刮擦。ESDF 在体素网格上提供任意点到最近障碍的真实距离和梯度,轨迹优化器可以把路径平滑地“顶”离高危区域。
具体案例:某电商仓库的潜伏式 AGV 在密集货架阵中行驶,规划模块读取 ESDF 梯度生成平滑轨迹,同时根据行驶速度动态增加障碍膨胀距离,通道通过率提升约 30%。
无人机室内巡检安全走廊
阶段:感知融合与飞行规划
解决的问题:无人机在无 GPS 的厂房内飞行,需要随时知道与墙、柱、桁架的距离。TSDF 负责构建稠密环境模型,ESDF 提供全空间距离查询,规划器据此生成满足安全间隔的飞行走廊。
具体案例:变电站巡检无人机先用 TSDF 完成定位与建图,再用 ESDF 评估每条候选航线的“最近距离代价”,自动选择离设备最远、最安全的走廊飞行。
Python 演示
import math class SimpleTSDF: """极简二维 TSDF 体素地图:截断距离 + 权重融合""" def __init__(self, size=50, voxel=0.1, trunc=0.5): self.size = size self.voxel = voxel self.trunc = trunc self.values = [[0.0] * size for _ in range(size)] self.weights = [[0.0] * size for _ in range(size)] def update(self, sx, sy, weight=1.0): """融合一次表面测量点 (sx, sy),更新其截断带内的体素""" idx = int(sx / self.voxel) idy = int(sy / self.voxel) radius = int(self.trunc / self.voxel) + 1 for di in range(-radius, radius + 1): for dj in range(-radius, radius + 1): ni, nj = idx + di, idy + dj if not (0 <= ni < self.size and 0 <= nj < self.size): continue cx = (ni + 0.5) * self.voxel cy = (nj + 0.5) * self.voxel dist = math.hypot(cx - sx, cy - sy) if dist > self.trunc: continue f = min(dist / self.trunc, 1.0) old_w = self.weights[ni][nj] self.values[ni][nj] = ( self.values[ni][nj] * old_w + f * weight) / (old_w + weight) self.weights[ni][nj] = old_w + weight def build_esdf(self): """从 TSDF 找表面体素,为每个体素计算真实欧氏距离""" surf = [(i, j) for i in range(self.size) for j in range(self.size) if 0.0 < self.values[i][j] < 0.4] esdf = [[0.0] * self.size for _ in range(self.size)] if not surf: return esdf for i in range(self.size): for j in range(self.size): esdf[i][j] = min(math.hypot(i - si, j - sj) for si, sj in surf) * self.voxel return esdf def main(): tsdf = SimpleTSDF(size=50, voxel=0.1, trunc=0.5) # 模拟激光雷达从两个视角扫描同一面墙 for x in range(5, 45, 3): tsdf.update(x * 0.1, 2.0, weight=0.8) tsdf.update(x * 0.1, 2.05, weight=1.2) esdf = tsdf.build_esdf() near = [v for row in tsdf.values for v in row if v > 0] print("TSDF 表面附近体素数:", len(near), "平均距离:", round(sum(near) / len(near), 3)) print("ESDF: 体素(10,14)到墙距离:", round(esdf[10][14], 3), "米") if __name__ == "__main__": main()
self.values = [[0.0] * size for _ in range(size)]为每个体素分配一个距离值,初始全为 0。radius = int(self.trunc / self.voxel) + 1计算截断半径,只更新表面附近的体素,体现 TSDF 的“局部精雕”。if dist > self.trunc: continue截断判断:超出 μ 的体素直接跳过,不参与融合。self.values[ni][nj] = (self.values[ni][nj] * old_w + f * weight) / (old_w + weight)加权平均融合:旧值和新值按权重比例混合,多帧测量越摸越准。if 0.0 < self.values[i][j] < 0.4把距离值较小的表面附近体素当作“障碍种子”,用于生成 ESDF。min(math.hypot(i - si, j - sj) for si, sj in surf) * self.voxel对每个空闲体素求到最近障碍种子的欧氏距离,再乘体素物理尺寸换算成米。tsdf.update(x * 0.1, 2.0, weight=0.8)模拟一次带权重的表面测量:不同角度给不同置信度。
常见误区
误区“TSDF 是重建用的,ESDF 是导航用的,两个系统各建各的图,要跑两遍建图流程。”
正解它们共享同一个体素网格。TSDF 先建出表面附近的截断距离场;ESDF 可以由 TSDF 或占用栅格通过快速推进、降维传播等算法转换而来(如 voxblox、OpenVDB)。工程上常常“一张地图,两种读法”:重建模块读 TSDF,规划模块读 ESDF,而不是跑两遍建图。
为何常见:因为两者常出现在不同模块(重建模块 vs 规划模块),看上去像“两套地图”,其实底层是同一个体素空间。
误区“μ 设得越大,保存的信息越多,地图肯定越精细。”
正解μ 太大时,远处体素的截断值 ±1 会混进表面附近的融合计算,把真实细节“拉平”;同时待更新体素数随 μ 三次方增长,计算量大增。经验值是 5~10 个体素边长,既要覆盖传感器深度误差,又要保住表面锐度。
为何常见:直觉上“多存总比少存好”,却忽略了截断值本身就是一种有损近似,存得越多噪声越多。
误区“ESDF 数值越大越安全,那就朝周围数值最大的方向一直走。”
正解梯度只告诉“远离当前最近障碍”的方向,它既不理解全局目标,也无法预知远方还有哪些障碍。路径规划要把 ESDF 距离和梯度当作代价函数的一项,与目标吸引、运动学约束一起联合优化,而不是无脑贪心。
为何常见:把“局部梯度”误当成“全局导航策略”,忽略了贪心算法只顾眼前、不顾全局的局限。
误区“体素越小细节越精细,直接把分辨率开到 1 毫米不就行了?”
正解内存按分辨率的三次方增长:边长减半,体素数变 8 倍;同时位姿误差对细体素的“污染”更明显,整张地图可能越细越糊。工程上按传感器精度和建图范围选 1~5cm,并用哈希体素等稀疏结构只为表面附近分配内存。
为何常见:只盯着精度看,忽略了内存、算力和位姿噪声三个硬约束之间的权衡。
面试题
Q1. 有符号距离场中“符号”的含义是什么?
- A.距离的数值单位
- B.体素在表面的内外← 正确答案
- C.传感器噪声大小
- D.地图更新的顺序
答案:B。有符号距离场的核心是“符号”:正号表示体素在表面外侧,负号表示在表面内侧。这就好比雕刻家的便签要区分“在雕像外面”还是“陷进粘土里”。A、C、D 都与符号的物理含义无关。
Q2. TSDF 中“截断”指的是什么?
- A.只更新表面附近的体素← 正确答案
- B.删除传感器的坏读数
- C.压缩地图的存储大小
- D.限制机器人的移动范围
答案:A。截断是指只在距表面 μ 范围内的体素上保存精确距离,远处统一记为 ±1。它控制的是“哪些体素值得精雕”,而不是删除读数、压缩文件或限制移动,因此选 A。
Q3. TSDF 权重融合的主要作用是什么?
- A.平滑多帧测量的噪声← 正确答案
- B.提高地图的渲染速度
- C.增大传感器的测量量程
- D.减少地图的数据维度
答案:A。融合把多帧测量按可信度加权平均,让随机噪声在叠加中相互抵消,表面越来越清晰。它既不能提升渲染速度,也不能改变传感器量程与数据维度,故选 A。这就像雕刻家多次触摸同一处后取平均。
Q4. 下列对 ESDF 的描述正确的是?
- A.提供全空间的真实距离← 正确答案
- B.只保存表面的截断信息
- C.无法用于轨迹优化
- D.必须由人工绘制生成
答案:A。ESDF 为空间中的每个体素保存到最近障碍的真实欧氏距离,梯度还可直接用于轨迹优化。B 是 TSDF 的特点,C、D 与事实相反,因此选 A。
Q5. ESDF 中梯度方向通常指向哪里?
- A.远离最近障碍的方向← 正确答案
- B.地图中央位置的方向
- C.传感器所在的方向
- D.机器人前进的方向
答案:A。ESDF 梯度定义为从最近障碍指向当前点的方向,因此总是指向“越来越空旷”的地方。它就是雾中的风向标,与地图中央、传感器位置、机器人朝向都无必然关系,所以选 A。
Q6. TSDF 中表面位置对应体素值是多少?
- A.距离恰好为零的位置← 正确答案
- B.距离取最大值的体素
- C.权重达到最高的体素
- D.截断阈值最大的方向
答案:A。表面正好是“内与外”的分界,也就是 TSDF 值为零的等值面。提取表面时找符号发生正负变化的相邻体素再插值到零点。最大距离、最大权重、最大阈值都不对应表面,因此选 A。
Q7. 使用 ESDF 进行避障时,机器人应沿梯度如何移动?
- A.正向移动以远离障碍← 正确答案
- B.反向移动贴近障碍
- C.垂直方向原地旋转
- D.任意方向匀速移动
答案:A。梯度指向远离最近障碍的方向,所以避障修正应沿梯度正向叠加,把机器人“推”离危险区。反向会撞墙,垂直旋转和无序移动都没有利用距离信息,故选 A。
Q8. 属于 TSDF 建图关键步骤的有哪些?
- A.计算体素的截断距离← 正确答案
- B.融合多帧测量的权重← 正确答案
- C.对齐传感器的位姿← 正确答案
- D.对图像做低通滤波
答案:ABC。建图三大步是:位姿对齐(C)、逐体素算截断距离(A)、多帧加权融合(B)。低通滤波是图像预处理手段,不是 TSDF 建图的必要环节,故排除 D。
Q9. ESDF 在路径规划中的优势有哪些?
- A.提供连续可用的梯度← 正确答案
- B.快速查询障碍距离← 正确答案
- C.支持障碍膨胀检测← 正确答案
- D.完全替代全局规划
答案:ABC。ESDF 提供全空间距离与梯度,查询是常数时间,且可结合机器人半径做膨胀检测。但它只是规划所需的“代价场”之一,仍需全局目标与运动约束配合,不能完全替代全局规划,排除 D。
Q10. 影响 TSDF 建图精度的因素有哪些?
- A.体素分辨率的大小← 正确答案
- B.截断距离的设置← 正确答案
- C.传感器位姿精度← 正确答案
- D.显示器分辨率
答案:ABC。分辨率决定空间采样密度,截断距离决定融合范围,位姿精度决定多帧对齐质量,三者共同影响建图精度。显示器只是显示设备,与传感器建图精度无关,故排除 D。