想象你走进一间漆黑的工作室,面前摆着一尊还没完工的巨大雕像。你看不见它,只能伸出手,一寸一寸地抚摸。每一次指尖触到粘土,你都在心里默默记下一个数字:“这一点离表面两根手指远,在雕像外面,记 +2”;“这里已经陷进粘土里,记 −1”。随着你反复触摸,工作室里的每一寸空气都被贴上了这样的“距离标签”。把这些标签集合起来,就是一张有符号距离场(SDF)——它回答两个问题:空间中任意一点离最近的表面有多远?这个点是在表面外面(正号)还是里面(负号)? 问题来了:雕像那么大,空间无穷多,给所有位置都贴标签太费力。聪明的雕刻家只会在表面附近一小层仔细标注,远处统一写“很远”——这就是截断,诞生了 TSDF(截断有符号距离场)。而当雕像完工,要把它搬出狭窄的门洞时,他又需要精确知道每一步的安全距离,于是补全整个空间的完整距离——这就是 ESDF(欧氏有符号距离场)。本文就用这座“黑暗中的雕塑”的故事,把机器人建图的两大招牌技术讲透。

岗位关联

对 AI 从业者来说,TSDF/ESDF 是 3D 视觉感知与自主导航之间的“翻译官”:搞懂它们,你就能理解从 KinectFusion 三维重建到仓储机器人避障的核心原理,快速上手 SLAM 与路径规划岗位。

目录
  1. 可视化演示
  2. 有符号距离场
  3. TSDF原理
  4. ESDF原理
  5. 建图流程
  6. SLAM应用
  7. 路径规划
  8. 工程调优
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

有符号距离场

雕刻家每摸到一个点,就相当于给空间的某个位置贴一张便签。便签上只写两件事:离最近表面的距离数值,以及在表面内外的方向——用正负号表示。当整个工作室的地板、墙面、空气中都贴满了这样的便签,就构成了一张“有符号距离场”。它不描述“哪里有东西”,而描述“这里离东西有多远、在东西里面还是外面”,这是它和普通占用地图最大的不同。

在机器人世界里,我们不可能给连续空间贴满便签,而是先把空间切成许多小立方体,这些立方体叫做体素(Voxel),就像把工作室地板铺上方格。每个体素里放一块“小石子”,上面刻着一个数字:它到最近表面的距离。所有体素合起来,就是离散化的距离场。体素越小,方格越密,便能刻画越精细的曲面,但代价也越大——这一点我们留到“工程调优”一节细说。

形式化地写,对于空间中的任意一点 $\mathbf{x}$,设场景中所有表面点的集合为 $\mathcal{S}$,那么该点的有符号距离值是:

就像雕刻工作室的地板被铺上带刻度的方格,机器人想知道自己离墙多远,低头看一眼脚下那块石子上的数字即可;符号则告诉它:此刻是站在粘土外面,还是正身处雕塑肚子里。

