老王是“魔方公寓”的管家。这栋公寓的特别之处在于:每一间屋子都能像魔方一样,被横切、竖切、纵切,变成8个一模一样的小房间;小房间还能继续切、继续分……切出来的每个隔间,老王都记在一本厚册子上。租客来来往往,他从不轻易断定某个房间“有人”或“没人”,而是在册子上写“这屋十有八九有人”“三楼东角多半空着”,随着观察不断加分减分。后来,无人机和机械臂也来向老王借地图——它们把这套“切方块+记分数”的本事,叫做八叉树地图(OctoMap)。这篇教程,我们就来拆开老王这本记分册,看看背后的空间结构、概率更新和工程魔力。

岗位关联

八叉树地图是机器人感知与定位模块的基石。理解它,你才能读懂 SLAM 的三维输出、设计无人机避障逻辑、优化机械臂抓取规划——这是系统集成与性能调优的必备基本功。

目录
  1. 可视化演示
  2. 空间八叉结构
  3. 八叉树构建
  4. 概率占用表示
  5. 多分辨率支持
  6. 传感器融合
  7. 工程实用
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示八叉树层次化空间细分结构、贝叶斯概率占据更新射线投射、多分辨率查询对比及无人机机械臂避障应用场景,含星座网络背景和发光动画。

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

空间八叉结构

老王接手魔方公寓的第一天,面对的是一整层空旷仓库。他拿起尺子,从横向、纵向、垂直方向各切一刀,仓库立刻变成8块大小一样的隔间;隔间不够用,他就继续对人多的一两块再各切8下……每一块切出来的空间,都在册子上登记坐标与大小。长此以往,整栋楼的空间被组织成一棵“树”:最顶上是整栋楼,往下每层分出8块,再往下还能继续分。这种“一次八等分”的空间组织方式,就是八叉树名字的由来。

在 OctoMap 中,根节点代表整个地图边界,也就是老王手里那层仓库。每个内部节点最多有8个子节点,每个子节点对应父立方体8等分后的一个小立方体。树的深度越深,体素越小,空间分辨率越高。每个节点在物理上都是一个“体素”(Voxel),它不仅记录坐标,还携带体积尺寸信息,这被称为“体素体积建模”。

更准确地讲,OctoMap 把三维空间递归分解为8个卦限(Octants)。8个子立方体无重叠地拼成父立方体,任意空间点都能找到唯一一条从根到叶的路径。这让“查询某个体素状态”变成一次树上的快速搜索,而不是在巨大的三维数组里逐一对比。

老王每天查册子,就是沿着“楼号—区号—隔间号”这条唯一路径往下找。八叉树把这条路径变成数据结构,让空间管理像翻册子一样有章可循——这正是“父子节点关系”和“根节点与深度”在工程中的体现。

Vnode=⋃i=18Vchild,i,Vchild,i∩Vchild,j=∅ (i≠j)\displaystyle V_{\text{node}} = \bigcup_{i=1}^{8} V_{\text{child},i}, \quad V_{\text{child},i} \cap V_{\text{child},j} = \varnothing\ (i \neq j)
这条公式告诉我们:父节点空间等于8个子节点空间的“拼图”,且拼图之间不重叠、不遗漏。
  1. 符号 \(V_{\text{node}}\)父节点代表的立方体空间,就像老王册子上写的“三楼东区”。
    Vnode\displaystyle V_{\text{node}}
  2. 并集运算 \(\bigcup_{i=1}^{8}\)把8个子立方体拼起来,正好还原成父立方体,没有缺口。
    ⋃i=18Vchild,i\displaystyle \bigcup_{i=1}^{8} V_{\text{child},i}
  3. 交集为空 \(\varnothing\)任意两个子块之间没有重叠,保证划分“不重不漏”。
    Vchild,i∩Vchild,j=∅\displaystyle V_{\text{child},i} \cap V_{\text{child},j} = \varnothing

八叉树构建

