Project AirSim简介(3):简单的无人机巡检算法
本文介绍了ProjectAirSim无人机仿真平台的关键API接口和基于规则的巡检算法实现。
一、Project AirSim关键Api接口
1.1 无人机碰撞检测
1.1.1 如何订阅碰撞信息
在运行hello_drone.py时,发现其日志输出有一下内容:
The following topics can be subscribed to for robot "Drone1":
...
robot_info["collision_info"]
...
由此可知,我们可以通过订阅robot_info["collision_info"]这个主题来获取无人机的碰撞信息。具体实现方式如下:
首先定义一个State类,用于管理无人机的状态:
class State:
def __init__(self):
self.collision = False
def set_collision(self, value):
self.collision = value
接着,通过client.subscribe()方法订阅碰撞信息主题,并指定一个回调函数:
client.subscribe(
drone.robot_info["collision_info"],
lambda topic, msg: state.collision_callback(True),
)
这种方式类似于 ROS 系统中的消息订阅机制。当无人机发生碰撞时,会触发该主题的回调函数。这里使用了一个匿名 lambda 函数,在接收到碰撞信息时将State类中的collision成员变量设置为True。这样,在后续循环执行的飞行任务中,我们就可以方便地判断无人机是否发生了碰撞。
1.1.2 如何忽略无人机初始化时的地面碰撞
根据 Project AirSim 文档说明,Actor 设置中的start-landed是一个可选的布尔参数,用于指定无人机是否以手动设定的着陆状态开始。当设置为true时,无人机会直接在生成原点启动,无需降落到带有碰撞网格的物体表面,从而避免初始阶段的碰撞检测。如果省略该参数,其默认值为false,即无人机将以非着陆状态开始运行。
例如,在初始化世界时执行以下代码:
world = World(client, "scene_drone_classic.jsonc", delay_after_load_sec=2)
只需在scene_drone_classic.jsonc配置文件中添加"start-landed" = true,即可实现忽略初始碰撞的功能。
1.2 异步任务的执行与调度:async和await
详细内容见链接:一文读懂Python async/await异步编程
await 是 Asyncio 并发编程的核心机制,它代表一个协作式任务切换点。当协程执行到 await 表达式时,它会挂起当前任务的执行,主动将控制权交还给事件循环,从而让其他就绪的任务有机会运行。这本质上是一种协作式多任务,而非抢占式。
这种机制意味着:只有在遇到 await 时任务才会暂停和切换。如果一个协程内部没有包含任何 await 语句,或者包含的是同步阻塞调用(如 time.sleep()),它将会独占事件循环,导致整个程序的其他异步任务都被阻塞,无法并发执行。
同时,任务调度不是随机的,而是确定且有序的。事件循环维护着一个就绪任务队列,通常按照任务就绪的先后顺序(FIFO,先进先出)来调度。在链接的代码中,三个任务的创建和启动顺序是固定的,因此它们的执行顺序也是确定的。
二、基于规则的无人机巡检算法
在无人机自主巡检系统中,路径规划与实时避障是两个核心模块。本文将介绍一种基于规则的巡检算法,实现无人机在预设交叉口网络中自主飞行,并在飞行中实时避开障碍物。
2.1系统架构概述
我们假设巡检区域由多个交叉口节点组成,无人机初始位置为其中一个交叉口。系统会随机选择一个相邻交叉口作为目标点,无人机朝目标飞行。到达目标后,再随机选择下一个相邻节点,循环执行,实现持续巡检。
在飞行过程中,无人机通过机载深度摄像头获取前方环境信息,判断是否存在障碍物。如无障碍,则继续朝目标点飞行;如检测到障碍,则根据深度图信息选择最安全的方向绕行。
2.2 实时避障:基于深度图的规则决策
避障是无人机自主飞行的关键能力。我们使用前向深度摄像头获取 DEPTH_PLANAR 类型的图像,并对其进行分区域分析,判断前方是否存在障碍物。
我们将深度图像在垂直方向截取中间三分之一区域(避免天花板和地面干扰),并水平划分为三个带状区域,分别计算每个区域的最小深度值,以此判断前方障碍物的分布情况。
num_bands = 3
height, width = depth_image.shape
middle_band = depth_image[middle_start:middle_end, :]
band_depths = [np.min(band) for band in bands]
如果中间区域的最小深度小于设定的阈值,则认为前方有障碍物。此时,系统会选择深度值最大的区域作为安全方向,并计算出一个偏航角度,使无人机朝该方向绕行。
if front_min < self.change_direction_distance_threshold:
safe_index = int(np.argmax(band_depths))
angle_offset = (safe_index - center_index) * angle_per_band
abs_yaw = yaw + angle_offset
v_north = MOVE_VELOCITY * math.cos(abs_yaw)
v_east = MOVE_VELOCITY * math.sin(abs_yaw)
如未检测到障碍,则无人机根据当前位置与目标点的坐标差,计算朝向目标的速度向量:
dx = to_x - cur_x
dy = to_y - cur_y
distance = math.hypot(dx, dy)
unit_dx = dx / (distance + 1e-6)
unit_dy = dy / (distance + 1e-6)
v_north = unit_dx * MOVE_VELOCITY
v_east = unit_dy * MOVE_VELOCITY
为避免速度突变,我们对控制指令进行平滑处理:
v_north = self.prev_v_north * (1 - self.smoothing_factor) + v_north * self.smoothing_factor
v_east = self.prev_v_east * (1 - self.smoothing_factor) + v_east * self.smoothing_factor
然后,我们使用 move_by_velocity_z_async 方法控制无人机以指定速度飞行,并保持高度稳定。该方法是异步执行的,确保系统能实时响应传感器数据并更新控制指令。
task = self.drone.move_by_velocity_z_async(v_north, v_east, z, 1,
yaw_control_mode=YawControlMode.ForwardOnly,
yaw_is_rate=False, yaw=0)
await task
三、相关连接
为了方便使用,我在 Project AirSim 基础上略作修改:https://github.com/QinCheng0928/ProjectAirSim_UAV_Inspection_Baseline.git
本文仅为个人学习与理解笔记,水平有限,如有错误或理解偏差,欢迎评论区指正,轻喷!
如果大家有更好的实现思路或改进建议,也非常欢迎联系我一起讨论!
希望这篇文章能帮到正在研究 Project AirSim 的同学!
更多推荐



所有评论(0)