d(x)={+min⁡p∈S∥x−p∥,x 在表面外−min⁡p∈S∥x−p∥,x 在表面内d(\mathbf{x})=\begin{cases}+\displaystyle\min_{\mathbf{p}\in\mathcal{S}}\left\|\mathbf{x}-\mathbf{p}\right\|, & \mathbf{x}\text{ 在表面外}\\[8pt]-\displaystyle\min_{\mathbf{p}\in\mathcal{S}}\left\|\mathbf{x}-\mathbf{p}\right\|, & \mathbf{x}\text{ 在表面内}\end{cases}
看不懂没关系:每个体素只干两件事——算出“离最近的表面多远”,再判断“我在表面外(正)还是内(负)”。
  1. 表面点集合 $\mathcal{S}$所有物体表面的点组成一个集合,就像雕塑表面每一个可以触摸到的位置。
    p∈S\mathbf{p}\in\mathcal{S}
  2. 最近表面点对每个候选表面点 $\mathbf{p}$ 算距离,取最小值,找到“离我最近的表面点”。
    min⁡p∈S∥x−p∥\min_{\mathbf{p}\in\mathcal{S}}\left\|\mathbf{x}-\mathbf{p}\right\|
  3. 欧氏距离$\left\|\mathbf{x}-\mathbf{p}\right\|$ 就是两点间的直线距离,也就是“手指到表面的空间距离”。
    ∥x−p∥=(xx−px)2+(xy−py)2+(xz−pz)2\left\|\mathbf{x}-\mathbf{p}\right\|=\sqrt{(x_x-p_x)^2+(x_y-p_y)^2+(x_z-p_z)^2}
  4. 内外符号分段函数给“表面外”加上正号、“表面内”加上负号,这就是“有符号”三个字的来历。
    {+∥x−p∗∥,外−∥x−p∗∥,内\begin{cases}+\left\|\mathbf{x}-\mathbf{p}^{*}\right\|, & \text{外}\\-\left\|\mathbf{x}-\mathbf{p}^{*}\right\|, & \text{内}\end{cases}

TSDF原理

雕刻家很快发现,给整个工作室都贴精确便签是白费力气:离雕塑三米远的墙角,贴“3 米”和“3.1 米”毫无区别。于是他只给表面附近一个“手肘长度”(记为 μ)以内的空气贴精确标签,更远的地方统一写“很远”。这个“只在表面附近精雕细琢”的设定,就是截断距离 μ。由此得到的场,叫作 TSDF——Truncated SDF,截断有符号距离场。

TSDF 把每个体素的距离值除以 μ 归一化,并且把结果截断在 $[-1, +1]$ 之间:表面处值为 0,表面外为正(最大 +1),表面内为负(最小 −1),超出截断带的一律记为 ±1。这样做的好处是:内存只花在表面附近,而表面附近恰恰是重建最需要细节的地方。

光有截断还不够。雕刻家每次触摸同一处,由于手抖、角度不同,读数会有误差。多摸几次后,他该信哪一次?答案是做加权平均:可信度高的触摸(正对着表面、距离近)权重大,可信度低的权重小。设第 $i$ 帧在体素 $\mathbf{x}$ 处测得值为 $f_i$、可信权重为 $w_i$,那么融合公式为:

回到工作室:雕刻家每次触摸只更新近表面一层便签,同一处被摸到多次后,便签上的数字取加权平均——手再抖,误差也会在一次又一次的触摸中被“平均”掉,雕塑的轮廓在黑暗中逐渐变得坚实而清晰。

TSDF(x)=sign⁡(d(x))×min⁡(1, ∣d(x)∣μ)\mathrm{TSDF}(\mathbf{x})=\operatorname{sign}\big(d(\mathbf{x})\big)\times\min\left(1,\ \dfrac{\left|d(\mathbf{x})\right|}{\mu}\right)
公式做的事,就像把新旧两张便签的内容按“谁更可信谁说了算”的比例重新抄一遍,噪声被平均掉了,表面轮廓越来越清晰。
  1. 新测量值 $f_i$当前帧在体素处算出的归一化截断距离。
    fi=TSDFi(x)f_i=\mathrm{TSDF}_i(\mathbf{x})
  2. 可信权重 $w_i$权重通常由传感器与表面的夹角、测量距离决定,正对表面时最可信。
    wi=w(θi, zi)w_i=w(\theta_i,\,z_i)
  3. 旧值加权保留旧值乘旧权重、新值乘新权重,再相加,体现“两边都参考”。
    Wi−1Fi−1+wifiW_{i-1}F_{i-1}+w_i f_i
  4. 权重归一除以总权重,得到更新后的平均距离值,同时累积权重 $W_i$。
    Fi=Wi−1Fi−1+wifiWi−1+wiF_i=\dfrac{W_{i-1}F_{i-1}+w_i f_i}{W_{i-1}+w_i}

ESDF原理

雕像完工,要把它搬出狭窄的门洞。这时只有表面附近的便签就不够用了:机械臂旋转时,任何一个凸起的肩部都可能撞上门框。雕刻家需要知道全空间的准确距离——从雕像表面到每一面墙、天花板、地板,每一个位置的直线距离都要心里有数。这种“全场真实距离”的字段,就是 ESDF——Euclidean SDF,欧氏有符号距离场。

ESDF 也建立在体素网格上,但每个体素存的不是“相对表面有多近”,而是“离最近的障碍物有多远”。对自由空间里的点,这个值是正数;障碍物内部的点则为负。由于它保存了全空间的真实距离,查询任意一点到最近障碍的距离都可以在常数时间内完成——这正是路径规划最需要的“体检报告”。假设障碍物区域为 $\mathcal{O}$:

在实际导航中,我们还会把机器人自身的半径考虑进去:查询距离时直接用 $D(\mathbf{x})-r_{\mathrm{robot}}$ 作为有效安全距离,相当于把障碍“膨胀”了一圈。把 ESDF 想象成一张布满“雾”的舒适度地图,越亮的地方离障碍越远;梯度就是雾中的“风向标”,永远指向更空旷的方向。机器人顺着风向标微调身形,就能安然穿过门洞。

D(x)=min⁡p∈O∥x−p∥,∇D(x)=x−p∗∥x−p∗∥D(\mathbf{x})=\displaystyle\min_{\mathbf{p}\in\mathcal{O}}\left\|\mathbf{x}-\mathbf{p}\right\|,\qquad\nabla D(\mathbf{x})=\dfrac{\mathbf{x}-\mathbf{p}^{*}}{\left\|\mathbf{x}-\mathbf{p}^{*}\right\|}
第一项是“到最近障碍有多远”;第二项是这个距离增长最快的方向——也就是“哪里更空旷”的方向,机器人沿着它走就能远离危险。
  1. 障碍集合 $\mathcal{O}$所有占据空间的体素集合,相当于雕像和墙壁占住的位置。
    p∈O\mathbf{p}\in\mathcal{O}
  2. 最近障碍点在障碍集合里找离 $\mathbf{x}$ 最近的点,记为 $\mathbf{p}^{*}$。
    p∗=arg⁡min⁡p∈O∥x−p∥\mathbf{p}^{*}=\arg\min_{\mathbf{p}\in\mathcal{O}}\left\|\mathbf{x}-\mathbf{p}\right\|
  3. 真实欧氏距离$D(\mathbf{x})$ 就是当前点到最近障碍的直线距离,单位是米。
    D(x)=∥x−p∗∥D(\mathbf{x})=\left\|\mathbf{x}-\mathbf{p}^{*}\right\|
  4. 梯度方向$\nabla D$ 是一个单位向量,永远指向“远离最近障碍”的方向。
    x−p∗∥x−p∗∥\dfrac{\mathbf{x}-\mathbf{p}^{*}}{\left\|\mathbf{x}-\mathbf{p}^{*}\right\|}

建图流程

一次完整的建图,就像雕刻家一次完整的“摸骨”:先确定自己的手在哪里(位姿估计),再沿手指方向感受哪里碰到了表面(深度测量),最后更新对应的便签(体素更新)。传感器换成深度相机或激光雷达,“手指”就是一束束射线。

具体来说,TOF 相机或激光雷达发射一束射线,遇到表面后返回深度值 $z_s$。对于同一条射线上的每个体素,设它沿光轴到传感器的深度为 $z(\mathbf{x})$,那么该体素的符号距离近似为“体素深度减去测量深度”:

整个流程串起来就是:深度帧输入 → 位姿对齐 → 体素更新 → 地图输出。其中位姿对齐由里程计或 SLAM 提供,位姿一旦有偏差,地图就会“重影”;体素更新则用前面讲的加权融合把多帧数据揉在一起。雕刻家每次触摸都是一帧“深度图”,他先确认自己站在哪儿,再记录指尖到表面的深度,最后把新便签与旧便签做加权平均——一轮轮循环,一尊立体的雕像就在黑暗中慢慢成形。

sdfi(x)=z(x)−zs\mathrm{sdf}_i(\mathbf{x})=z(\mathbf{x})-z_s
沿射线看:体素在表面前方,$z < z_s$,差值为负,说明它在表面内侧;体素在表面前方时差值不为零,越接近表面越接近零。
  1. 体素深度 $z(\mathbf{x})$体素中心沿传感器光轴到光心的距离。
    z(x)=∥x−o∥⋅cos⁡θz(\mathbf{x})=\left\|\mathbf{x}-\mathbf{o}\right\|\cdot\cos\theta
  2. 测量深度 $z_s$射线命中表面后,传感器记录的真实深度读数。
    zs=深度相机输出值z_s=\text{深度相机输出值}
  3. 作差得符号距离$z(\mathbf{x})-z_s$ 的正负正好对应体素在表面前方还是后方,大小对应离表面远近。
    sdfi(x)=z(x)−zs\mathrm{sdf}_i(\mathbf{x})=z(\mathbf{x})-z_s
  4. 截断与归一把算出的 sdf 带入上一节的截断公式,再与旧值做加权融合,就完成了一次体素更新。
    fi=min⁡(1, ∣sdfi∣μ)⋅sign⁡(sdfi)f_i=\min\left(1,\ \dfrac{|\mathrm{sdf}_i|}{\mu}\right)\cdot\operatorname{sign}(\mathrm{sdf}_i)

SLAM应用

有了 TSDF 地图,雕刻家终于可以“看见”完整的雕像了。但体素里存的是距离值,不是表面本身——该怎么把表面“取”出来?答案很漂亮:表面恰好穿过那些距离值为零的位置。

在体素网格中,表面一侧的体素 TSDF 为正、另一侧为负。只要找到相邻体素符号发生变化的“临界地带”,再用线性插值求出距离恰好为零的亚体素位置,就能拼出光滑的表面网格。这个操作叫表面提取,三维中常用的 Marching Cubes 算法做的正是这件事:

在 RGB-D SLAM 中,TSDF 是稠密重建的“常客”:KinectFusion 把每一帧深度图实时融合进 TSDF 体素场,再用光线投射直接渲染出表面;Open3D 等库则帮助把多帧点云通过 ICP 配准对齐,再统一融进同一张 TSDF 网格。雕刻家凭着上千次触摸零点的位置,把一张张“指纹”(点云帧)严丝合缝地拼在一起,终于拼出雕像完整的肌肉与轮廓。

S={ x  |  TSDF(x)=0 }\mathcal{S}=\left\{\,\mathbf{x}\;\middle|\;\mathrm{TSDF}(\mathbf{x})=0\,\right\}
寻宝口诀:不用看颜色、不用管权重,专找那些“距离标签刚好从正变负”的地方,那里就是雕塑的皮肤。
  1. 表面集合 $\mathcal{S}$所有满足条件的位置 $x$ 组成的集合,就是提取出的表面。
    S={x∣TSDF(x)=0}\mathcal{S}=\left\{\mathbf{x}\mid \mathrm{TSDF}(\mathbf{x})=0\right\}
  2. 正负跨越在相邻体素间观察 TSDF 值是否从正变负或从负变正,跨越处必有表面。
    TSDF(xi)>0, TSDF(xi+1)<0\mathrm{TSDF}(\mathbf{x}_i)>0,\ \mathrm{TSDF}(\mathbf{x}_{i+1})<0
  3. 线性插值用两边体素的数值按比例逼近零点,得到亚体素精度的表面坐标。
    xsurf=xi+∣TSDF(xi)∣∣TSDF(xi)∣+∣TSDF(xi+1)∣(xi+1−xi)\mathbf{x}_{\mathrm{surf}}=\mathbf{x}_i+\dfrac{|\mathrm{TSDF}(\mathbf{x}_i)|}{|\mathrm{TSDF}(\mathbf{x}_i)|+|\mathrm{TSDF}(\mathbf{x}_{i+1})|}\left(\mathbf{x}_{i+1}-\mathbf{x}_i\right)

路径规划

现在轮到助手出场:一个轮式机器人要把雕像搬出门。它手里有三件法宝——目标方向(门的方向)、ESDF 距离表,以及表上的梯度“风向标”。每走一步,它查一下脚下体素的距离和梯度,就能决定该怎么调整。

ESDF 的梯度永远指向“远离最近障碍”的方向,所以机器人把速度拆成两股:一股朝目标前进,一股沿梯度方向“往外顶”,合成后的速度既靠近门,又被障碍“推开”。这个简单而优雅的思路写出来就是:

工程上更成熟的方案是轨迹优化(如 Chomp):把 ESDF 的距离和梯度写进代价函数,算法反复迭代,把一条原本会剐蹭的轨迹“顶”离障碍物,同时保持平滑。动态场景中,还可以比较当前 ESDF 与历史 ESDF 的距离突变,快速发现新出现的障碍。就像助手双手端着雕像,身体感受着四面八方“空气的推力”(梯度),每当靠近门框,推力就轻轻把它推回安全区,最终有惊无险地挪出门洞。

vsafe(x)=vgoal+λ ∇D(x)\mathbf{v}_{\mathrm{safe}}(\mathbf{x})=\mathbf{v}_{\mathrm{goal}}+\lambda\,\nabla D(\mathbf{x})
就像风筝既要被线拽向目标,又被风吹离障碍——两股力合成一条安全的弧线轨迹。
  1. 目标速度 $\mathbf{v}_{\mathrm{goal}}$指向最终目标点的期望速度,大小由规划层决定。
    vgoal=k xgoal−x∥xgoal−x∥\mathbf{v}_{\mathrm{goal}}=k\,\dfrac{\mathbf{x}_{\mathrm{goal}}-\mathbf{x}}{\left\|\mathbf{x}_{\mathrm{goal}}-\mathbf{x}\right\|}
  2. 距离梯度 $\nabla D(\mathbf{x})$ESDF 中当前点的梯度,指向远离最近障碍的方向。
    ∇D(x)=x−p∗∥x−p∗∥\nabla D(\mathbf{x})=\dfrac{\mathbf{x}-\mathbf{p}^{*}}{\left\|\mathbf{x}-\mathbf{p}^{*}\right\|}
  3. 安全系数 $\lambda$控制“避障力度”的权重,离障碍越近时 $\lambda$ 往往调得越大。
    λ=λ0⋅1D(x)+ϵ\lambda=\lambda_0\cdot\dfrac{1}{D(\mathbf{x})+\epsilon}
  4. 速度合成两向量相加,得到兼顾“走向目标”与“远离障碍”的最终速度指令。
    vsafe=vgoal+λ ∇D(x)\mathbf{v}_{\mathrm{safe}}=\mathbf{v}_{\mathrm{goal}}+\lambda\,\nabla D(\mathbf{x})

工程调优

终于到了落地环节。雕刻家的工作室地板方格该铺多密?格子越小,雕像细节越真,但地板要铺的格子数量按三次方爆炸式增长。机器人建图同样面临这个核心权衡:体素分辨率与内存、算力的取舍。

假设建图区域是边长 $L$ 米的立方体,体素边长是 $r$ 米,每个体素存距离和权重需要 $B$ 个字节,那么总内存为:

工程经验上,截断距离 $\mu$ 通常取 5~10 个体素边长:太小则传感器误差导致空洞、融合不到帧;太大则远处的截断值污染表面细节。内存吃紧时,可以用哈希体素只为表面附近的截断带分配内存(TSDF 天然适合这种“稀疏存储”),或者先粗建图确认轮廓,再在重点区域局部细化。聪明的雕刻家正是这么干的:先粗铺一遍方格确认轮廓,再在雕塑肩膀、脸部换细方格,门框周围加密便签,而不必给整间屋子铺满——该省的地方省,该精细的地方精细,这才是工程的真功夫。

M=(Lr)3×BM=\left(\dfrac{L}{r}\right)^{3}\times B
分辨率翻倍,即 $r$ 减半,括号里的数变成原来的 8 倍——内存直接暴涨 8 倍。这就是“细节的代价”。
  1. 地图边长 $L$建图区域的尺寸,比如一个 10 米见方的房间。
    L=10 mL=10\,\text{m}
  2. 体素边长 $r$每个小立方体的边长,$r$ 越小分辨率越高。
    r=0.02 mr=0.02\,\text{m}
  3. 体素总数量每维体素数 $(L/r)$ 的三次方,决定网格的总单元数。
    (Lr)3=5003=1.25×108\left(\dfrac{L}{r}\right)^{3}=500^{3}=1.25\times10^{8}
  4. 每体素字节数 $B$距离值加权重通常 8~16 字节,直接乘上去就是总内存。
    M=(Lr)3×BM=\left(\dfrac{L}{r}\right)^{3}\times B

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 演示

python63 行
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()
目的:用 50×50 的二维体素网格模拟 TSDF 建图(截断 + 加权融合),再从 TSDF 中提取表面体素并计算全场的 ESDF 距离,演示从“深度测量”到“可规划地图”的完整链路。
  1. self.values = [[0.0] * size for _ in range(size)]为每个体素分配一个距离值,初始全为 0。
  2. radius = int(self.trunc / self.voxel) + 1计算截断半径,只更新表面附近的体素,体现 TSDF 的“局部精雕”。
  3. if dist > self.trunc: continue截断判断:超出 μ 的体素直接跳过,不参与融合。
  4. self.values[ni][nj] = (self.values[ni][nj] * old_w + f * weight) / (old_w + weight)加权平均融合:旧值和新值按权重比例混合,多帧测量越摸越准。
  5. if 0.0 < self.values[i][j] < 0.4把距离值较小的表面附近体素当作“障碍种子”,用于生成 ESDF。
  6. min(math.hypot(i - si, j - sj) for si, sj in surf) * self.voxel对每个空闲体素求到最近障碍种子的欧氏距离,再乘体素物理尺寸换算成米。
  7. 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. 有符号距离场中“符号”的含义是什么?

单选题 · easy

  • A.距离的数值单位
  • B.体素在表面的内外← 正确答案
  • C.传感器噪声大小
  • D.地图更新的顺序

答案:B。有符号距离场的核心是“符号”:正号表示体素在表面外侧,负号表示在表面内侧。这就好比雕刻家的便签要区分“在雕像外面”还是“陷进粘土里”。A、C、D 都与符号的物理含义无关。

Q2. TSDF 中“截断”指的是什么?

单选题 · easy

  • A.只更新表面附近的体素← 正确答案
  • B.删除传感器的坏读数
  • C.压缩地图的存储大小
  • D.限制机器人的移动范围

答案:A。截断是指只在距表面 μ 范围内的体素上保存精确距离,远处统一记为 ±1。它控制的是“哪些体素值得精雕”,而不是删除读数、压缩文件或限制移动,因此选 A。

Q3. TSDF 权重融合的主要作用是什么?

单选题 · mid

  • A.平滑多帧测量的噪声← 正确答案
  • B.提高地图的渲染速度
  • C.增大传感器的测量量程
  • D.减少地图的数据维度

答案:A。融合把多帧测量按可信度加权平均,让随机噪声在叠加中相互抵消,表面越来越清晰。它既不能提升渲染速度,也不能改变传感器量程与数据维度,故选 A。这就像雕刻家多次触摸同一处后取平均。

Q4. 下列对 ESDF 的描述正确的是?

单选题 · mid

  • A.提供全空间的真实距离← 正确答案
  • B.只保存表面的截断信息
  • C.无法用于轨迹优化
  • D.必须由人工绘制生成

答案:A。ESDF 为空间中的每个体素保存到最近障碍的真实欧氏距离,梯度还可直接用于轨迹优化。B 是 TSDF 的特点,C、D 与事实相反,因此选 A。

Q5. ESDF 中梯度方向通常指向哪里?

单选题 · mid

  • A.远离最近障碍的方向← 正确答案
  • B.地图中央位置的方向
  • C.传感器所在的方向
  • D.机器人前进的方向

答案:A。ESDF 梯度定义为从最近障碍指向当前点的方向,因此总是指向“越来越空旷”的地方。它就是雾中的风向标,与地图中央、传感器位置、机器人朝向都无必然关系,所以选 A。

Q6. TSDF 中表面位置对应体素值是多少?

单选题 · hard

  • A.距离恰好为零的位置← 正确答案
  • B.距离取最大值的体素
  • C.权重达到最高的体素
  • D.截断阈值最大的方向

答案:A。表面正好是“内与外”的分界,也就是 TSDF 值为零的等值面。提取表面时找符号发生正负变化的相邻体素再插值到零点。最大距离、最大权重、最大阈值都不对应表面,因此选 A。

Q7. 使用 ESDF 进行避障时,机器人应沿梯度如何移动?

单选题 · mid

  • A.正向移动以远离障碍← 正确答案
  • B.反向移动贴近障碍
  • C.垂直方向原地旋转
  • D.任意方向匀速移动

答案:A。梯度指向远离最近障碍的方向,所以避障修正应沿梯度正向叠加,把机器人“推”离危险区。反向会撞墙,垂直旋转和无序移动都没有利用距离信息,故选 A。

Q8. 属于 TSDF 建图关键步骤的有哪些?

多选题 · mid

  • A.计算体素的截断距离← 正确答案
  • B.融合多帧测量的权重← 正确答案
  • C.对齐传感器的位姿← 正确答案
  • D.对图像做低通滤波

答案:ABC。建图三大步是:位姿对齐(C)、逐体素算截断距离(A)、多帧加权融合(B)。低通滤波是图像预处理手段,不是 TSDF 建图的必要环节,故排除 D。

Q9. ESDF 在路径规划中的优势有哪些?

多选题 · hard

  • A.提供连续可用的梯度← 正确答案
  • B.快速查询障碍距离← 正确答案
  • C.支持障碍膨胀检测← 正确答案
  • D.完全替代全局规划

答案:ABC。ESDF 提供全空间距离与梯度,查询是常数时间,且可结合机器人半径做膨胀检测。但它只是规划所需的“代价场”之一,仍需全局目标与运动约束配合,不能完全替代全局规划,排除 D。

Q10. 影响 TSDF 建图精度的因素有哪些?

多选题 · easy

  • A.体素分辨率的大小← 正确答案
  • B.截断距离的设置← 正确答案
  • C.传感器位姿精度← 正确答案
  • D.显示器分辨率

答案:ABC。分辨率决定空间采样密度,截断距离决定融合范围,位姿精度决定多帧对齐质量,三者共同影响建图精度。显示器只是显示设备,与传感器建图精度无关,故排除 D。