老王盖公寓从不“一口吃成胖子”。五楼空着,他就只在册子上写一行“五楼空”;三楼东角住进来好几户,他才把那块隔成更小的房间。哪里有需求,哪里才动工细分——这是老王的管理学,也是八叉树构建的核心理念。

OctoMap 构建地图时,根节点起初只是一个未分裂的大体素。当激光雷达或深度相机的测量点落入某个叶子体素时,系统检查该体素是否还有“细分名额”(深度是否小于最大深度)。若还有名额,就把它分裂成8个子节点,并让观测数据继续落入对应子节点;若已到上限,就直接在该体素上累积状态。

这种“按需生长”意味着地图内存不是由环境边长的三次方决定,而是由实际观测覆盖的区域决定。绝大多数空间是空气,空气处以大体素保存,只在墙面、桌面、货架表面附近出现高分辨率小体素。树在更新中还支持剪枝(Pruning):如果8个子节点的状态足够一致,就把它们合并回父节点,进一步压缩体积。整个过程随传感器移动动态扩展,地图不必预先知道环境大小。

老王只在有人住的隔间细化登记,空楼层一笔带过。八叉树的“插入空间观测、递归分裂叶子、剪枝合并、动态扩展”,每一步都对应老王的日常:他从不预先画好所有隔间,而是边观察边隔墙、边记录边合并。

sd=sroot2d\displaystyle s_{d} = \frac{s_{\text{root}}}{2^{d}}
体素边长随深度按指数缩小:深度每加一,边长减半,分辨率翻倍。
  1. 符号 \(s_{\text{root}}\)根节点体素边长,也就是整张地图的边界尺寸,相当于老王册子封面写的“整栋楼尺寸”。
    sroot\displaystyle s_{\text{root}}
  2. 指数 \(2^d\)每一次8等分,等价于把立方体的三条边各二等分一次,所以分母是2的幂。
    2d\displaystyle 2^{d}
  3. 整体含义深度 d 的体素边长等于根边长除以 2 的 d 次方,精细度随深度指数上升。
    sroot2d\displaystyle \frac{s_{\text{root}}}{2^{d}}

概率占用表示

老王记性虽好,却从不在门牌上写死“有人”或“没人”。他采用记分制:今天看到302亮灯,就在302房那一页加2分;连续一周没动静,再减1分。分数越高越相信有人,分数越低越相信没人,但永远不会绝对化——这就是“不确定性处理”的朴素形态。

OctoMap 同样用0到1之间的占据概率 \(p\) 描述体素状态。但直接对概率做贝叶斯更新需要连乘,数值小且计算麻烦;工程上引入 log-odds(对数几率),定义为 \(L=\log \frac{p}{1-p}\)。它把“概率相乘”变成“分数相加”,正好像老王在册子上加加减减。

每次传感器观测 \(z_t\) 到来时,先由逆传感器模型算出本次观测的 log-odds 增量,再沿树路径做一次加法。查询时用 sigmoid 函数还原成概率。因为加法满足交换律和结合律,观测顺序不影响最终结果。多次观测同一堵墙,得分持续上升;行人经过时偶尔扫到一次,分数只会轻微波动,噪声被自然抑制。

老王一年四季翻册子加加减减,最终对每间房都有一个稳定的“几成把握”——这恰是 OctoMap 概率更新机制的日常:用对数几率做“贝叶斯概率更新”,用加减法完成“占据概率定义”的迭代。

