一、 背景
我曾经使用遗传算法常用于解决旅行商问题,认为遗传算法解决问题的关键是对问题编码。看到了一篇有趣的技术博文【1】,介绍使用遗传算法(Genetic Algorithm, GA) 训练小车在模拟环境中实现自动泊车。尽管文章中对泊车问题编码的思路非常精彩,但是使用 Three.js 进行算法实现和可视化的,对后端选手不太友好😢。在复现的过程中,参考了博文【1】的算法思路,为了避免碰撞,优化了损失函数。本文使用python实现算法,并使用pygame库进行可视化,源码上传了git仓库(github.com/lcmchn/self...)。
二、 算法设计
首先要理解本算法设计的核心思想:
自动泊车的本质是 "小车根据环境(障碍物)调整动作(行驶、转向),最终精准停靠目标车位"。本文用进化(遗传算法)的方式"训练"小车学会这一过程,而不是手动编写规则。
然后大家在阅读本文前思考几个问题,这是本文解决泊车任务的关键:
👀 感知(看):小车如何获取周围环境信息? 🤔 决策(想):如何根据环境信息生成动作指令? 📖 优化(学):如何让小车通过迭代 "学会" 最优决策逻辑?
为了解决上面三个问题,本文先对小车建模,然后利用遗传算法进化小车的"大脑"。下面分别介绍。
2.1 小车控制建模
为了简化模型,小车的动作被抽象为两个独立维度的信号,每个信号仅 3 种状态。小车每隔一定步长(本文为100ms)接收一次动作信号,然后执行。
2.1.1 发动机信号
| -1 | 0 | +1 |
|---|---|---|
| 倒车 | 静止 | 前进 |
2.1.2 方向盘信号
| -1 | 0 | +1 |
|---|---|---|
| 左转 | 直行 | 右转 |
2.1.3 8方位距离传感器
每辆小车拥有8个距离传感器,检测与障碍物的距离,使小车拥有感知周围环境的能力。每隔一定步长(本文为100ms)传感器输出 8 个浮点数(对应 8 个传感器的距离值),作为大脑决策系统的输入。当监测范围内(4m)无障碍物,传感器数值为0;当传感器数值很小但不为0,表示障碍物就在附近。

2.1.4 大脑决策系统
小车的大脑决策系统实现了由"感知"到"动作"的转换:距离传感器输入, 动作信号输出。本文算法通过一个简单的线性函数+信号转换建模:
- 信号表达:
scss
-->发动机信号
engineSignal = brainToMuscleSignal(
(e0 * sensor0) + (e1 * sensor1) + ... + (e7 * sensor7) + ebias // <- brainFunction
)
-->方向盘信号
wheelSignal = brainToMuscleSignal(
(w0 * sensor0) + (w1 * sensor1) + ... + (w7 * sensor7) + wbias // <- brainFunction
)
sensor[i]:小车周围8方位距离传感器,返回到障碍物的距离。
e[i]、ebias、w[i]、wbias:可学习的权重参数(即"基因")。
- 信号转换函数:brainToMuscleSignal()函数实现"多项式输出→动作信号" 的映射。
scss
-->sigmoid函数
将多项式输出的任意浮点数压缩到 (0,1) 区间(归一化)
-->阈值划分
(0, 0.1)→-1,(0.1, 0.9)→0,(0.9,1)→+1(通过 margin 参数控制阈值范围,-1\0+1就是需要的动作信号!!)

2.1.5 基因
每个个体(一辆小车)的"基因"就是其线性控制函数的所有权重和偏置(e0-e7、ebias、w0-w7、wbias 共 18 个系数):
- 编码规则:系数为浮点数,用 10 位二进制表示(1 位符号位 + 4 位指数位 + 5 位尾数位,平衡精度与长度)。
- 基因组长度:18 个系数 × 10 位 = 180 位(0/1 序列),确保 "遗传操作(交叉、变异)" 可高效执行。

2.2 利用遗传算法进化小车的"大脑"
为了得到优秀的基因(180位0/1 序列),解决自动泊车问题。本算法使用遗传算,模拟自然选择,迭代产生更优个体。下图为算法的关键步骤和流程。

2.2.2 关键步骤
| 遗传算法概念 | 泊车任务实现 |
|---|---|
| 个体(Individual) | 一个小车基因组(180位0/1 序列) |
| 种群(Population) | 一代小车的集合(默认 1000 个个体) |
| 适应度函数(Fitness Function) | 基于 "车轮到车位角的平均距离" 计算:fitness = 1 / (loss + 1),其中 loss 是 4 个车轮到车位 4 个角的平均距离(距离越近,fitness 越高) 🔑 加入碰撞惩罚项:发生碰撞直接将适应度降至1% |
| 选择(Selection) | 加权随机选择:适应度越高的个体,被选中作为父母的概率越大;同时保留 10%"最优个体" 直接进入下一代(避免优质基因丢失) |
| 交叉(Crossover) | 均匀交叉:父母基因组的每一位以 50% 概率遗传给子女(模拟基因重组) |
| 变异(Mutation) | 随机变异:每个基因位以 4% 概率翻转(0→1 或 1→0,引入新基因多样性) |

python
def car_fitness_from_loss(loss: float, has_collision: bool = False, is_loop: bool = False,
is_timeout: bool = False) -> float:
"""
增强版适应度函数:
- loss越小(离车位越近),适应度越高
- 碰撞大幅惩罚适应度
"""
base_fitness = 1.0 / (loss + 1e-6)
# 惩罚项:碰撞/循环/超时直接将适应度降至1%
if has_collision:
base_fitness *= 0.01
# 额外奖励:离车位足够近(<50像素)时翻倍奖励
if loss < 50:
print()
base_fitness *= 2.0
return
2.2.3 算法流程
-
初始化种群(N 辆随机小车)。
-
对每辆车:
- 在仿真环境中运行其控制策略。
- 计算适应度(评价基因好坏)。
-
根据适应度选择、交叉、变异,生成新一代种群。
-
重复步骤 2--3,直到某代出现能成功泊车的小车,或达到最大代数。
三、结果可视化
初始代的小车的"基因"是随机的,完全没有自动泊车的能力:

遗传算法训练50代后,小车掌握了自动泊车的能力,基本能停靠在目标车位附近:

平均适应度随着迭代呈上升趋势,说明算法正在有效筛选优质基因,种群整体的泊车能力在持续提升。

四、总结与反思
虽然本文的算法不适合直接用于真实自动驾驶(训练的小车一路上有时会撞到别的车,而且停车位也停得不准确),但为理解机器学习、优化算法和机器人控制提供了起点。下面是针对本文算法的几点反思:
- 当前使用线性控制器过于简单,难以泛化到不同车位布局或初始位置。
- 若想提升性能,应改用多层感知机(MLP) 作为控制器(即神经网络)。
- 当前策略倾向于"一直倒车找空位",缺乏复杂机动(如前进+后退配合)。