资讯详情 AirSim无人机自主飞行仿真:从环境搭建到避障算法验证
📅 2026/10/30 15:45:51
简介这份资源面向无人机算法、无人驾驶与智能机器方向的开发者与学习者聚焦在AirSim仿真平台下实现复杂环境中的无人机自主飞行。内容围绕仿真环境搭建、六自由度建模与PID/滑模控制、多传感器感知与避障、GPS与视觉SLAM定位导航、A*与RRT路径规划以及多机协同等关键环节展开帮助读者把控制算法与真实飞行场景衔接起来。压缩包共2个文件包含1个Python脚本与1个Markdown说明文档整体约7KB脚本对应核心算法实现文档用于梳理项目结构与使用说明便于快速上手与二次修改。目前已有342人学习下载。通过阅读源码与说明读者可掌握AirSim环境配置、算法调试与性能评估的完整思路并借助实时仿真反馈迭代优化飞行策略为无人机自主飞行研究与工程实践打下基础。1. 复杂环境下的无人机自主飞行AirSim 能帮你验证到什么程度城市楼宇间的 GPS 拒止区域、山区突发的风切变、室内走廊的狭窄通道——这些场景下让无人机自己飞最怕的不是算法写不出来而是根本没有一个安全、可重复、成本可控的地方去验证它。AirSim 仿真平台就是干这个的它把 Unreal Engine 的视觉渲染能力和物理引擎绑在一起给无人机提供一个带真实动力学响应和传感器噪声的虚拟试飞场。你可以在里面反复测试路径规划、避障、视觉感知模块炸机了按一下重置就行不用捡碎片。这个方案适合做无人机算法验证的研究生、做飞控原型的工程师以及需要快速迭代自主飞行逻辑但苦于外场测试成本太高的团队。它不能替代真机但能让你在真机之前把大部分逻辑错误筛掉。2. AirSim 环境搭建与无人机模型配置从零到能起飞2.1 为什么选 AirSim 而不是纯数学仿真做无人机自主飞行验证常见做法有三类纯 MATLAB/Simulink 数学仿真、Gazebo 加 PX4 软件在环、以及 AirSim 这类带高保真视觉渲染的平台。纯数学仿真跑得快但传感器数据是理想化的视觉算法根本没法测Gazebo 生态成熟但场景视觉真实度有限做视觉感知时域随机化很吃力。AirSim 的核心优势在于它把 Unreal Engine 的渲染管线接进来了摄像头能输出带光照、纹理、遮挡的真实感图像IMU 和气压计也带噪声模型。如果你的自主飞行方案里包含视觉避障、目标识别或者视觉里程计AirSim 是目前开源方案里比较省事的选择。选版本时注意AirSim 有 Unreal 插件版和 Unity 版社区里做无人机自主飞行绝大多数用 Unreal 插件版。Windows 下建议用 Visual Studio 2019 或 2022 编译Linux 下用 clang 更顺。显卡方面NVIDIA GTX 1060 以上能跑动基础场景想开高质量渲染建议 RTX 2070 起步。2.2 搭建最小可飞环境下面以 Windows 加 Unreal Engine 4.27 为例走一遍从拉取代码到无人机起飞的流程。Linux 下命令基本一致把路径换成对应目录即可。# 1. 克隆 AirSim 仓库包含 Unreal 插件和 Python 客户端 git clone https://github.com/microsoft/AirSim.git cd AirSim # 2. 运行构建脚本生成插件和 Python 包 # Windows 下用 build.cmdLinux 下用 ./build.sh ./build.sh # 3. 下载一个预编译的 Unreal 场景Blocks 环境最轻量 # 从 AirSim 官方 releases 页面获取 Blocks.zip解压到任意目录 # 注意不要放在中文路径下Unreal 对中文路径支持有问题构建完成后把 AirSim 插件复制到你的 Unreal 项目 Plugins 目录或者直接用官方 Blocks 场景。启动 Blocks.exe 后按以下步骤配置无人机// 在 Documents/AirSim/settings.json 中写入 { SettingsVersion: 1.2, SimMode: Multirotor, Vehicles: { Drone1: { VehicleType: SimpleFlight, AutoCreate: true, X: 0, Y: 0, Z: 0, Sensors: { Imu: { SensorType: 2, Enabled: true, AngularRandomWalk: 0.0001, GyroBiasStabilityTau: 500, GyroBiasStability: 4.0 }, Barometer: { SensorType: 1, Enabled: true, PressureFactorSigma: 0.001 }, CameraFront: { SensorType: 0, Enabled: true, CaptureSettings: [ { ImageType: 0, Width: 640, Height: 480, FOV_Degrees: 90 } ] } } } } }这段配置做了三件事把仿真模式设为多旋翼创建一架默认无人机并挂载 IMU、气压计和前视摄像头。IMU 的AngularRandomWalk和GyroBiasStability参数决定了陀螺仪噪声水平做视觉惯性里程计验证时这两个值直接影响算法鲁棒性。气压计的PressureFactorSigma控制高度噪声室内飞行可以调小到 0.0005 减少高度抖动。摄像头ImageType: 0表示场景彩色图改成 1 是深度图2 是分割图做视觉感知时按需切换。2.3 用 Python 客户端让无人机动起来AirSim 提供 Python 客户端方便你写自主飞行脚本。先安装依赖pip install airsim msgpack-rpc-python然后跑一个最简单的起飞加悬停脚本import airsim import time # 连接仿真器默认端口 41451 client airsim.MultirotorClient() client.confirmConnection() # 解锁电机并起飞到 5 米高度 client.enableApiControl(True) client.armDisarm(True) client.takeoffAsync().join() # 飞到指定位置x10, y0, z-5NED 坐标系z 负值表示向上 client.moveToPositionAsync(10, 0, -5, 5).join() # 悬停 3 秒读取 IMU 数据 time.sleep(3) imu_data client.getImuData() print(角速度:, imu_data.angular_velocity) print(线加速度:, imu_data.linear_acceleration) # 降落并锁定 client.landAsync().join() client.armDisarm(False) client.enableApiControl(False)这里的关键是 NED 坐标系x 朝北y 朝东z 朝下。所以z-5表示离地 5 米。moveToPositionAsync的第四个参数是速度单位米每秒。getImuData()返回的角速度单位是弧度每秒线加速度单位是米每二次方秒和真实 IMU 一致。如果你发现无人机起飞后漂移严重先检查 settings.json 里 IMU 噪声参数是不是设得太大再确认仿真器帧率是否稳定——帧率波动会导致物理积分误差累积。3. 复杂环境场景构建把楼宇、树木和风加进去3.1 用 Unreal 编辑器搭建自定义场景Blocks 场景太干净验证不了避障算法。你需要一个带障碍物的环境。常见做法是在 Unreal Engine 里新建一个 Level从自带资源库拖入建筑、树木、围栏等静态网格体然后调整 AirSim 插件的生成点。具体步骤打开 Unreal 项目在 Content Browser 里搜索SM_Building或SM_Tree这类资源拖到视口中。用缩放工具调整障碍物尺寸确保无人机能穿过间隙。一般走廊宽度留 2 到 3 米比较接近真实楼宇。在 World Settings 里确认 AirSim 插件已启用GameMode 设为AirSimGameMode。把无人机起始点放在障碍物前方 5 米处避免一起飞就撞。如果不想自己搭AirSim 官方还提供 Neighborhood 和 City 场景建筑密度更高适合测试城市环境下的路径规划。下载后直接运行对应 exesettings.json 不用改客户端连接方式一样。3.2 风场扰动配置与参数影响复杂环境不只是静态障碍物风是让无人机自主飞行翻车的主要原因之一。AirSim 支持在 settings.json 里配置风场{ Vehicles: { Drone1: { VehicleType: SimpleFlight, AutoCreate: true, Wind: { Enabled: true, MeanWindSpeed: 5.0, WindDirection: [1.0, 0.0, 0.0], GustAmplitude: 2.0, GustFrequency: 0.5 } } } }MeanWindSpeed是平均风速单位米每秒5.0 大概相当于 3 级风无人机能明显感到侧向推力。WindDirection是风向向量[1,0,0]表示从北向南吹。GustAmplitude是阵风幅度GustFrequency是阵风频率这两个参数模拟突风。做抗风扰算法验证时我一般从 3 米每秒开始逐步加到 8 米每秒观察位置控制器能不能把误差压回 1 米以内。如果一加风就发散先检查姿态控制环的积分限幅是不是太松。3.3 传感器噪声与采样率设置真实 IMU 采样率通常 100 到 200 HzAirSim 默认按仿真帧率输出可能远高于这个值。如果你的算法假设固定采样周期需要做降采样或者直接在 settings.json 里限制Imu: { SensorType: 2, Enabled: true, AngularRandomWalk: 0.0002, GyroBiasStabilityTau: 300, GyroBiasStability: 5.0, LinearAccelerationRandomWalk: 0.001, AccelBiasStabilityTau: 400, AccelBiasStability: 8.0 }AngularRandomWalk和LinearAccelerationRandomWalk分别控制陀螺仪和加速度计的随机游走噪声值越大噪声越明显。做视觉惯性里程计时这两个参数要和真实 IMU 数据手册对标否则仿真里跑通的算法上真机可能直接崩。摄像头帧率在CaptureSettings里用Framerate字段控制默认 30 帧做高速视觉伺服可以调到 60。4. 自主飞行算法接入路径规划与避障的仿真验证4.1 用 Python 写一个 A* 路径规划器并接入 AirSimAirSim 本身不提供路径规划算法但你可以用 Python 在外部算好路径点再通过moveToPositionAsync逐个飞过去。下面是一个简化的 A* 实现假设你已经从仿真器拿到了障碍物栅格地图import airsim import numpy as np import heapq def astar(grid, start, goal): # grid: 0 可通行, 1 障碍物 rows, cols grid.shape open_set [(0, start)] came_from {} g_score {start: 0} f_score {start: heuristic(start, goal)} while open_set: _, current heapq.heappop(open_set) if current goal: return reconstruct(came_from, current) for dx, dy in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(-1,1),(1,-1),(1,1)]: neighbor (current[0]dx, current[1]dy) if not (0 neighbor[0] rows and 0 neighbor[1] cols): continue if grid[neighbor] 1: continue tentative_g g_score[current] np.hypot(dx, dy) if tentative_g g_score.get(neighbor, float(inf)): came_from[neighbor] current g_score[neighbor] tentative_g f_score[neighbor] tentative_g heuristic(neighbor, goal) heapq.heappush(open_set, (f_score[neighbor], neighbor)) return [] def heuristic(a, b): return np.hypot(a[0]-b[0], a[1]-b[1]) def reconstruct(came_from, current): path [current] while current in came_from: current came_from[current] path.append(current) return path[::-1] # 接入 AirSim client airsim.MultirotorClient() client.confirmConnection() client.enableApiControl(True) client.armDisarm(True) client.takeoffAsync().join() # 假设栅格分辨率 1 米起点 (0,0)终点 (20,15) grid np.zeros((30, 30)) grid[10:15, 5:20] 1 # 模拟一堵墙 path astar(grid, (0,0), (20,15)) for wp in path: x, y wp[0], wp[1] client.moveToPositionAsync(x, y, -5, 3).join()这段代码的核心是 A* 搜索grid里 1 表示障碍物0 表示可通行。heuristic用欧几里得距离比曼哈顿距离更适合无人机斜向飞行。接入 AirSim 时把栅格坐标直接映射到 NED 坐标系的 x 和 yz 固定为 -5 米。每飞到一个路径点用.join()阻塞等待确保飞控跟得上。如果发现无人机在拐角处切内弯撞墙把路径点加密或者加一个膨胀层让障碍物栅格向外扩张 0.5 米。4.2 避障验证用深度图做反应式避障路径规划是全局的避障需要局部反应。AirSim 的深度图可以直接拿来用import airsim import numpy as np import cv2 client airsim.MultirotorClient() client.confirmConnection() client.enableApiControl(True) client.armDisarm(True) client.takeoffAsync().join() # 请求深度图 responses client.simGetImages([ airsim.ImageRequest(CameraFront, airsim.ImageType.DepthPlanar, True) ]) depth airsim.list_to_2d_float_array( responses[0].image_data_float, responses[0].width, responses[0].height ) # 取图像下半部分计算最近障碍物距离 h, w depth.shape roi depth[h//2:, :] min_dist np.min(roi) print(最近障碍物距离: %.2f 米 % min_dist) # 简单反应式避障如果太近就后退 if min_dist 2.0: client.moveByVelocityAsync(-1, 0, 0, 1).join() else: client.moveByVelocityAsync(2, 0, 0, 1).join()ImageType.DepthPlanar返回的是平面深度图每个像素值就是该点到相机的距离单位米。取下半部分是因为无人机前飞时下方障碍物更危险。min_dist小于 2 米就触发后退这是一个最简反应式避障。实际用的时候可以把深度图分成左中右三列分别判断实现绕行而不是单纯后退。注意深度图有噪声单帧判断容易误触发建议做 3 帧滑动平均。4.3 仿真结果怎么评估跑完仿真不能只看“没撞”要量化。我一般记录三个指标位置跟踪误差、避障最小距离、任务完成时间。位置误差用client.getMultirotorState().kinematics_estimated.position和期望路径点做差取均方根。避障最小距离从深度图里逐帧取最小值看有没有低于安全阈值。任务完成时间从起飞到降落的仿真时钟差。这三个指标在 AirSim 里都能用 Python API 拿到跑 10 次取平均比单次结果可靠得多。5. 避坑与排查AirSim 仿真中常见的 5 个翻车点5.1 无人机起飞后原地打转或漂移现象解锁后无人机不升空或者升空后水平方向持续漂移位置控制器压不住。原因最常见的是 settings.json 里 IMU 噪声参数设得过大姿态解算发散其次是仿真器帧率低于 20 帧物理积分误差累积还有一种情况是SimpleFlight控制器的位置环增益不适合当前风场配置。解决先把AngularRandomWalk和LinearAccelerationRandomWalk减半看是否改善。如果无效在 Unreal 编辑器里按~打开控制台输入stat fps查看帧率低于 20 就降低渲染质量或换更简单的场景。风场开启时把MeanWindSpeed暂时设为 0确认无风状态下能稳定悬停再逐步加风。5.2 Python 客户端连接超时现象运行脚本时报msgpackrpc.exceptions.RPCError: Timeout或者ConnectionRefusedError。原因仿真器没启动或者端口被占用或者防火墙拦截了 41451 端口。Linux 下还可能是 AirSim 插件没编译进 Unreal 项目。解决先确认 Blocks.exe 或你的 Unreal 项目已经运行并且窗口标题栏显示 AirSim 已加载。然后在命令行跑netstat -ano | findstr 41451Windows或lsof -i :41451Linux看端口是否监听。如果端口没监听检查 settings.json 里ApiServerPort是不是被改了。防火墙问题临时关闭防火墙测试确认后加例外规则。5.3 深度图全零或全是无穷大现象simGetImages返回的深度图所有像素值为 0 或inf避障逻辑完全失效。原因摄像头方向不对或者ImageType设错了。DepthPlanar和DepthPerspective返回的数据格式不同前者是平面深度后者是透视深度用错会导致数值异常。另外如果场景里没有障碍物深度图自然全是无穷大。解决先确认ImageRequest里的摄像头名称和 settings.json 里一致大小写敏感。然后检查ImageType避障一般用DepthPlanar。如果场景太空放几个立方体进去再测。深度图数值可以用np.isfinite()过滤无穷大再取最小值。5.4 路径规划能跑通但无人机飞不到终点现象A* 算出的路径在栅格地图上看着没问题但无人机飞到一半就停住或者绕圈。原因栅格地图和 AirSim 实际场景的坐标系没对齐或者路径点之间的间距太大位置控制器在拐角处超调。还有一种情况是路径点 z 值设成了正值NED 坐标系下正值表示向下无人机往地下飞了。解决在仿真器里用client.simPlotPoints把路径点画出来和实际场景对比。路径点间距建议不超过 2 米拐角处加密到 0.5 米。z 值统一用负值起飞高度 5 米就写 -5。如果位置控制器超调把moveToPositionAsync的速度参数从 5 降到 2牺牲时间换稳定。5.5 仿真跑久了越来越卡现象刚开始跑 60 帧跑十几分钟后掉到 10 帧以下无人机响应变慢。原因Unreal 编辑器在 Play 模式下会持续累积未释放的资源尤其是频繁调用simGetImages时图像数据没及时回收。另外Python 客户端如果每帧都请求高分辨率图像内存会涨得很快。解决降低图像请求频率避障不需要每帧都拿深度图隔 3 帧取一次足够。在 Python 脚本里显式删除不再使用的图像数据用del responses释放引用。如果还卡把仿真器重启AirSim 目前没有热重载机制长时间跑建议分段测试。6. 从仿真到真机的迁移技巧参数对标与域随机化仿真里跑通的算法上真机翻车十有八九是传感器噪声特性和动力学响应不一致。我一般做两件事参数对标和域随机化。参数对标是把真机 IMU 数据手册里的噪声密度、零偏稳定性填进 AirSim 的 settings.json。比如真机陀螺仪零偏稳定性是 10 度每小时换算成弧度每秒大约是 0.00005就把GyroBiasStability设成对应量级。加速度计同理。这一步做完仿真里的姿态解算表现会和真机接近很多。域随机化是在仿真里随机扰动参数让算法见过足够多的变化。下面这段代码在每次重置仿真时随机调整风场和 IMU 噪声import airsim import random client airsim.MultirotorClient() client.confirmConnection() for episode in range(20): # 随机风速 0 到 8 米每秒 wind_speed random.uniform(0, 8) # 随机风向 wind_dir [random.uniform(-1, 1) for _ in range(3)] # 通过 AirSim API 动态设置风场需要 AirSim 版本支持 client.simSetWind(airsim.Vector3r( wind_speed * wind_dir[0], wind_speed * wind_dir[1], wind_speed * wind_dir[2] )) # 随机 IMU 噪声通过重新加载 settings 或使用 API # 这里简化处理实际需要修改 settings.json 后重启仿真 client.reset() client.enableApiControl(True) client.armDisarm(True) client.takeoffAsync().join() # 跑你的自主飞行算法 # ... client.landAsync().join() client.armDisarm(False) client.enableApiControl(False)simSetWind是 AirSim 提供的动态风场接口不用改 settings.json 就能在运行时调整。每轮 episode 随机风速和风向让路径规划算法适应不同风扰。IMU 噪声的动态调整目前 AirSim 没有直接 API常见做法是准备多份 settings.json每轮替换后重启仿真器。20 轮下来如果算法在 80% 的 episode 里都能完成任务上真机的把握就大很多。还有一个血泪经验仿真里摄像头没有运动模糊真机高速飞行时图像糊成一片视觉算法直接失效。如果做视觉避障在 AirSim 里把摄像头曝光时间调长或者后期加运动模糊滤镜提前暴露这个问题。另外真机电池电压下降会导致动力衰减仿真里动力恒定做长航时任务时要在代码里加电压补偿逻辑或者至少在仿真里模拟动力衰减曲线。我自己的习惯是每次改完算法先在 AirSim 里跑 50 次随机场景记录失败案例分类是感知问题还是控制问题。感知问题回去调摄像头参数和噪声模型控制问题回去调增益和限幅。这个流程走顺了真机测试基本就是验证仿真结论而不是从头排查。希望帮到你。本文还有配套的精品资源点击获取