L(n∣z1:t)=L(n∣z1:t−1)+L(n∣zt),p=11+e−L\displaystyle L(n \mid z_{1:t}) = L(n \mid z_{1:t-1}) + L(n \mid z_t), \qquad p = \frac{1}{1+e^{-L}}
新分数 = 旧分数 + 本次观测分数;最后用 sigmoid 把分数翻译回概率。
  1. 条件 \(z_{1t}\):从第1次到第t次所有观测的集合,就像老王前30天看到的灯光记录。
    z1:t\displaystyle z_{1:t}
  2. 旧分数 \(L(n \mid z_{1t-1})\):上一次累积得到的 log-odds,对应册子上原来的分数。
    L(n∣z1:t−1)\displaystyle L(n \mid z_{1:t-1})
  3. 观测增量 \(L(n \mid z_t)\)本次测量带来的分数变化,可能为正也可能为负。
    L(n∣zt)\displaystyle L(n \mid z_t)
  4. sigmoid 还原把最终分数转换为0到1之间的占据概率,分数越高概率越接近1。
    p=11+e−L\displaystyle p = \frac{1}{1+e^{-L}}

多分辨率支持

外卖小哥到公寓门口问路,老王扫一眼总览图就能指出“东侧人多、西侧空”;可要是有人丢了戒指,他就得下钻到最深层的隔间仔细翻找。同一本册子,粗看细看都行——这就是多分辨率查询的绝活。

OctoMap 天然支持任意深度查询:指定查询深度 \(d\),系统就从根节点下钻到第 \(d\) 层,返回该层体素状态。做粗略全局规划时,用第2层的粗体素判断“这半边能不能飞”;做精细避障时,再下钻到第5层看“这条缝隙能不能过”。“体素粒度选择”完全由应用需求决定。

如果是一棵满八叉树,深度 \(d\) 的节点总数会指数膨胀。OctoMap 的高明之处在于:大多数节点从不展开,配合剪枝合并,实际存储量远小于满树。这带来出色的“内存效率优化”。无人机飞行时可以动态选择分辨率:高空看大势,低空看细节,实时性与内存兼得。

外卖小哥关心“能不能更快到达”,钻戒主人关心“一个角落也别放过”。多分辨率解决的不是二选一,而是让同一张地图同时服务两类人——这正是 OctoMap 在“空间区域搜索”上的独到优势。

Nd=∑k=0d8k=8d+1−17\displaystyle N_d = \sum_{k=0}^{d} 8^k = \frac{8^{d+1}-1}{7}
这是满八叉树的理论节点总数;OctoMap 通过惰性分裂和剪枝,让实际存储量远远小于这个数。
  1. 求和下限 \(k=0\)从根节点所在的第0层开始逐层计数。
    ∑k=0d\displaystyle \sum_{k=0}^{d}
  2. 每层节点数 \(8^k\)满树第 k 层最多有 8 的 k 次方个节点。
    8k\displaystyle 8^{k}
  3. 等比数列求和把每层节点数累加,得到满树节点总数公式。
    8d+1−17\displaystyle \frac{8^{d+1}-1}{7}

传感器融合

老王的消息渠道可不止一双老花眼:门卫登记、楼下灯光、电梯监控录像,甚至邻居闲谈都会送来线索。他从不因为一条消息就急着重写册子,而是在对应房间“加一笔”或“减一笔”。

OctoMap 是一个与具体传感器解耦的框架。激光雷达(LiDAR)点云、深度相机(RGB-D)深度图、超声波距离读数,都能被转换为“本次观测的 log-odds 增量”,再累加到地图中。关键在于每种传感器都要有逆传感器模型(Inverse Sensor Model),回答一个问题:如果测到某个读数,某个体素有多大概率被占用?

以深度相机为例:射线上比测得距离近的体素,大概率空闲,log-odds 减小;距离落点附近的体素,大概率被占用,log-odds 增大。不同传感器在时间轴上交错到来,每一帧只是一次加法。移动的干扰物只会让体素分数偶尔跳动,静止的墙体因反复累积稳定在高得分——这就是“噪声与滤波”。

老王把门卫、灯光、监控各说各话的碎片信息,都折成分数记进同一本账。OctoMap 让机器人的各路感官也记入同一本账:激光点云、深度相机、距离传感器各司其职,概率融合之后,地图越来越像真实世界。

