越卷越像:我用遗传算法做了个"内卷"实验
在线体验:http://139.224.13.254:23456/
注意建议用电脑打开,手机屏幕太小容易难以显示完整界面
一、先说结论
我让一群四足机器人在六种不同的"评价标准"下进化,跑了 50 代 × 64 个个体,结果很一致:
- 只比一个方向(E1/E2/E3):种群的基因多样性掉到 0.31--0.37,64 个个体里有 34 个都挤在最好成绩的 90% 以内------几乎塌成一个人;
- 每个方向都算赢(E4/E6):多样性只掉 4--10%,最好成绩 3.73 / 4.22 米,只比最远的 E2 少 25--34%。
用一句大白话说:当所有人都被同一把尺子量,赢家只有一个,其余人不是变强,是变得一样。
这就是我想用代码演示的"内卷"。
二、为什么做这个
遗传算法有个经典毛病叫早熟收敛:种群过早变得一模一样,失去继续进化的能力。教科书上的解释通常很抽象,我想把它做成一个能看、能玩、能改参数的东西。
载体选了"四足机器人走路":
- 它足够难,不是一眼能猜出答案的玩具问题;
- 它足够直观,走得好不好,肉眼就能判断;
- 它足够"物理",每一步都要真的和地面接触,没法作弊。
于是就有了这个项目:同一套进化机制,只换评价标准,看种群会不会变得不一样。
三、它是怎么工作的
1. 一个个体 = 16 个数字
每个机器人有 4 条腿,每条腿 4 个参数:频率、相位、摆幅、抬脚。全部是 0~1 的小数,运行时再换算成物理量。
!16 个基因
比如 0.62 → 频率 1 + 0.62×3 = 2.86 步/秒,0.35 → 抬脚 0.35×0.10 = 3.5 厘米。
2. 一代是怎么跑的
!一代的循环
32 个个体各跑 5 秒 → 打分 → 排序 → 取前 8 名 → 交叉 + 变异生 24 个 → 下一代。
3. 交叉与变异
!交叉与变异
- 交叉:孩子的 16 个位置,每一位掷一次硬币,决定抄爸爸还是抄妈妈;
- 变异:每个位置有 5% 概率被重置成随机数。
变异率不是越高越好。这个项目实测:20% 的变异率下,每个孩子平均被打散 3.2 个基因,父母刚调好的组合就散了;降到 5% 后,平均距离提升约 60%,倒地率从 49% 降到 33%。
四、六种评价标准
| 环境 | 谁算赢 | 本质 |
|---|---|---|
| E1 只比 x | 向前的位移最大 | 单一标量 |
| E2 距离原点 | 离出发点最远,不分方向 | 单一标量 |
| E3 0.5x + 0.5y | 两个方向加权相加 | 还是单一标量 |
| E4 全方向 | 存在某个方向,它的投影最远(凸包顶点) | 多标准 |
| E5 Pareto | 没人同时在 x 和 z 上都压过它 | 多标准 |
| E6 扇区冠军 | 每 15° 一个扇区,取扇区内沿自身方向最远的(且 ≥ 全场最远者的 50%) | 多标准 |
!E4 凸包
E4 是"无数个方向"的精确解:每个方向问一次"谁最远",答案一定是凸包顶点;灰色点在内部,永远轮不到。
E5 的区别在于它能容纳"凹点"------
!Pareto 与凸包
C 没人同时在两项上压过它,所以是 E5 的冠军;但它赢不了任何方向,所以不是 E4 的冠军。
五、实验结果
一次真实实验:50 代、每代 64 个个体、变异 5%、种子 76767、重力 4.9、摩擦 1.5。
| 环境 | 多样性(第 1 代 → 第 50 代) | 最好成绩 | 冠军数 |
|---|---|---|---|
| E1 只比 x | 1.51 → 0.37 | 2.60 m | 34* |
| E2 距离原点 | 1.52 → 0.36 | 5.63 m | 34* |
| E3 0.5x+0.5y | 1.56 → 0.31 | 1.58 m | 34* |
| E4 全方向 | 1.59 → 1.43 | 3.73 m | 8 |
| E5 Pareto | 1.52 → 1.00 | 2.97 m | 16 |
| E6 扇区 | 1.55 → 1.49 | 4.22 m | 5 |
* E1--E3 这一列是"达到最好成绩 90% 的人数":64 个里有 34 个,人多恰恰说明大家太像了。
!实验报告
三个观察:
- 单一标准会把人挤成一个:E1/E2/E3 的多样性掉到 0.31--0.37;
- 多标准几乎不损失成绩:E4/E6 只掉 4--10%,成绩仍有 3.73 / 4.22 米;
- 加权和不等于多目标:E3 掉 80%、成绩也最差------把两个方向加成一个分数,本质还是单一标准。
六、怎么用这个网站
打开 http://139.224.13.254:23456/,你会看到 32 个小机器人在网格上跑,右上角是控制面板。
实时模式
| 控件 | 作用 |
|---|---|
| 评价标准 | 切换 E1--E6,切换后种群重置 |
| 种群 | 8 / 16 / 24 / 32 / 48 / 64 |
| 每代 | 3--20 秒,跑多久算一代 |
| 速度 | 1--8 倍速播放 |
| 变异率 | 默认 5%,建议 2--10% |
| 种子 | 随机数起点,同一个种子结果可复现 |
| 重力 | 默认 4.9(0.5g),越小越不容易摔但抓地力也小 |
| 摩擦 | 默认 1.40,太低会打滑 |
| 摆幅 | 默认 0.70,步子迈多大 |
| 站立加分 | 勾上后站得越久位移加成越多(全程站住 ×1.5) |
左上角的数字会实时刷新:第几代、最好成绩、多少个体倒地。
实验模式
点「实验模式」,可以设代数、每代秒数、变异率、种群、种子、重复次数、时间速率,还有重力、摩擦、摆幅和站立加分。跑的时候六个环境(E1→E6)轮流训练,画面会实时显示正在评估的那批个体。
跑完自动生成报告:
- 末代散点:点铺成一条线 = 只有一个方向;铺成一圈 = 多个方向共存;
- 多样性曲线:看它塌不塌;
- 优胜数曲线:这一代有几个赢家;
- 方向分布 / 相似度直方图;
- 汇总表:均值 ± 标准差。
读图顺序建议:先看多样性塌不塌,再看散点铺不铺得开,最后看表确认。
项目介绍
右上角「项目介绍」是一个面向零基础的图文教程,从"16 个数字"讲到六种标准,数学公式放在最后,可以跳过。
七、技术实现
- 渲染:three.js
- 物理:Rapier(WASM),每个个体一个独立世界,互不干扰
- 进化:自写的 GA,锦标赛选择 + 均匀交叉 + 重置变异,种子可复现
- 构建:Vite + TypeScript,纯静态,6.6 MB
- 部署:一台 2 核 2G 的云服务器 + nginx,端口 23456
32 个个体同时跑物理,每代 5 秒大约占满一个 CPU 核心的一小部分,所以 2G 内存的机器完全够用。
八、局限与后续
说点实话:
- 机器人走得不快(0.2--0.5 m/s),因为控制是开环正弦 + 简单反射,大部分力浪费在打滑和四条腿互相抵消上;
- 数据大多来自单种子,正式结论应该跑 3--5 次重复;
- 还没做完整的落脚点规划(Raibert 控制器只做了简化版)。
下一步想做的是 trot 步态约束 (对角腿同步)和真正的落脚点规划,这两项最有可能把速度提上去。
九、结语
这个项目没有多高深的技术,但它把一件抽象的事变得能看见:评价标准的设计,本身就在塑造种群。
如果它对你有用,或者你发现了什么有意思的现象,欢迎在评论区告诉我。