你是否好奇,在不预先编程每一个动作的情况下,如何教会机器人让无人机着陆?这正是我在探索的问题。我花了数周时间开发了一款游戏:在游戏中,一架虚拟无人机需自主学会降落到平台上 —— 它无需遵循预先编写的指令,而是像人类学习骑自行车那样,通过反复试错来掌握技巧。
这就是强化学习(Reinforcement Learning,简称 RL),它与其他机器学习方法有着本质区别。你无需向人工智能展示数千个 “正确着陆” 的示例,只需向其提供反馈即可:比如 “这次做得不错,但下次或许可以更平缓一些”,或是 “糟糕,你坠毁了 —— 下次最好别这么做”。经过无数次尝试,人工智能会逐渐摸索出有效与无效的行为模式。
在本文中,我将记录自己从强化学习基础入门,到搭建一套可运行的系统(该系统大体上能教会无人机着陆)的全过程。你将看到过程中的成功与失败,以及我为调试各种异常行为所付出的努力。
一. 强化学习:概述
强化学习的核心思想,很大程度上可以关联到巴甫洛夫(Ivan Pavlov)的狗实验和斯金纳(B. F. Skinner)的老鼠实验。其原理是:当实验对象做出你期望的行为时,给予它 “奖励”(正强化);当它做出不良行为时,给予 “惩罚”(负强化)。经过多次重复尝试,实验对象会从这种反馈中学习,逐渐发现哪些行为能导向成功 —— 这与斯金纳实验中老鼠学会按压哪个杠杆能获得食物奖励的过程类似。

图 1. 巴甫洛夫的经典条件反射实验(由谷歌 Gemini 人工智能生成的图像)
同理,我们希望构建一个系统,让它通过学习完成特定任务,从而实现 “最大化奖励、最小化惩罚” 的目标。请记住 “奖励最大化” 这一关键原则,后续内容会多次涉及。
1.1核心概念
当我们需要在计算机上通过编程实现强化学习系统时,最好先对其中涉及的抽象概念进行清晰而具体的定义。在人工智能(更具体地说是强化学习)领域,核心概念可归纳为以下几类:
- 智能体(Agent,或行动者 Actor):对应前文提到的 “实验对象”,可以是巴甫洛夫实验中的狗、在大型工厂中尝试自主导航的机器人、电子游戏中的非玩家角色(NPC)等。
- 环境(Environment,或外部世界):可以是真实场景、带有约束条件的模拟环境、游戏中的虚拟世界等。我倾向于这样理解:“环境就像一个盒子(无论真实还是虚拟),智能体的所有活动都局限在这个盒子内;它只知晓盒子内发生的事情。而我们作为‘掌控者’,可以改变这个盒子的状态,但在智能体看来,这就像是‘上天’对其所处世界施加的影响。”
- 策略(Policy):与政府、企业等组织中的 “政策” 类似,强化学习中的 “策略” 规定了 “在特定情境下应采取何种行动”。
- 状态(State):指智能体对当前自身所处情境的 “感知” 或 “认知”。可以将其理解为智能体在某一时刻对现实的 “快照”—— 例如,你开车时看到的交通灯颜色、车辆当前速度、与十字路口的距离等信息,共同构成了你所处的 “状态”。
- 动作(Action):既然智能体能够 “感知” 环境状态,它就可能会针对当前状态采取行动。比如,智能体刚从一夜睡眠中醒来,想要喝杯咖啡,那么它首先会做的动作就是 “起床”。这一动作的目的,就是帮助智能体实现 “喝到咖啡” 的目标。
- 奖励(Reward):每当智能体(自主地)执行一个动作,环境状态就可能发生变化。例如,智能体起床后走向厨房,却因走路不稳绊倒了 —— 此时,作为 “掌控者” 的我们会给予它惩罚(负奖励),因为它走路的方式不对;但当智能体成功走到厨房并拿到咖啡时,我们会给予奖励(正奖励),比如一块饼干。