L(n∣zt)=log⁡P(n∣zt)1−P(n∣zt)\displaystyle L(n \mid z_t) = \log \frac{P(n \mid z_t)}{1 - P(n \mid z_t)}
逆传感器模型先把读数翻译成“占据概率”,再换算成 log-odds 增量,供地图累加。
  1. 测量事件 \(z_t\)第 t 次传感器读数,比如“深度相机测得前方2.3米有物体”。
    zt\displaystyle z_t
  2. 逆测量模型 \(P(n \mid z_t)\)已知读数时,体素 n 被占据的概率。
    P(n∣zt)\displaystyle P(n \mid z_t)
  3. 概率转 log-odds把概率比值取对数,得到本次观测的增量。
    log⁡P(n∣zt)1−P(n∣zt)\displaystyle \log \frac{P(n \mid z_t)}{1 - P(n \mid z_t)}

工程实用

魔方公寓的册子从不只给老王自己看。外卖小哥拿它规划最快路线,仓库机械臂拿它避开货架缝隙里的暗角。这本“活地图”,成了机器人干活的导航仪。

在机器人领域,OctoMap 最常见的应用是三维避障与运动规划。无人机电力巡检时,边飞边把激光点云插入地图,规划器每走一步就查询前方体素占用概率,把高概率占用区域视为“禁飞区”。机械臂在货架缝隙间抓取时,用细粒度体素判断连杆与货物的间隙,保证无碰撞。SLAM 系统中,OctoMap 常作为三维地图后端,把位姿轨迹与传感器数据渲染成随时可查询的占用地图。

形式化地看,规划器为候选轨迹定义代价函数:轨迹太长要罚,路径点落进高占用体素更要重罚。多分辨率查询让规划分两层进行——先在粗体素层找全局走廊,再在细体素层做局部避障。这套流程兼顾效率与安全,正是 OctoMap 在路径规划中被广泛采用的原因。

老王把册子往桌上一摊:“路线怎么走,代价自己算。”无人机和机械臂接过这份 OctoMap,在楼宇与货架间安全穿行——这就是无人机避障、机械臂抓取、路径规划与 SLAM 地图集成背后最朴素的工程逻辑。

J(τ)=∑k=1K(α⋅∥qk−qk−1∥+β⋅Pocc(qk))\displaystyle J(\tau) = \sum_{k=1}^{K} \left( \alpha \cdot \| q_k - q_{k-1} \| + \beta \cdot P_{\text{occ}}(q_k) \right)
轨迹总代价 = 长度代价 + 占用代价;权重 \(\alpha、\beta\) 决定“省距离”和“躲障碍”哪个更重要。
  1. 轨迹 \(\tau\)由 K 个路径点组成的候选运动轨迹,相当于机器人打算走的一条路线。
    τ={q1,q2,…,qK}\displaystyle \tau = \{q_1, q_2, \dots, q_K\}
  2. 路径长度惩罚 \(\|q_k - q_{k-1}\|\)相邻路径点的欧氏距离,累加后表示轨迹总长度。
    ∥qk−qk−1∥\displaystyle \| q_k - q_{k-1} \|
  3. 占用惩罚 \(P_{\text{occ}}(q_k)\)查询 q_k 所在体素的占用概率,概率越高惩罚越大。
    Pocc(qk)\displaystyle P_{\text{occ}}(q_k)
  4. 权重 \(\alpha, \beta\)调节“走短路”与“躲障碍”的优先级,由工程师按任务标定。
    α, β\displaystyle \alpha,\ \beta

AI 应用场景

无人机电力巡检避障

阶段:感知与定位(环境建图)

解决的问题:传统无人机沿固定航线巡检时,遇到新增障碍(如施工吊臂、临时树枝)容易撞机。利用 OctoMap 实时建图后,无人机能动态查询前方体素占用概率,自动绕开障碍,无需人工干预。

