什么是自主代理?
在人工智能和计算机图形学中,Autonomous Agent 是一个能够自行决定如何在环境中行动的实体,不受领导者或全局计划的影响。有三个关键组成部分:
- 有限感知:能感知其环境和自身内部状态的能力,例如昆虫能通过视觉和嗅觉了解周围世界
- 信息处理与决策:将感知转化为动作。例如鱼感受到捕食者逼近,就会施加一个相反方向的逃逸力
- 自治性:自主代理没有领导者,是去中心化的个体行为(取决于具体情况)
人工模拟的蚂蚁和白蚁群落是自主代理系统的绝妙示范,推荐阅读 Mitchel Resnick 的《海龟、白蚁与交通堵塞》(Turtles, Termites, and Traffic Jams, Bradford Books, 1997)
Vehicles and Steering 车辆和转向行为
在 1980 年代末,计算机科学家 Craig Reynolds 开发了用于动画角色的 Steering 算法行为。这些行为使个体元素能够以逼真的方式在其数字环境中导航,具有逃跑、徘徊、到达、追逐、躲避等策略。后来,在他 1999 年的论文 Steering Behaviors for Autonomous Characters 中,Reynolds 使用了 vehicle 一词来描述他的自主代理。
因此,我们将自主代理类命名为
Vehicle
Reynolds 描述了理想化的车辆运动,分为三个层次 (layer):
- Action selection 行动选择:决定目标是什么,一个车辆有一个目标(或多个目标),并可以基于该目标选择一个动作(或一组合动作)
- Steering 转向:将目标转化为一个「转向力」,本质上是速度差,转向力 = 期望速度 – 当前速度
- Locomotion 运动执行:将转向力转化为运动
Seek - 追寻目标
考虑以下场景:一辆车正在寻找一个目标

我想要车辆根据其对自身状态(其速度和当前移动方向)以及环境(目标的位置)的感知做出智能决策,朝目标方向转向
可以将期望的速度设置为车辆当前位置到目标位置的向量 p5.Vector.sub(target, position) ,向量的大小设置为最大车速,为此要给车辆加一个 maxspeed 属性
$$ \text{steering force} = \text{desired velocity} - \text{current velocity} $$

转向力等于期望速度减去当前速度
还需要考虑车辆的操控性,来决定车辆是否能够立刻更改到期望的速度,为此,需要限制转向力的大小,给车辆增加一个 maxforce 属性
把这一切整合在一起,可以写一个叫做 seek() 的方法,该方法接收一个 p5.Vector 目标并计算朝该目标的转向力
seek(target) {
let desired = p5.Vector.sub(target,this.position);
desired.setMag(this.maxspeed);
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce);
this.applyForce(steer);
}Arrive 到达
当车辆非常接近目标时,避免「冲过头」,可以设置期望速度和到目标的距离成正比,比如 desired.mult(0.05)

Reynolds 描述了一种更复杂的方法。想象一下,目标周围有一个给定半径 r 的圆。如果车辆在这个圆内,它会逐渐减速,到目标处速度为 0

let d = desired.mag(); // 指向目标的向量的大小
if (d < 100) {
let m = map(d, 0, 100, 0, this.maxspeed);
desired.setMag(m);
} else {
desired.setMag(this.maxspeed);
}Wander - 游荡
搜索和到达,都可以视为为每个行为计算一个单一的向量:期望速度,Reynolds 提出的每一个引导行为都遵循这个模式
游荡的目标不是随机运动,而是感觉在一个方向上移动一段时间,然后在下一个方向上游荡一小会,下一帧上的方向与前一帧上的方向相关,这产生了比在每个帧生成一个随机操控方向更有趣的运动
首先,车辆预测其未来位置为在其前方的固定距离(朝其当前速度方向)。然后,它在该位置画一个以半径 r 为中心的圆,并在圆周上随机选择一个点。这个点在每一帧动画中随机围绕圆移动,是车辆的目标,因此其所需速度指向该方向。