图2. 强化学习系统的理论示意图
不难想象,要让智能体完成特定任务或解决特定问题,上述大部分核心组件都需要根据具体需求进行定制。
二. 训练环境(Gym)
理解了强化学习的基础概念后,你可能会问:我们该如何实际搭建这样一个系统?接下来,我将向你展示我开发的这款游戏。
在本文中,我编写了一款定制化电子游戏,任何人都可以获取并使用它来训练自己的机器学习智能体,让智能体学会 “玩” 这款游戏。
完整的代码仓库可在 GitHub 上获取(如果觉得有用,欢迎点亮星标)。我计划在后续的强化学习系列文章中,继续使用这个代码库陆续更新更多游戏、仿真代码和一些进阶的强化学习玩法。
配送无人机(Delivery Drone)
“配送无人机” 游戏的目标是操控一架无人机(机上可能装载着货物)降落到平台上。要 “赢得” 这款游戏,需满足以下着陆条件:
- 无人机处于平台的着陆范围内
- 无人机速度足够慢
- 无人机保持机体直立状态(倒置着陆更接近于坠毁,而非成功降落)

图3.本项目中我所制作游戏的截图
若无人机飞出屏幕范围或接触地面,将被判定为 “坠毁”,即任务失败。
状态描述
无人机可观测到 15 个连续数值,这些数值共同完整地描述了它当前的状态:

着陆成功条件
无人机必须同时满足以下所有条件,才算着陆成功:
- 水平对齐:处于平台边界内(|dx| < 0.0625)
- 安全接近速度:速度小于 0.3
- 姿态平衡:倾斜角度小于 20°(|angle| < 0.111)
- 正确高度:无人机底部接触平台顶部
这就像侧方停车 —— 你需要调整到正确的位置、保持正确的角度,同时控制速度足够慢,才能不“刮蹭”地完美入位!
如何设计策略?
设计策略的方法有很多种:可以是贝叶斯策略(基于信念维持概率分布)、针对离散状态下的查找表、人工编写的规则系统(例如 “若距离 < 10,则减速”)、决策树,也可以是我们接下来要探讨的“神经网络”——通过梯度下降学习 “状态到动作” 映射关系。
本质上,我们需要一个 “处理器”:输入前文所述的 “状态”,对其进行一定的计算处理,最终输出应执行的 “动作”。
利用深度学习构建策略?
那么,如何设计一种策略,它既能处理连续状态(如无人机的精确位置),又能学习复杂行为呢?这就需要用到神经网络了。
在神经网络(或深度学习)领域,通常最好基于 “动作概率” 的形式来进行决策,即 “在当前状态下,哪种动作最有可能是最佳选择?”。因此,我们可以定义一个神经网络:将 “状态” 以 “向量” 或 “向量集合” 的形式作为输入(这些向量需从观测到的状态中构建)。
在我们的 “配送无人机” 游戏中,状态向量(state vector)包含如下信息:
无人机可观测自身的绝对位置、速度、姿态、燃料量、平台位置,以及衍生出的度量指标,如下图所示。


其中,各个分量的定义如下:
- x, y:无人机在游戏世界中的当前位置
- vₓ, vᵧ:水平与垂直方向速度分量
- θ:倾斜角度(θ = 0 表示垂直向上,必须足够小才能安全降落)
- ω:角速度(表示无人机旋转的快慢)
- f ∈ [0, 1]:燃料比例(1 = 满油,0 = 空油)
- pₓ, pᵧ:平台的位置(降落目标点)
- d:无人机与平台中心的欧几里得距离
- Δx, Δy:无人机相对于平台的相对位置(为负表示平台在该方向上)
- v:总速度(用于检测降落时的安全速度)
- landed, crashed:布尔标志,用于表示终止状态(已降落或已坠毁)
为确保神经网络训练的稳定性,所有分量的数值均被归一化到大致 [0,1] 或 [-1,1] 的范围。
动作空间(由三个独立的二进制推进器组成)
与将动作组合离散化的做法不同,这里我们将每个推进器视为独立的控制单元:
- 主推进器(Main thruster):提供向上的推力
- 左推进器(Left thruster):顺时针旋转
- 右推进器(逆时针旋转):逆时针旋转
每个动作均从伯努利分布(Bernoulli distribution)中采样,因此在每个时间步(timestep),智能体需独立做出 3 个相互独立的二进制决策(即每个推进器 “启动” 或 “不启动”)。
神经网络策略(基于伯努利采样的概率型策略)
设 fₜₕₑₜₐ(s) 为神经网络经过 Sigmoid 激活函数后的输出。该策略采用独立的伯努利分布(Bernoulli distributions)来表示各个动作的概率::