具体案例:某电力公司用搭载激光雷达的无人机巡检高压铁塔。飞机每扫描一圈,点云插入 OctoMap;当飞机接近塔身导线时,规划器发现前方体素占用概率超过 0.8,自动绕出 1.2 米安全距离,巡检任务顺利完成。

仓储机械臂抓取规划

阶段:运动规划(环境感知+轨迹优化)

解决的问题:货架缝隙狭窄,机械臂按固定轨迹容易碰撞包裹。OctoMap 以多分辨率体素表达缝隙占用状态,机械臂下钻到细粒度层级查询最小可通过间隙,实现无碰撞抓取。

具体案例:某快递分拣仓库中,机械臂要从堆满包裹的货架里抽取指定箱子。腕部深度相机每帧更新 OctoMap,规划器先在粗体素层找到通道,再在细体素层把手爪路径约束在间隙内,抓取成功率从 71% 提升到 96%。

服务机器人实时SLAM

阶段:同步定位与建图(SLAM)

解决的问题:扫地机器人需要持续在家工作,普通 2D 栅格地图无法表达茶几下方、窗帘背后的三维结构;OctoMap 三维地图支持多分辨率查询,让机器人快速判断“这扇窗帘能不能钻”“沙发底能否通过”。

具体案例:某家用扫地机器人用 RGB-D 相机实时构建客厅 OctoMap。来到沙发前,它先查粗体素判断区域开阔,再查细体素确认底盘高度可通过,顺利完成沙发底清扫;断电重启后加载已保存的 OctoMap,无需重新探路。

Python 演示

python80 行
import mathimport random  class Node:    """Node of octree: center, size, depth and log-odds value."""     def __init__(self, x, y, z, size, depth):        self.x = x        self.y = y        self.z = z        self.size = size        self.depth = depth        self.log_odds = 0.0        self.children = None  # None means not split yet  class SimpleOctoMap:    """A simplified OctoMap for education."""     def __init__(self, root_size=8.0, max_depth=4):        self.root = Node(0.0, 0.0, 0.0, root_size, 0)        self.max_depth = max_depth     def _child_index(self, node, x, y, z):        ix = 0 if x < node.x else 1        iy = 0 if y < node.y else 1        iz = 0 if z < node.z else 1        return iz * 4 + iy * 2 + ix     def _child_center(self, node, idx):        half = node.size / 4.0        dx = [-half, half, -half, half, -half, half, -half, half]        dy = [-half, -half, half, half, -half, -half, half, half]        dz = [-half, -half, -half, -half, half, half, half, half]        return node.x + dx[idx], node.y + dy[idx], node.z + dz[idx]     def update(self, x, y, z, delta):        node = self.root        while node.depth < self.max_depth:            if node.children is None:                node.children = [None] * 8                for i in range(8):                    cx, cy, cz = self._child_center(node, i)                    node.children[i] = Node(cx, cy, cz, node.size / 2.0, node.depth + 1)            node = node.children[self._child_index(node, x, y, z)]        node.log_odds += delta     def query(self, x, y, z, depth=None):        target = depth if depth is not None else self.max_depth        node = self.root        while node.depth < target and node.children is not None:            node = node.children[self._child_index(node, x, y, z)]        return 1.0 / (1.0 + math.exp(-node.log_odds))  def main():    random.seed(42)    omap = SimpleOctoMap(root_size=8.0, max_depth=4)     # simulate repeated "occupied" observations near (2,1,1)    for _ in range(6):        omap.update(2.0 + random.uniform(-0.1, 0.1),                    1.0 + random.uniform(-0.1, 0.1),                    1.0 + random.uniform(-0.1, 0.1), 0.8)     # simulate repeated "free" observations at (-2,-1,-1)    for _ in range(10):        omap.update(-2.0, -1.0, -1.0, -0.8)     p_occ = omap.query(2.0, 1.0, 1.0)    p_free = omap.query(-2.0, -1.0, -1.0)    p_prior = omap.query(5.0, 5.0, 5.0)    print("occupied voxel probability:", round(p_occ, 4))    print("free voxel probability:", round(p_free, 4))    print("unobserved voxel probability (prior):", round(p_prior, 4))  if __name__ == "__main__":    main()
目的:用约一百行代码实现一个简化版 OctoMap,模拟“重复观测占用/空闲”后,查询体素占据概率的变化,直观展示 log-odds 累加更新。
  1. return iz * 4 + iy * 2 + ix把三维空间的8卦限编码成0~7的子节点索引,这是八叉树“每次8等分”的代码化身。
  2. self.children = None采用惰性分裂:节点未观测到数据时先不建孩子,内存因此大幅节省。
  3. node = node.children[self._child_index(node, x, y, z)]沿树路径下钻到目标体素,查询深度与构建深度保持一致。
  4. node.log_odds += delta核心一行:新分数等于旧分数加本次观测增量,这就是老王记分册的数学本质。
  5. return 1.0 / (1.0 + math.exp(-node.log_odds))sigmoid 函数,把 log-odds 分数还原为0到1的占据概率。
  6. omap.update(-2.0, -1.0, -1.0, -0.8)用负增量模拟“观测到空闲”,重复10次后该体素概率应明显下降。
  7. p_occ = omap.query(2.0, 1.0, 1.0)查询曾被6次“占用”观测覆盖的体素概率,理应接近1。

