自主代理:从个体行为到复杂群体系统

什么是自主代理?

在人工智能和计算机图形学中,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):

Seek - 追寻目标

考虑以下场景:一辆车正在寻找一个目标

我想要车辆根据其对自身状态(其速度和当前移动方向)以及环境(目标的位置)的感知做出智能决策,朝目标方向转向

可以将期望的速度设置为车辆当前位置到目标位置的向量 p5.Vector.sub(target, position) ,向量的大小设置为最大车速,为此要给车辆加一个 maxspeed 属性

$$ \text{steering force} = \text{desired velocity} - \text{current velocity} $$

转向力等于期望速度减去当前速度

还需要考虑车辆的操控性,来决定车辆是否能够立刻更改到期望的速度,为此,需要限制转向力的大小,给车辆增加一个 maxforce 属性

把这一切整合在一起,可以写一个叫做 seek() 的方法,该方法接收一个 p5.Vector 目标并计算朝该目标的转向力

JAVASCRIPT
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

JAVASCRIPT
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π 的角度(不过柏林噪声是正态分布的,因此会倾向于向左运动)

JAVASCRIPT
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 复杂系统

作为一个合乎逻辑的下一步,代理应当不仅能够感知他们的物理环境,还能够感知同伴代理的行为,并相应地采取行动。

复杂系统通常被定义为一个超越其部分之和的系统。虽然系统的个别元素可能非常简单且容易理解,但系统整体的行为可能高度复杂、智能且难以预测。

想象一下一只微小的爬行蚂蚁,蚂蚁是一个自主代理;它可以感知其环境(利用触角收集关于化学信号方向和强度的信息)并根据这些信号做出移动决策。但是,一只独自行动的蚂蚁能建立巢穴、收集食物或保护女王吗?蚂蚁是一个只能感知其直接环境的简单单位。然而,蚁群是一个复杂的系统,是一个协同工作的超有机体,其组成部分共同努力实现困难、复杂的目标。

构建复杂系统的三个核心原则:

  1. 简单单元之间有短程关系:对环境感知有限
  2. 简单单元并行操作:每次循环中,每个单元将计算自己的转向力
  3. 系统整体呈现出涌现现象:复杂的行为、模式和智能可以从简单单元之间的交互中涌现。这种现象在自然界中发生,如蚂蚁群体、迁徙模式、地震和雪花。

复杂系统的另外三个特征将有助于框定讨论,并提供软件模拟的指导方针。这是一组模糊特征,并不是所有复杂系统都有所有特征:

  1. 非线性:复杂系统的这一方面通常被称为蝴蝶效应,源于数学家和气象学家爱德华·诺顿·洛伦兹,他是混沌理论研究的先驱。之所以称为非线性,是因为初始条件的变化与结果之间没有线性关系。初始条件的小变化可能对结果产生巨大影响。即使在由许多 0 和 1 组成的系统中,只要更改一个比特,结果也会完全不同。非线性系统是混沌系统的超集
  2. 竞争与合作:组成元素之间同时存在竞争与合作
  3. 反馈:复杂系统通常包括一个循环,将系统的输出反馈到系统中,以积极或消极的方向影响其行为

Flocking 聚集

Flocking 是一种在许多生物中发现的群体动物行为,例如鸟类、鱼类和昆虫。1986 年,Reynolds 创建了一个聚群行为的计算机模拟,并在他的论文 Flocks, Herds, and Schools: A Distributed Behavioral Model 中记录了该算法

Reynolds 使用术语 boid(一个虚构的词,指的是一种鸟类物体)来描述一个群体系统的元素

Flocking 的三个规则:

这些简单局部规则,在系统层面却能形成自然逼真的群体模式

版权声明

作者: Aspi-Rin

链接: https://blog.aspi-rin.top/posts/%E8%87%AA%E4%B8%BB%E4%BB%A3%E7%90%86%E4%BB%8E%E4%B8%AA%E4%BD%93%E8%A1%8C%E4%B8%BA%E5%88%B0%E5%A4%8D%E6%9D%82%E7%BE%A4%E4%BD%93%E7%B3%BB%E7%BB%9F/

许可证: CC BY 4.0

This work is licensed under a Creative Commons Attribution 4.0 International License. Please attribute the source.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键