简化的 Python 代码示例(摘自本项目)
# build state vector from DroneState
s = np.array([
state.drone_x, state.drone_y,
state.drone_vx, state.drone_vy,
state.drone_angle, state.drone_angular_vel,
state.drone_fuel,
state.platform_x, state.platform_y,
state.distance_to_platform,
state.dx_to_platform, state.dy_to_platform,
state.speed,
float(state.landed), float(state.crashed)
])
# network outputs probabilities for each thruster (after sigmoid)
action_probs = policy(torch.tensor(s, dtype=torch.float32)) # shape: (3,)
# sample each thruster independently from Bernoulli
dist = Bernoulli(probs=action_probs)
action = dist.sample() # shape: (3,), e.g., [1, 0, 1] means main+right thrusters
上述代码展示了我们如何将游戏中的物理观测数据转换为 15 维的归一化状态向量,并为每个推进器生成独立的二进制决策。
代码设置(第一部分):导入库与游戏 socket 配置
首先,我们需要启动游戏的 socket 监听器。为此,你可以导航到我打代码库中的 “delivery_drone” 目录,并执行以下命令:
pip install -r requirements.txt # run this once for setting up the required modules
python socket_server.py --render human --port 5555 --num-games 1 # run this whenever you need to run the game in socket mode
注意:运行该代码需要 PyTorch 环境,请确保已提前完成 PyTorch 的安装与配置
import os
import torch
import torch.nn as nn
import math
import numpy as np
from torch.distributions import Bernoulli
# Import the game's socket client
from delivery_drone.game.socket_client import DroneGameClient, DroneState
# setup the client and connect to the server
client = DroneGameClient()
client.connect()
如何设计奖励函数?
什么才是 “好的奖励函数”?这可以说是强化学习中最困难的部分(也是我花费了大量时间调试的地方 🫠)。
奖励函数是任何强化学习实现的 “灵魂”(相信我,若奖励函数设计不当,智能体可能会做出各种“离谱”的行为)。从理论上讲,奖励函数应明确
- “哪些行为是需要学习的‘好行为’”
- “哪些行为是应该避免的‘坏行为’”
智能体执行的每个动作,都会根据该动作所表现出的行为特征,累积相应的奖励值。例如,若希望无人机平缓着陆,你可能会为 “靠近平台” 和 “低速飞行” 给予正奖励,而为 “坠毁” 或 “燃料耗尽” 等情况给予惩罚 —— 这样,智能体就会通过学习,不断调整策略,以实现长期累积的奖励总和的最大化。
优势函数(Advantage):一种更优的奖励衡量方式
在训练策略时,我们不仅想知道某个动作是否带来了奖励,更想知道这个动作是否 “优于常规水平”。这正是 “优势函数” 的设计初衷。
优势函数要回答的问题是:“这个动作比我们通常预期的效果更好,还是更差?”
![]()
在我们的实现中,优势函数的计算步骤如下:
- 收集多个训练回合(episode)的数据,并计算每个回合的回报值(即折扣奖励总和)
- 取所有回合的平均回报作为 “基准值”(baseline)
- 计算每个时间步的优势值:优势值 = 该时间步的回报 – 基准值(Advantage=Return−Baseline)
- 对优势值进行归一化处理(使其均值为 0、标准差为 1),以确保训练稳定性
这种方法的优势在于:
- 优势值为正 → 该动作优于平均水平 → 提高该动作的执行概率
- 优势值为负 → 该动作低于平均水平 → 降低该动作的执行概率
- 通过上述方式,可以减少梯度更新的方差,使学习过程更稳定
这个简单的基准值设定,已经能让训练效果远优于直接使用原始回报!它会将整个动作序列与最终结果(坠毁或着陆成功)进行加权比较,从而让策略学习到能带来更优优势值的动作。
经过大量的试验和调参,我设计了如下奖励函数。其中的核心思路是:奖励需同时依赖 “距离” 和 “垂直位置”—— 只有当无人机处于平台上方时,才能获得正奖励,以此避免智能体出现 “在平台下方悬停以骗取奖励” 的投机行为。