常见误区

误区初学者看到“每个节点最多8个孩子”,就以为树必须每一层都完整展开,那地图稍微大一点内存就撑不住了。

正解OctoMap 是“按需生长 + 剪枝”的。只有被观测覆盖的叶子才分裂,空闲区域保持高层大体素;剪枝还会把状态一致的8个子节点合并回父节点。这就像老王只在有人的隔间建明细账,整层空着就只在册子上写一行“五楼空”。

为何常见:教材通常先讲满二叉树、满八叉树,让人误以为八叉树必须完整展开,忽略了工程实现的惰性策略。

误区有人认为既然 log-odds 是加法累加,重复观测会让分数无限增大,概率最终变成严格的0或1,地图“一言九鼎”无法再改变。

正解工程实现通常对 log-odds 做 clamping(值域截断),例如限制在 ±2 左右。这样墙体可以获得高置信,但环境变化后,新观测仍能把分数拉回来。数学上的极限趋近不等于工程上的绝对冻结。

为何常见:把数学极限与工程的数值截断混为一谈,忽略了 OctoMap 对不确定性的保留设计。

误区初学者觉得 OctoMap 就是把均匀体素换成树来存,查询、更新、规划跟普通三维栅格没什么两样。

正解OctoMap 提供了真正的多分辨率查询、按需细分、概率融合与剪枝优化。规划器可以在粗粒度快速筛选走廊,再在细粒度进行局部避障;普通三维栅格如果要达到同等精度,内存会暴涨几个数量级。

为何常见:树结构看起来只是“存储容器”,忽略了层次结构带来的算法能力跃迁。

误区以为每次传感器数据到达,OctoMap 都要把整张地图重新计算一遍,实时性很差。

正解OctoMap 只更新观测射线经过的体素路径,时间复杂度与树的深度成正比,与地图大小无关。老王只改册子上涉及到的几行,不会把整本账重抄一遍。

为何常见:把“整图重建”的惯性思维从其他地图算法(如某些栅格更新)带了过来,忽略了树的路径局部性。

面试题

Q1. 八叉树中,每个内部节点最多有多少个子节点?

单选题 · easy

  • A.2个孩子
  • B.4个孩子
  • C.8个孩子← 正确答案
  • D.16个孩子

答案:C。八叉树每次把立方体在三维方向各切一刀,得到8个子块,因此每个内部节点最多8个子节点。2、4、16分别对应二叉树、四叉树和十六叉树,不符合OctoMap定义。

Q2. OctoMap 中,节点保存的核心状态量是什么?

