低空多源气象扰动下无人机影响预测与风险分级(ROS 2)

本项目在 ROS 2 中演示一条从多源气象估计到小型飞行器响应预测、再到四级安全风险的完整链路。输入不是预先录制的标签,而是三路异步风估计;预测不是单点风速,而是未来 10 秒的姿态、轨迹偏差和控制裕度超限概率。

运行效果

项目刻意保持自包含:不调用在线气象 API,不依赖 AirSim,不发送飞控指令。其价值在于把“天气数据”转化为可复现的飞行风险指标,并暴露模型边界,便于后续替换为真实气象场和更高保真飞行器模型。

1. 定位与边界

本项目适合课程实验、算法原型和 ROS 2 接口联调,不是以下系统:

  • 不是业务气象预报,不提供真实天气准确率承诺;
  • 不是 6-DOF 飞行动力学或风洞试验;
  • 不是适航认证、运行许可或空管决策工具;
  • 不是纯气象因果归因。风险分数同时包含当前机动需求,航点切换时也可能出现短时升高。

模块只发布风险建议和可视化标记,不发布速度、姿态或航点控制指令。实机运行前须由持证人员独立评估。

2. 方法链路

NWP 背景场 ─┐
地面风塔   ─┼─ 逆方差融合 ─ 平均风 + 阵风/湍流 ─ 降阶飞行器模型
机载估计   ─┘                                      │
                                                   ├─ 蒙特卡洛响应集合
                                                   ├─ 超限概率与风险分数
                                                   └─ ROS 2 JSON + MarkerArray

演示中的三路数据由 GustFrontScenario 生成:NWP 平滑但有 6 秒延迟,地面风塔响应快,机载估计有 1 秒延迟和固定偏置。这样可以在没有外部数据的情况下稳定复现“多源不一致、融合后更平滑”的行为。

3. 多源风场融合

对第 个源的 ENU 风矢量 、标准差 、置信度 和数据年龄 ,权重取

融合不确定度按一阶误差传播计算:

默认 age_tau_s=30。当某一路数据变旧或置信度降低时,它的权重会下降,但不会把其他源直接丢弃。融合结果和每一路原始估计同时写入 weather 话题,方便审计。

4. 扰动与飞行器响应

阵风和湍流用离散 Ornstein-Uhlenbeck 过程近似:

飞行器采用二维水平加高度的降阶模型,核心是相对气流阻力:

控制器根据位置误差产生速度指令,再产生加速度指令。倾角不是完整姿态解算,而是维持指令加速度并补偿水平阻力所需的等效倾角:

因此这里的倾角是控制裕度代理量,不是实测滚转/俯仰角。代码中的 roll_deg、pitch_deg 也只用于演示可视化姿态,不应作为飞控姿态真值。

5. 影响预测与风险指标

每次评估从当前状态出发,生成 samples 条未来阵风样本,在 horizon_s 内推进同一控制律。四项指标分别为:

指标 定义 默认阈值
风速超限 集合中最大风速超过阈值 11 m/s
等效倾角超限 集合中最大等效倾角超过阈值 33°
轨迹偏差 相对无风参考轨迹的最大位置偏差 2.5 m
控制饱和 所需水平加速度超过可用加速度的比例 1.10

轨迹偏差不是“到远航点的距离”。对每个评估时刻,先在同一初始状态下做一次无风参考推进,再比较扰动集合与参考轨迹在同一时刻的位置。这样 30 米外的前方航点不会天然被判定为风险。

风险分数为四项超限概率的加权和:

同时设置概率下限规则,防止单项极端超限被平均分掩盖:

等级 分数条件 额外条件 含义
NOMINAL 最大概率 < 0.50 当前条件下未发现明显超限趋势
CAUTION 或最大概率 ≥ 0.50 需要关注,建议降低速度或延迟任务
WARNING 或最大概率 ≥ 0.90 高风险趋势,建议改变航线或等待天气窗口
CRITICAL 或最大概率 ≥ 0.98 演示中的最高告警级,不等于真实飞行许可结论

6. 程序结构

src/air/uav_weather_risk/
├── README.md
├── package.xml
├── setup.py / setup.cfg
├── config/demo.yaml
├── launch/main.launch.py
├── resource/uav_weather_risk
├── sample/timeseries.csv / summary.json
├── tests/test_core.py
├── main.py
└── uav_weather_risk/
    ├── core/
    │   ├── types.py       # ENU 向量、数据容器
    │   ├── weather.py     # 多源融合与阵风过程
    │   ├── aircraft.py    # 降阶响应模型
    │   ├── risk.py        # 蒙特卡洛与风险分级
    │   └── scenario.py    # 阵风锋演示场景
    ├── demo_node.py       # 合成气象与飞行状态发布
    ├── risk_node.py       # 风险预测与 MarkerArray
    ├── headless_demo.py   # 无 ROS 的离线入口
    └── plotting.py        # CSV/PNG/GIF 输出