关于奖励的反向与非线性缩放
通常,我们希望奖励能与某些状态值呈反比关系。例如,无人机与平台的距离范围(经窗口宽度归一化后)为 0 到约 1.41,我们希望当距离≈0 时给予高奖励,当距离较远时给予低奖励。为此,我使用了多种缩放函数,例如:

图 4 高斯标量函数示意图
辅助函数:
def inverse_quadratic(x, decay=20, scaler=10, shifter=0):
"""Reward decreases quadratically with distance"""
return scaler / (1 + decay * (x - shifter)**2)
def scaled_shifted_negative_sigmoid(x, scaler=10, shift=0, steepness=10):
"""Sigmoid function scaled and shifted"""
return scaler / (1 + np.exp(steepness * (x - shift)))
def calc_velocity_alignment(state: DroneState):
"""
Calculate how well the drone's velocity is aligned with optimal direction to platform.
Returns cosine similarity: 1.0 = perfect alignment, -1.0 = opposite direction
"""
# Optimal direction: from drone to platform
optimal_dx = state.dx_to_platform
optimal_dy = state.dy_to_platform
optimal_norm = math.sqrt(optimal_dx**2 + optimal_dy**2)
if optimal_norm < 1e-6: # Already at platform
return 1.0
optimal_dx /= optimal_norm
optimal_dy /= optimal_norm
# Current velocity direction
velocity_norm = state.speed
if velocity_norm < 1e-6: # Not moving
return 0.0
velocity_dx = state.drone_vx / velocity_norm
velocity_dy = state.drone_vy / velocity_norm
# Cosine similarity
return velocity_dx * optimal_dx + velocity_dy * optimal_dy
当前奖励函数的代码
def calc_reward(state: DroneState):
rewards = {}
total_reward = 0
# 1. Time penalty - distance-based (penalize more when far)
minimum_time_penalty = 0.3
maximum_time_penalty = 1.0
rewards['time_penalty'] = -inverse_quadratic(
state.distance_to_platform,
decay=50,
scaler=maximum_time_penalty - minimum_time_penalty
) - minimum_time_penalty
total_reward += rewards['time_penalty']
# 2. Distance & velocity alignment - ONLY when above platform
velocity_alignment = calc_velocity_alignment(state)
dist = state.distance_to_platform
rewards['distance'] = 0
rewards['velocity_alignment'] = 0
# Key condition: drone must be above platform (dy > 0) to get positive rewards
if dist > 0.065 and state.dy_to_platform > 0:
# Reward movement toward platform when velocity is aligned
if velocity_alignment > 0:
rewards['distance'] = state.speed * scaled_shifted_negative_sigmoid(dist, scaler=4.5)
rewards['velocity_alignment'] = 0.5
total_reward += rewards['distance']
total_reward += rewards['velocity_alignment']
# 3. Angle penalty - distance-based threshold
abs_angle = abs(state.drone_angle)
max_angle = 0.20
max_permissible_angle = ((max_angle - 0.111) * dist) + 0.111
excess = abs_angle - max_permissible_angle
rewards['angle'] = -max(excess, 0)
total_reward += rewards['angle']
# 4. Speed penalty - penalize excessive speed
rewards['speed'] = 0
speed = state.speed
max_speed = 0.4
if dist < 1: rewards['speed'] = -2 * max(speed - 0.1, 0) else: rewards['speed'] = -1 * max(speed - max_speed, 0) total_reward += rewards['speed'] # 5. Vertical position penalty - penalize being below platform rewards['vertical_position'] = 0 if state.dy_to_platform > 0: # Drone is above platform (GOOD)
rewards['vertical_position'] = 0
else: # Drone is below platform (BAD!)
rewards['vertical_position'] = state.dy_to_platform * 4.0 # Negative penalty
total_reward += rewards['vertical_position']
# 6. Terminal rewards
rewards['terminal'] = 0
if state.landed:
rewards['terminal'] = 500.0 + state.drone_fuel * 100.0
elif state.crashed:
rewards['terminal'] = -200.0
# Extra penalty for crashing far from target
if state.distance_to_platform > 0.3:
rewards['terminal'] -= 100.0
total_reward += rewards['terminal']
rewards['total'] = total_reward
return rewards
没错,像 4.5、0.0625、4.0 这样的 “魔法数字”,都是在无数次试错中调试出来的。欢迎来到强化学习的世界 —— 在这里,超参数调优一半是艺术,一半是科学,还有一半是运气。(是的,我知道这已经是“三个一半”了 😅)
def compute_returns(rewards, gamma=0.99):
"""
Compute discounted returns (G_t) for each timestep based on the Bellman equation
G_t = r_t + γ*r_{t+1} + γ²*r_{t+2} + ...
"""
returns = []
G = 0
# Compute backwards (more efficient)
for r in reversed(rewards):
G = r + gamma * G
returns.insert(0, G)
return returns
需要重点注意的是,奖励函数的设计离不开细致的试错。哪怕只是一个设计失误,或是对某个行为奖励过度,智能体都会偏离预期目标,转而优化那些利用奖励漏洞的行为 —— 这就会导致 “奖励作弊”(Reward Hacking)。
奖励作弊(Reward Hacking)
当智能体找到一种 “非预期的方式” 来最大化奖励,却没有真正完成你希望它解决的任务时,就发生了奖励作弊。智能体并非 “故意作弊”—— 它只是严格执行了你设定的规则,只是没按你 “期望的方式” 执行。
经典案例:如果你给清洁机器人设定的奖励规则是 “无可见灰尘”,它可能会学会关掉自己的摄像头(这样就 “看不到” 灰尘了),而不是真的去打扫卫生!
我的惨痛教训:我亲身体会过这种坑。在无人机着陆奖励函数的早期版本里,我设定 “只要无人机在平台附近保持稳定且低速,就能获得奖励”。听起来很合理,对吧?结果——大错特错!仅仅训练了 50 个回合,无人机就学会了在原地无限悬停,以此骗取奖励。、从 “糟糕的奖励函数” 角度看,它的行为是 “最优” 的 —— 但要说 “着陆”?完全没做到!我眼睁睁看着它悬停了 5 分钟,才反应过来问题出在哪。
以下是我当时写的有问题的代码:
# DO NOT COPY THIS!
# If drone is above the platform (|dx| < 0.0625) and close (distance < 0.25):
corridor_reward = inverse_quadratic(distance, decay=20, scaler=15) # Up to 15 points
if stable and slow:
corridor_reward += 10 # Extra 10 points!
# Total possible: 25 points per step!
奖励作弊的实际示例:

图5 无人机学会了在平台附近悬停刷分

图6 显示无人机明显出现“奖励作弊”行为的曲线图
总结
上文系统阐述了强化学习的核心框架,包括状态定义、动作空间设计、奖励函数构建及其潜在陷阱。我们明确了策略网络作为”智能体大脑”的关键角色,并完成了从环境感知到动作概率映射的理论铺垫。
接下来,我们将深入策略网络的具体实现:解析网络架构设计、激活函数选择与训练流程。重点探讨如何通过策略梯度方法更新网络参数,并分析训练过程中的稳定性挑战与应对策略,最终实现无人机的自主着陆。
原文作者:Vedant Jumle
原文链接:https://towardsdatascience.com/deep-reinforcement-learning-for-dummies/