单选题 · easy

  • A.直接存0/1整数
  • B.对数几率实数值← 正确答案
  • C.用字符串标签存储
  • D.只缓存最近一次值

答案:B。OctoMap用log-odds(对数几率)实数表示占据置信度,这样概率更新可以转化为简单的加法。0/1整数无法表达不确定性,字符串标签和缓存最近值都不符合概率融合的工程需求。

Q3. 查询一个从未被观测的体素,其占据概率最接近多少?

单选题 · easy

  • A.概率1.0表示占用
  • B.返回0.5先验值← 正确答案
  • C.报错并终止程序
  • D.随机返回0或1

答案:B。未观测体素的log-odds保持初始值0,sigmoid(0)=0.5,即“未知”状态。返回1.0、随机值或报错都不符合概率地图的语义。

Q4. 在八叉树中,体素边长与树深度 d 的关系是?

单选题 · mid

  • A.随深度线性增长
  • B.随深度线性减小
  • C.随深度指数减小← 正确答案
  • D.随深度指数增长

答案:C。体素边长等于根节点边长除以2的d次方,深度每加一,边长减半,因此是指数减小,而非线性变化。

Q5. OctoMap 相比均匀三维栅格地图的主要优势是什么?

单选题 · mid

  • A.能够识别物体颜色
  • B.多分辨率和内存省← 正确答案
  • C.无需任何传感器数据
  • D.输出完整语义标签

答案:B。OctoMap用层次化结构按需细分,支持多分辨率查询,内存量远小于同等精度的均匀栅格。识别颜色、无需数据、语义标签都不是OctoMap的核心能力。

Q6. 下列哪些属于 OctoMap 的特点?

多选题 · mid

  • A.层次化的树状结构存储← 正确答案
  • B.支持任意深度的查询← 正确答案
  • C.用log-odds进行融合更新← 正确答案
  • D.必须等建图完毕才能使用

答案:ABC。OctoMap是树状层次结构,可在任意深度查询,并用log-odds做概率融合。它支持边建图边使用,D说法错误。

Q7. 新的传感器读数到达后,OctoMap 如何更新地图?

单选题 · mid

  • A.用读数平均值覆盖全树
  • B.转为log-odds增量累加← 正确答案
  • C.只在首次观测时记录
  • D.先删除原记录再写入

答案:B。每次观测先通过逆传感器模型转为log-odds增量,再沿树路径做加法。覆盖全树、只记首次、删除重写都与概率融合的思想相悖。

Q8. 关于 OctoMap 的剪枝(Pruning)操作,正确的理解是?

单选题 · hard

  • A.合并同类叶子节省内存← 正确答案
  • B.用满深度展开全部叶节点
  • C.把整棵地图保存到磁盘
  • D.删除所有高占用区域点

答案:A。剪枝把8个状态一致的子节点合并回父节点,减少节点数量、节省内存。展开全部叶子、保存磁盘、删除高占用区域都不是剪枝的定义。

Q9. 关于 log-odds 更新的说法,哪些是正确的?

多选题 · hard

  • A.多次同向观测会让置信度累积← 正确答案
  • B.噪声观测只会小幅波动不翻转← 正确答案
  • C.更新后可用sigmoid还原概率← 正确答案
  • D.更新公式需要多个分数连乘

答案:ABC。log-odds是累加更新,同向观测分数越积越高;单次噪声只会造成小幅波动;sigmoid可还原概率。D错误,log-odds恰恰把连乘变成加法。

Q10. 某体素连续收到100次“空闲”观测(每次ΔL=-1,且实现设有clamp下限-10),查询其占据概率最接近?

单选题 · extreme

  • A.趋近于0的非零数值← 正确答案
  • B.严格等于0的整数值
  • C.保持0.5的先验概率
  • D.接近1的高置信抢占

答案:A。log-odds被clamp在-10,sigmoid(-10)约等于0.000045,是趋近于0但非严格0的值。严格为0、保持先验、接近1都与clamp后的概率更新结果不符。