两个移动智能体观察同一个区域时,合并地图不等于把两张图片拼起来。对于占据栅格,每个格子保存“被占据”的概率;新观测逐次更新这份环境表示。下面从已对齐的格子坐标开始,用log-odds把两个角色的证据合并成三格地图,明确区分没有观察与证据相互抵消。
本文面向学习多智能体建图数据流程的开发者。示例使用人工观测概率,已在Python 3.11.15本地执行,不读取真实传感器、不估计机器人位姿、不训练模型。它是机器人AI环境建模的一段基础计算,不是生成旅游地图、绘制路线或接入地理编码API。

先固定坐标、先验与观测含义
A和B的观测都已经转换到同一格子坐标系。只建(0,0)、(1,0)、(2,0)三格,初始占据先验p0=0.5。观测中的0.7或0.3表示给定逆传感器模型对单格的占据判断,来自本例设定,不能理解为真实设备准确率。地图假定静态,观测按模型所需条件近似独立;一条传感记录在共享后不能被多个角色当成多份独立证据。
| 角色 | 格子 | 单次占据概率 |
|---|---|---|
| A | (0,0) | 0.7 |
| B | (0,0) | 0.7 |
| A | (1,0) | 0.7 |
| B | (1,0) | 0.3 |
(2,0)没有观测,必须保留未知先验,不能写成“空闲”。两个观测0.7与0.3抵消后回到0.5的(1,0)与未观测格数值相同,但数据来历不同。应用中应额外保存观测数量和冲突标记,而不是只存一张黑白图。
用log-odds更新,而不是直接相加概率
定义L(p)=ln(p/(1−p))。一次更新为L新=L旧+L(单次观测概率)−L(p0),最后用p=1/(1+exp(−L))恢复概率。先验0.5的L为0,所以本例更新只需加观测log-odds。若改变先验,初始格子值也必须改为相应L(p0),不能只改函数参数而保留全零初值。
依据是Sebastian Thrun的Robotic Mapping: A Survey,2002年技术报告CMU-CS-02-111,第8节式(17)/(18)。该节说明已知位姿占据栅格的递归更新,也指出位姿不确定、相关噪声和格间独立假设的边界。本文只演示这些前提下的单格更新,不声称解决完整多机器人SLAM。原资料读取于2026-10-03北京时间。
对(0,0),两份0.7观测各自赔率为7/3,合成赔率49/9,概率49/(49+9)=49/58≈0.844828;不能用0.7+0.7得到1.4。对(1,0),(7/3)×(3/7)=1,概率为0.5。这些数值是本例计算结果,不是建图精度。
运行三格融合与失败样本
让AI按上述公式生成实现时,明确要求概率严格落在0和1之间、格子必须已对齐、非法批次不改旧地图。保存下方为mapping_demo.py,执行python -X utf8 mapping_demo.py。Python 3.11标准库即可运行。
from math import log, exp, isfinite, isclose
def logit(p):
if type(p) not in (int, float) or not isfinite(p) or not 0 < p < 1:
raise ValueError('probability must be finite and inside (0,1)')
return log(p/(1-p))
def merge_observations(current, observations, prior=0.5):
baseline = logit(prior)
candidate = current.copy()
for agent, cell, inverse_probability in observations:
if agent not in {'A','B'} or cell not in candidate:
raise ValueError('unknown agent or unaligned cell')
candidate[cell] += logit(inverse_probability) - baseline
return candidate
def probability(l):
# Stable logistic conversion avoids exp(large positive number).
return 1/(1+exp(-l)) if l >= 0 else exp(l)/(1+exp(l))
grid = {(0,0):0.0, (1,0):0.0, (2,0):0.0}
observations = [('A',(0,0),.7), ('B',(0,0),.7),
('A',(1,0),.7), ('B',(1,0),.3)]
merged = merge_observations(grid, observations)
result = {cell:probability(l) for cell,l in merged.items()}
assert isclose(result[(0,0)],49/58)
assert isclose(result[(1,0)],.5) and result[(2,0)] == .5
assert all(v == 0 for v in grid.values())
print('occupancy', {c:round(p,6) for c,p in result.items()})
before = merged.copy()
try:
merge_observations(merged, [('A',(0,0),.7),('B',(1,0),1.0)])
except ValueError:
pass
else:
raise AssertionError('invalid probability accepted')
assert merged == before
print('invalid_batch_rejected_unchanged', True)
# Replaying the same observation would create unsupported confidence.
twice = merge_observations(merged, observations)
assert probability(twice[(0,0)]) > result[(0,0)]
print('replayed_same_observations', round(probability(twice[(0,0)]),6))
核对概率与旧地图是否保留
occupancy {(0, 0): 0.844828, (1, 0): 0.5, (2, 0): 0.5}
invalid_batch_rejected_unchanged True
replayed_same_observations 0.967365
第一行三个概率应为0.844828、0.5、0.5。未观测(2,0)的初始log-odds仍为0。函数在旧字典副本中计算,只有成功返回后调用者才取得候选地图;示例故意在一条有效观测后加入p=1的非法记录,整批抛错,旧merged与调用前相同。这个性质只覆盖本地函数,未实现数据库事务或分布式提交。
最后一行故意把同一批记录再次合并,(0,0)变为0.967365,展示重复使用相同证据会夸大置信度。这不是新的真实观测,也不是算法获得更高精度。真实共享记录必须保存原始观测身份;若B只是转发A的同一测量,就不能当A和B各自独立观察。
遇到这些输入先停止合并
两图没有同一坐标原点、方向、分辨率时,应先解决配准,不能把同名格子直接相加。移动物体违反静态地图假设,陈旧占据值需要另定衰减或动态建模规则。0、1、NaN和无穷在本例被拒绝;概率截断、log-odds饱和值与数值存储范围需要按实际传感器模型确定,本文没有给出通用配置。
重复观测、共享祖先证据和相关传感噪声会让独立性假设失效。收到两份意见不同的地图时,不应让语言模型凭措辞挑一个“更有把握”的概率。应检查坐标、观测来源与模型定义,必要时保留冲突;没有原始证据不能把0.5任意改成0或1。
接入自己的建图任务前检查三项
先用一个已知格子的两次观测复算赔率,再核对一个未观测格保持先验,最后插入一个非法观测确认旧地图原样。只有这三项成立,才扩大到二维格子表。随后保存格子坐标、观测角色、原始观测ID、传感模型版本、更新前后L值和时间,方便判断高概率来自新证据还是重复记录。
Python 3.11 math官方文档支持log、exp、isfinite及isclose的用法。代码用分支恢复概率,避免对大正L计算exp(L);仍需在实际长期更新中监控数值范围,不把浮点计算成功当成地图可信。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/33487.html