用Python+PyTorch实现DQN无人机避障:从环境搭建到实战训练(附完整代码)

用Python+PyTorch实现DQN无人机避障:从环境搭建到实战训练(附完整代码)

你是否曾想过,让一架无人机像拥有“直觉”一样,在复杂的室内或模拟环境中自主穿梭,灵巧地避开所有障碍物?这听起来像是科幻电影里的场景,但今天,借助深度强化学习的力量,我们完全可以在自己的电脑上构建并训练出这样一个智能体。本文将带你从零开始,手把手实现一个基于深度Q网络(DQN)的无人机避障系统。我们将使用Python和PyTorch,从最基础的环境模拟器搭建,到DQN算法的核心实现,再到模型训练与调优,最终见证一个“笨拙”的智能体如何通过反复试错,成长为一位敏捷的“飞行高手”。无论你是对强化学习充满好奇的开发者,还是希望将AI应用于机器人控制领域的爱好者,这篇详尽的实战指南都将为你提供一条清晰的路径。

1. 项目概述与环境设计

在开始敲代码之前,我们必须清晰地定义我们要解决的问题。我们的目标是训练一个无人机智能体,在一个二维或简化的三维栅格环境中,从起点出发,避开随机分布的障碍物,最终安全抵达目标点。为了简化问题、聚焦于算法核心,我们通常会先从一个二维平面环境开始。

为什么选择DQN? 深度Q网络是深度强化学习的里程碑式算法,它成功地将深度神经网络与Q-Learning结合,解决了传统强化学习在高维状态空间(如图像)下的“维度灾难”问题。对于无人机避障,环境状态(如传感器数据、周围障碍物信息)往往是高维且连续的,DQN正是处理这类问题的利器。

我们的项目将分为几个核心模块:

  1. 环境模拟器 (Environment):定义状态、动作、奖励和状态转移规则。
  2. 智能体 (Agent):包含DQN神经网络和算法逻辑,负责根据状态选择动作。
  3. 经验回放缓冲区 (Replay Buffer):存储智能体的交互经验,用于打破数据间的相关性,提高学习稳定性。
  4. 训练循环 (Training Loop):组织上述模块,进行迭代学习。

首先,我们来构建一个简单但足够有挑战性的环境。我们将使用 gymnasium(原OpenAI Gym的维护分支)作为环境接口标准,这有利于我们未来迁移到更复杂的环境。

import gymnasium as gym
from gymnasium import spaces
import numpy as np
import pygame
import random

class DroneObstacleEnv(gym.Env):
    """
    自定义无人机避障环境。
    环境是一个二维网格世界,无人机需要从起点(S)移动到目标(G),避开障碍物(O)。
    """
    metadata = {'render_modes': ['human', 'rgb_array'], 'render_fps': 4}

    def __init__(self, grid_size=10, render_mode=None):
        super().__init__()
        self.grid_size = grid_size
        self.render_mode = render_mode
        self.window_size = 512  # 渲染窗口的像素尺寸

        # 动作空间: 0:上, 1:下, 2:左, 3:右
        self.action_space = spaces.Discrete(4)

        # 状态空间: 无人机当前位置坐标 (x, y)
        # 为了提供更多信息,我们也可以将目标位置和最近障碍物的相对位置作为状态的一部分。
        # 这里我们先使用一个简化的状态:仅包含自身位置。
        # 状态将被扁平化为一个长度为2的向量。
        self.observation_space = spaces.Box(low=0, high=grid_size-1, shape=(2,), dtype=np.int32)

        # 初始化起点、目标、障碍物和无人机位置
        self._agent_location = None
        self._target_location = None
        self._obstacles = []
        self._generate_obstacles(num_obstacles=5)

        # Pygame渲染相关
        self.window = None
        self.clock = None

    def _generate_obstacles(self, num_obstacles):
        """在网格中随机生成不重叠的障碍物,并避开起点和目标。"""
        self._obstacles = []
        possible_positions = [(x, y) for x in range(self.grid_size) for y in range(self.grid_size)]
        # 暂时移除起点和目标(稍后设置)
        for _ in range(num_obstacles):
            if not possible_positions:
                break
            pos = random.choice(possible_positions)
            self._obstacles.append(pos)
            possible_positions.remove(pos)

    def _get_obs(self):
        """返回当前观察值(状态)。"""
        return np.array(self._agent_location, dtype=np.int32)

    def _get_info(self):
        """返回辅助信息(用于调试,不用于训练)。"""
        return {
            "distance_to_target": np.linalg.norm(np.array(self._agent_location) - np.array(self._target_location)),
            "is_collision": self._is_collision(self._agent_location)
        }

    def _is_collision(self, pos):
        """检查给定位置是否与障碍物或边界碰撞。"""
        x, y = pos
        # 检查边界
        if x < 0 or x >= self.grid_size or y < 0 or y >= self.grid_size:
            return True
        # 检查障碍物
        if tuple(pos) in self._obstacles:
            return True
        return False

    def reset(self, seed=None, options=None):
        super().reset(seed=seed)
        # 随机设置起点和目标,确保它们不在障碍物上且不重合
        possible_positions = [(x, y) for x in range(self.grid_size) for y in range(self.grid_size)]
        for obs in self._obstacles:
            possible_positions.remove(obs)

        self._agent_location = random.choice(possible_positions)
        possible_positions.remove(self._agent_location)
        self._target_location = random.choice(possible_positions)

        observation = self._get_obs()
        info = self._get_info()

        if self.render_mode == "human":
            self._render_frame()

        return observation, info

    def step(self, action):
        # 动作映射:0:上(y-1), 1:下(y+1), 2:左(x-1), 3:右(x+1)
        direction_map = [(0, -1), (0, 1), (-1, 0), (1, 0)]
        dx, dy = direction_map[action]
        new_x = self._agent_location[0] + dx
        new_y = self._agent_location[1] + dy
        new_location = (new_x, new_y)

        # 默认设置
        terminated = False
        truncated = False
        reward = -0.1  # 每一步的小惩罚,鼓励智能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值