7. 构建与运行

推荐环境:Ubuntu 22.04、ROS 2 Humble、Python 3.10。Humble 之外的发行版未验证。

source /opt/ros/humble/setup.bash
rosdep install --from-paths src/air/uav_weather_risk --ignore-src -r -y
colcon build --symlink-install --packages-select uav_weather_risk
source install/setup.bash
python3 -m unittest discover -s src/air/uav_weather_risk/tests -v
ros2 launch uav_weather_risk main.launch.py

启动后查看:

ros2 topic list | grep uav_weather_risk
ros2 topic echo /uav_weather_risk/assessment

RViz2 中添加 MarkerArray 显示,话题选择 /uav_weather_risk/markers,Fixed Frame 设为 map。标记包括预测轨迹集合、当前风险包络球、融合风矢量和等级文字。SPHERE 的 scale.x/y/z 是直径,本模块按等级设置为 1.0、1.6、2.2、2.8 米。

话题

话题 类型 方向 说明
/uav_weather_risk/odom nav_msgs/Odometry 输入 ENU 位置、速度,frame map
/uav_weather_risk/weather std_msgs/String 输入 融合风场与原始源 JSON
/uav_weather_risk/target geometry_msgs/PointStamped 输入 当前航点;缺失时按悬停评估
/uav_weather_risk/assessment std_msgs/String 输出 等级、分数、概率、原因、输入年龄
/uav_weather_risk/markers visualization_msgs/MarkerArray 输出 RViz2 预测与风险可视化

风险节点对坏 JSON、非有限数、缺失输入做防御处理。输入超过 stale_timeout_s 时发布 valid=false、建议等级 CAUTION,并在 reason 中说明是数据陈旧,而不是伪造一个正常结论。

8. 离线复现

不安装 ROS 2 也能运行核心模型。仅绘图需要 Matplotlib;核心算法只使用 Python 标准库。

cd src/air/uav_weather_risk
python3 -m pip install matplotlib
python3 main.py --duration 60 --seed 42 --output-dir artifacts/demo

输出:

  • timeseries.csv:121 个评估时刻的时间、位置、风场、概率和等级;
  • summary.json:峰值、等级计数和记录 SHA256;
  • trajectory.png、risk_timeline.png、wind_sources.png;
  • demo.gif:俯视轨迹动画。

随包 sample/ 目录保存了本次实际生成的小型 CSV 和 summary,可直接核对;完整 PNG/GIF 位于 docs/img/air/uav_weather_risk/。

9. 本次实际运行结果

9.1 ROS 2 Humble 实测

在 Ubuntu 22.04.5 / ROS 2 Humble / Python 3.10 虚拟机中完成了实际构建、节点启动和话题录制:

colcon build --packages-select uav_weather_risk
Finished <<< uav_weather_risk [0.80s]
Summary: 1 package finished [1.04s]

12 项 unittest 全部通过。启动 ros2 launch uav_weather_risk main.launch.py 后确认了 5 个话题:

/uav_weather_risk/assessment
/uav_weather_risk/markers
/uav_weather_risk/odom
/uav_weather_risk/target
/uav_weather_risk/weather

连续录制 49.2 秒,共 486 个真实 ROS 2 里程计样本:

项目 结果
NOMINAL 315
CAUTION 91
WARNING 35
CRITICAL 45
最大真实风速 11.81 m/s
最大融合风速 11.07 m/s
最大风险分数 0.785

ROS 2 Humble 实测轨迹与话题流

这张图的左图来自 /uav_weather_risk/odom 的实际轨迹,颜色来自 /uav_weather_risk/assessment;右图的真实风速、融合风速和风险分数来自同一时间段的话题流,不是离线重算。

VM 内 RViz2 实际渲染

上图是在 VM 的 Xvfb 显示中实际启动 RViz2 后抓取的窗口,不是宿主机生成的示意图。RViz2 订阅 /uav_weather_risk/markers,画面中可以看到风险等级文字、风险包络、风矢量和预测轨迹。

运行中抓取的一条有效评估消息:

{"valid":true,"target_available":true,"level":0,"level_name":"NOMINAL","score":0.0,"probabilities":{"wind":0.0,"tilt":0.0,"position":0.0,"control":0.0},"predicted_p95":{"wind_mps":9.17,"tilt_deg":14.71,"position_error_m":0.82,"control_saturation":0.51},"dominant_factor":"wind","reason":"wind above 11.0 m/s in 0% of 64 rollouts over 10.0 s","horizon_s":10.0,"samples":64}