游荡行为将一个在车辆前方的圆周上的随机点视为目标
它利用随机性来驱动车辆的转向,但通过圆周约束了这种随机性,防止车辆的运动显得抖动或完全随机
Flow-field following 流场跟随
什么是流场呢?将画布视为一个网格,网格中的每个单元格都有一个方向向量。当车辆在画布上移动时,它会问:「嘿,我下面是什么箭头?那就是我想要的速度!」

使用柏林噪声,可以将噪声的值从 01 映射到 02π 的角度(不过柏林噪声是正态分布的,因此会倾向于向左运动)

let xoff = 0;
for (let i = 0; i < this.cols; i++) {
let yoff = 0;
for (let j = 0; j < this.rows; j++) {
let angle = map(noise(xoff, yoff), 0, 1, 0, TWO_PI);
this.field[i][j] = p5.Vector.fromAngle(angle); // 根据角度生成一个单位向量
yoff += 0.1;
}
xoff += 0.1;
}Path Following 路径跟随
Simple Path Following

路径跟随需要一个路径、一个车辆、一个未来位置、一个法线和一个目标
路径是什么?一种简单的方法是将路径定义为一系列连接的点,路径具有半径,也就是道路的宽度。

沿当前方向预测未来位置,只有当车辆偏离路径时才需要应用转向力

找到法线点,在法线点的前方设置目标(目标在路径上),最后计算期望速度
Path Following with Multiple Segments 多段路径跟随
关键在于沿路径找到目标点的方式,也就是要先找到正确的线段,并计算该线段上的法线点

Reynolds 提出的解决方案是选择最近的且在路径上的法线点
Complex Systems 复杂系统
作为一个合乎逻辑的下一步,代理应当不仅能够感知他们的物理环境,还能够感知同伴代理的行为,并相应地采取行动。
复杂系统通常被定义为一个超越其部分之和的系统。虽然系统的个别元素可能非常简单且容易理解,但系统整体的行为可能高度复杂、智能且难以预测。
想象一下一只微小的爬行蚂蚁,蚂蚁是一个自主代理;它可以感知其环境(利用触角收集关于化学信号方向和强度的信息)并根据这些信号做出移动决策。但是,一只独自行动的蚂蚁能建立巢穴、收集食物或保护女王吗?蚂蚁是一个只能感知其直接环境的简单单位。然而,蚁群是一个复杂的系统,是一个协同工作的超有机体,其组成部分共同努力实现困难、复杂的目标。
构建复杂系统的三个核心原则:
- 简单单元之间有短程关系:对环境感知有限
- 简单单元并行操作:每次循环中,每个单元将计算自己的转向力
- 系统整体呈现出涌现现象:复杂的行为、模式和智能可以从简单单元之间的交互中涌现。这种现象在自然界中发生,如蚂蚁群体、迁徙模式、地震和雪花。
复杂系统的另外三个特征将有助于框定讨论,并提供软件模拟的指导方针。这是一组模糊特征,并不是所有复杂系统都有所有特征:
- 非线性:复杂系统的这一方面通常被称为蝴蝶效应,源于数学家和气象学家爱德华·诺顿·洛伦兹,他是混沌理论研究的先驱。之所以称为非线性,是因为初始条件的变化与结果之间没有线性关系。初始条件的小变化可能对结果产生巨大影响。即使在由许多 0 和 1 组成的系统中,只要更改一个比特,结果也会完全不同。非线性系统是混沌系统的超集
- 竞争与合作:组成元素之间同时存在竞争与合作
- 反馈:复杂系统通常包括一个循环,将系统的输出反馈到系统中,以积极或消极的方向影响其行为
Flocking 聚集
Flocking 是一种在许多生物中发现的群体动物行为,例如鸟类、鱼类和昆虫。1986 年,Reynolds 创建了一个聚群行为的计算机模拟,并在他的论文 Flocks, Herds, and Schools: A Distributed Behavioral Model 中记录了该算法
Reynolds 使用术语 boid(一个虚构的词,指的是一种鸟类物体)来描述一个群体系统的元素
Flocking 的三个规则:

- 分离:转向以避免与邻近者碰撞
- 对齐:朝与邻近者相同的方向行驶
- 期望速度设置为邻居的平均速度
- 内聚:朝邻近者的中心方向行驶(与群体保持一致)
这些简单局部规则,在系统层面却能形成自然逼真的群体模式

