AirSim Unity 无人机高度自适应越障实验(设计、离线验证与实飞结果)
状态(2026-11-10):代码、57 项离线测试与两次实飞均已完成。 2026-11-09 在本机 Unity 两次实飞均越障到点(第 5 节),录屏见同节;两次数据为相机偏移修正前采集,修正后的复飞尚未进行。本文结论只覆盖这一静态矮墙场景,高柱水平绕行仍待实飞。
1. 问题与动机
同仓库的 AirSim Unity 单柱绕行(PR #597,已合入 master)与双柱 S 形绕行(本地分支 codex/airsim_double_pillar_avoidance)都只扫描深度图中部竖直带:巡航高度全程锁定,任何进入通道的障碍都只能水平绕行。对一根矮墙而言,这意味着为了绕过一堵本可以从上方飞过的墙,要多飞十几米;在窄走廊里甚至可能找不到可用的水平通道。
本模块保留原有的深度解码、通道膨胀、偏航平滑与看门狗,把决策从“只有水平方向”扩展为“水平 + 高度”:
- 读取整幅深度图,逐列恢复障碍顶边相对相机的高度;
- 顶边足够低、上方净空已验证、且高度预算允许时,先原地爬升再从上方通过(
overfly); - 顶边太高、净空不可验证或预算不足时,退回原来的水平绕行(
curve); - 越障连续失败两次后永久解除越障,避免反复朝障碍爬升。
边界:这是只依赖前视深度图的局部规划器,不建图、不做全局寻路,也不替代 uav_lidar_safety_corridor、uav_altitude_command_guard、octree_uav_3d_pathfinding 等模块。
2. 方法
2.1 深度与几何
输入是 AirSim Unity 的浮点 DepthVis。按裁剪面与色彩空间解码为沿光轴的米制深度:
shader = (linear_depth^(1/4)),经 sRGB 编码后由相机返回
depth = ((pixel^(1/0.25)) / (1 + near/far) + near/far) * far
相机水平前视、居中安装。AirSim Unity 的 CameraInfo.fov 来自 Camera.fieldOfView,是垂直视场(已在 DataCaptureScript.cs 中核对);方形像素下横纵焦距相同,因此焦距(像素)由图像高度换算:
f = H / (2 * tan(FOV / 2)), c = (H - 1) / 2
前视相机 0 实际是 Front_Center,垂直 FOV 90°;160×90 画面下水平视场约 121°。若按宽度换算焦距(把 FOV 当水平视场),顶边高度会被低估约宽高比倍,高柱可能被误判为可越。
AirSim Unity 的浮点深度缓冲区与 Unity 纹理一致、自下而上存储:2026-11-09 现场帧验证,缓冲第 5 行读到的是水平相机下方 1.4 m 的地面、第 131 行是天空,方向与自上而下正好相反。读取后先按行翻转再做列分析;只取每列最小值的旧模块不受影响,高度逻辑则必须翻转,否则会把障碍底部当作顶部、低估墙高并导致越障爬升不足。
对每一列,在分析带 [row_top, row_bottom] 内找最近障碍距离 d,再向上搜索同一障碍的连续像素,直到遇到自由空间或无效像素,得到顶边行 r_t:
top_height = d * (c - r_t) / f # 顶边在相机上方为正
overhead = d * (c - row_top) / f - top_height
row_top = H/8,row_bottom = H - 1 - ground_rows。底部 ground_rows 行按地面处理而不当作障碍;无效像素按 0 距离处理,宁可悬停也不把坏点读成自由空间。overhead > 0 才说明相机确实看到了顶边之上的自由空间;障碍填满整个分析带时 overhead = 0,不会触发爬升。
2.2 决策
先用与单柱模块相同的膨胀扫掠体测试计算前向通道距离,膨胀半径 r = 机体半径 + 安全间距。所有进入该扫掠体的障碍(按通道入口距离判断,而不是只看图像中部窄带)都参与越障可行性判断:
climb_needed = max(0, max(top_height) + camera_offset + r)
overfly_ok = armed
and 存在进入扫掠体的障碍
and climb_needed <= min(max_climb, max_altitude - cruise_altitude)
and min(overhead) > 0
camera_offset 是相机相对机体原点的安装高度,连接时用 simGetCameraInfo 实测(本机约 0.49 m)。top_height 是相对相机的,而 r 是相对机体的,两者不能直接相加;漏掉这个偏移会让实际净空比配置的 r 少 0.49 m。满足时输出 overfly;否则若通道畅通输出 advance;再否则在候选方向里按目标方向、上次航向、绕行侧迟滞和净空评分,输出 curve;没有可用方向时输出 blocked 并原地扫描。用整幅图而不是中部窄带聚合障碍,是为了挡住“正前方矮墙可越、侧方 1.8 m 处高杆在膨胀半径内”这一危险组合。
2.3 越障状态机
OverflyState 保证一次爬升被提交后不会在障碍顶附近来回升降:
- 保持:连续
OVERFLY_HOLD_FRAMES = 3帧目标高度稳定(±0.15 m)才提交; - 只升不降:提交后目标只能被抬高,不能在爬升途中降低,否则会重新落回障碍上;
- 完成:连续
OVERFLY_DONE_FRAMES = 6帧前向通道畅通才结束; - 保持:结束后不立即下降,保持越障高度继续飞行
--overfly-hold-distance(默认 5 m)再降回巡航,保证机体越过障碍远端时仍留有完整安全间距;新的越障提交或失败会取消未完成的保持; - 中断:出现水平绕行或 blocked 扫描时清零已累计的畅通帧,避免把不连续的帧拼成一次完成;
- 失败:提交 10 s 后仍未达到 committed 高度的 70% 记为一次失败;累计
OVERFLY_MAX_FAILS = 2次后armed=False,本任务永久退化为水平绕行; - 宽限:进度只从提交(或目标被抬高 0.2 m 以上)之后计时,避免把刚起步的正常爬升误判为停滞。
飞行循环在 overfly 期间把前向速度压到 0,直到实测抬升距 committed 高度不足 0.3 m,再以规划速度越过顶边。这样“先升高、后前进”,避免一边爬升一边把机头送进墙里;越过之后保持越障高度飞行 --overfly-hold-distance 米再下降,避免机体在墙顶附近过早掉高。高度目标被 max_altitude 截断,绝不用爬升预算换碰撞余量。
2.4 飞行循环与安全
沿用同仓库已验证的开环高度通道(该 Unity 插件空中仍报 landed_state=Landed,闭环 moveToZAsync 不可靠),以实测高度判断爬升和降落;起飞后再建立碰撞基线,避免地面接触误报;每帧只取一次深度并校验时间戳,冻结相机立即中止;看门狗分别约束“连续 blocked 扫描 30 s”和“20 s 无水平进展”;异常时尝试降落,落地未确认则保留控制权并提示检查 Unity。
3. 参数
| 参数 | 默认值 | 含义 |
|---|---|---|
--altitude |
3.0 | 巡航高度,相对起飞后位置,米 |
--max-climb |
3.5 | 单次越障最大抬升,米 |
--max-altitude |
7.0 | 相对起飞点的绝对高度上限,米 |
--overfly-hold-distance |
5.0 | 越过障碍后保持越障高度继续飞行的水平距离,米;0 表示越过即下降 |
--speed |
1.2 | 巡航速度,m/s |
--stop-distance |
3.0 | 障碍前的最小停距,米 |
--curve-distance |
2.0 | 距障碍多早开始水平绕行,米 |
--safety-margin |
1.5 | 障碍面之外的额外间距,米 |
--vehicle-radius |
0.6 | 含桨叶的机体半径,米 |
--ground-rows |
12 | 图像底部按地面处理的行数 |
--timeout |
180 | 任务超时,秒 |
复现命令为了给墙高标定留 ±0.5 m 余量,显式使用 --max-climb 4.5 --max-altitude 10;默认值 3.5/7 对应更紧的预算。max-climb 必须大于 vehicle-radius + safety-margin + 相机偏移 + 顶边高度(0.6 + 1.5 + 0.49 ≈ 2.6 m,再加顶边),否则永远不会触发越障。
4. 离线验证(已完成)
2026-11-09 在 Python 3.9.13、无 AirSim 服务端的条件下运行:
Push-Location .\src\air\airsim_height_aware_overfly
& "..\..\..\.venv-airsim\Scripts\python.exe" -m unittest -v test_avoidance
Pop-Location
结果:57 项全部通过。覆盖范围:
- 深度解码:线性/sRGB 往返、非法值与坏标定、Unity 自下而上缓冲区的行翻转;
- 相机几何与列顶边恢复、地面行排除、死像素、全无效图像;
- 通道膨胀、远处障碍不参与、矮墙越障、高墙/预算/天花板/顶边不可见时改水平绕行、侧向高杆否决越障、相机安装偏移计入预算与 committed 目标;
- 越障状态机:3 帧提交、只升不降、完成、绕行/扫描清空帧不误判完成、两次失败解除、越障后保持高度与新爬升取消保持、10 s 进度宽限;
- 看门狗、运行目录隔离、事件日志、偏航限幅、参数校验。
这些测试只验证算法逻辑,不能替代仿真飞行,也不证明任何真实场景的通过率。
5. 实飞验证(2026-11-09)
环境:Windows 11、Python 3.9.13、AirSim 1.8.1 Unity、Unity 2019.3.12f1、SimpleFlight;场景为 DroneDemo,出生点 Unity (-320, 3, 212),障碍为 8 m 宽、6 m 高、0.4 m 厚的矮墙(Unity (-320, 3, 227),Scale (8, 6, 0.4)),目标北向 +20 m(Unity Z=232),命令见模块 README 第 4 节。两次连续实飞均完成越障:巡航时机体 z≈-5.0~-5.3,相机 0 装在机体原点上方约 0.49 m(simGetCameraInfo 实测),故相机约 5.5~5.8 m,6 m 墙顶只比相机高约 0.2~0.5 m,top_height_m 读数 0.04~0.11 m(顶边像素混叠会再低一两行,约 0.09 m/行):
| 运行(本地时间) | 时长 | committed 爬升 | 实测 lift_m 峰值 |
越障后保持 | 到点位置(目标 232) | 降落 |
|---|---|---|---|---|---|---|
| 09:15:09 | 33.1 s | 2.14 m | 2.05 m | 5.25 m | 231.33(误差 0.67 m) | landing_confirmed |
| 09:16:55 | 32.6 s | 2.06 m | 1.97 m | 5.22 m | 231.42(误差 0.58 m) | landing_confirmed |
这两次运行是相机偏移修正前采集的。 当时 committed 2.06/2.14 m 按 top + 2.1 计算,实际机体净空只有约 1.53/1.61 m(少算了一个 0.49 m 的相机安装偏移)。代码现已把偏移计入 climb_needed 与 committed 目标,复飞时 committed 会约 2.6 m、净空恢复到 2.1 m;该修正有 5 项新增离线测试覆盖,尚未复飞。
两次事件序列一致:mission_started → overfly_started (climb≈2.1, top≈0.04–0.11) → overfly_completed (hold=2.06/2.14) → overfly_hold_done (travelled=5.22/5.25) → goal_reached → landing_confirmed;无新增碰撞、无 overfly_failed。flight.csv 中 overfly_hold=1 的保持段持续约 5 m 路程,mode 依次为 advance → overfly → advance。
证据文件(第二次运行,随 PR 提交):flight.csv(22 列,含 lift_m、top_height_m(相对相机)、overfly_hold)、events.jsonl、settings.json。

图为 09:16:55 这次运行的录屏:无人机在墙前先原地爬升,越过墙顶后保持越障高度继续飞行约 5 m,再降回巡航并到达目标。
仍待补:高柱(不可越障时水平绕行)的实飞录像与数据。
6. 已知限制
- 实飞只做了两次同一静态矮墙、直线航线的场景;3 帧保持、6 帧结束、10 s 宽限、2 次解除等阈值只有这两次数据,未做多速度、多高度、多障碍标定。
- 只处理静态障碍;相机必须水平前视并居中。侧后方、细杆、动态物体、玻璃/反光面和复杂死胡同未验证。
- 顶边只在图像竖直带内可验证:障碍高到填满图像上部时一直改水平绕行,不会盲爬。
- 图像底部按固定行数当地面排除;相机俯仰、地面坡度或低空飞行会让地面进入障碍带,需要重新标定
--ground-rows。 - 越障是开环高度控制,依赖推力余量和实测高度;
--max-altitude只是相对起飞点的软件上限,不是避障保证。 - 单目深度解码依赖本机 Unity 插件、裁剪面和色彩空间,不能原样当作 Unreal AirSim 的米制深度。
- 高度换算依赖“AirSim Unity 回报垂直 FOV”这一事实;换到返回水平 FOV 的平台(如 Unreal)时必须改回按图像宽度换算,否则高度会系统性偏差。
- 局部方向选择不构成全程连续曲率或固定最小间距的数学保证;仿真成功不能作为真实无人机部署结论。
7. 提交前检查
- [x] 源码在
src/air/airsim_height_aware_overfly,文档在docs/air,使用英文下划线命名。 - [x] 提供
main.py、launch/run_demo.ps1、requirements.txt、README.md和 57 项离线测试。 - [x] 矮墙越障实飞两次(2026-11-09),真实数据、录屏与 CSV/事件日志已写入第 5 节。
- [ ] 高柱水平绕行实飞(待补)。
- [ ]
mkdocs build与页面链接检查(文档入口已加入mkdocs.yml和docs/index.md;本机未安装 mkdocs)。
大模型使用声明
本模块代码及文档由 DeepSeek Harness 辅助完成方案讨论、代码起草、离线测试与文档撰写;算法基线继承自已合入的单柱模块(PR #597)。提交者负责检查验证,并对提交内容的正确性和完整性负责。