原始 CSV、summary、话题列表和这条评估消息保存在 sample/ 目录,便于复核。

9.2 离线 60 秒复现

环境:Windows 11、Python 3.13.7;NumPy 2.3.4、Matplotlib 3.10.8 仅用于绘图。

固定 seed=42、60 秒、评估周期 0.5 秒、每次 64 个集合成员的本次输出:

项目 结果
评估次数 121
NOMINAL 78
CAUTION 20
WARNING 13
CRITICAL 10
最大真实风速 12.57 m/s
最大融合风速 10.79 m/s
最大等效倾角 33.23°
最大控制饱和 1.285
最大风险分数 0.807
峰值时刻 33.5 s
记录 SHA256 cc617e25...d600173a

轨迹与风险等级

风险时间线

多源融合

这些离线图来自同一份实际 CSV,不是手工绘制的示意图。

10. 验证状态与提交前检查

已完成:

  • Ubuntu 22.04.5 / ROS 2 Humble 下 colcon build、ros2 launch 和 5 个话题的实际运行;
  • 49.2 秒、486 个 /uav_weather_risk/odom 样本的 ROS 2 运行数据录制与绘图;
  • VM 内 Xvfb + RViz2 实际加载 /uav_weather_risk/markers 并抓取渲染截图;
  • 12 项核心 unittest;
  • 60 秒离线端到端运行,CSV、summary、PNG、GIF 均由同一 seed=42 生成;
  • package.xml 使用 ament_python,入口、launch、config、resource 和 README 齐备。

尚未完成:

  • 没有真实气象数据、AirSim 飞行或实机测试。
  • 还应由另一名同学在另一台机器复跑一次并确认干净退出。

11. 参数说明

参数 默认值 说明
rate_hz 10.0 演示节点发布频率
duration_s 0.0 0 表示持续运行
assessment_period_s 0.5 风险评估周期
horizon_s 10.0 预测时域
dt_s 0.25 集合推进步长
samples 64 每个评估时刻的集合成员数
max_wind_mps 11.0 风速超限阈值
max_tilt_deg 33.0 等效倾角阈值
max_position_error_m 2.5 相对无风参考轨迹的偏差阈值
max_control_saturation 1.10 控制饱和阈值
turbulence_sigma_mps 1.3 阵风过程标准差
turbulence_tau_s 3.0 阵风相关时间
stale_timeout_s 2.0 输入过期判定
keep_paths 8 保留并可视化的集合轨迹条数

风险阈值是演示默认值,不是监管限值。替换机型或场景时必须重新标定,并在文档中记录标定数据来源。

12. 局限与后续

  1. 当前气象场是合成场景,不是 ERA5、雷达、激光雷达或地面站实测;真实数据接入需要单独的数据适配器和缺测处理。
  2. 飞行器模型是降阶代理,不包含旋翼气动、执行机构延迟、姿态控制器内环和电池压降;不可用于适航或寿命结论。
  3. 风险分数没有经过真实事故样本校准,概率含义是“模型集合内超限频率”,不是经过验证的实际事故概率。
  4. 尚未实现 6-DOF 与 PX4/ArduPilot SITL 闭环,也没有真实飞行数据对照。
  5. 后续可扩展:接入 OpenHUTB/AirSim 状态、ERA5/地面站适配器、集合校准、SORA 风格风险矩阵、6-DOF 校验和真实飞行日志回放。

13. 参考与术语

  • MIL-F-8785C / Dryden 湍流模型:阵风相关时间与功率谱的工程近似;
  • JARUS SORA:仅参考其“地面风险/空中风险”和运行边界的分层思想,本项目不声称符合 SORA;
  • 中国民航局 CCAR-92《民用无人驾驶航空器运行安全管理规则》:实际运行须遵循现行法规与地方空域管理要求;
  • OpenHUTB/ros2 的 uav_mission_safety_manager、telemetry_diagnostics 模块:本项目沿用其“可离线复现 + ROS 2 薄封装 + 明示验证边界”的工程风格。

14. 大模型使用声明

本项目使用 OpenAI Codex 辅助完成 ROS 2/Python 代码、单元测试、文档草稿、离线仿真与绘图脚本,以及 PR 描述整理。实验 CSV、ROS 2 运行数据和图表均由随附代码实际运行生成,未使用大模型生成或篡改实验数据。作者 Leclerc666 已逐项复核代码、参数和文档,并对提交内容、数据真实性及后续维护负责。