数字果蝇实验室:连接组点亮的果蝇飞过夜色里的大自然

数字果蝇实验室

生态箱 ↗只改世界 · 它自己学、自己进化

朝果蝇扔球、喂糖粒苦粒、往触角上撒灰。决定往哪转、何时起飞、吃不吃、要不要梳理的,是在你浏览器里实时运行的 4,599 个 FlyWire 连接组神经元;起飞后是真实果蝇的逃逸飞行轨迹。

躲开0
被击中0
起跳0
吃掉0
梳理0
行走中 大脑 0.00 s · 1.00× 实时
点击场地任意位置,从那里朝果蝇扔一个球
左眼
右眼
后 180°前 0°后 180°
摄像机原始投影(采样成小眼之前)——墙、看台、别的果蝇都在里面
左 90°前 0°右 90°
三通道合成紫外+蓝+绿
果蝇视角 · 721 小眼/眼
7 个手术任务:只准动开关,把果蝇改造成指定的样子。每关的通过线都是实测出来的。
球场版:任务、突变体、光遗传那些已发布的数字都是在这颗 5,563 个神经元的大脑上量的。
0 只 每只都有自己独立的一份真实连接组大脑;只接了视觉→转向/起飞这一路
未抽取
口渴 0%

任务模式:每一关的通过线都是实测出来的

七个手术任务,玩家只准动开关(断突触 / 敲真实神经元 / 打乱接线 / 光遗传脉冲),不准改规则。 问题是:怎么知道一关设计得对不对?我们的做法是把每关都用 node dodge/mission_test.js 跑两遍——不干预参考解,各 3 个种子 × 60 秒,然后照着实测分布定通过线。 一关如果不干预也能过,它就没在考任何东西;参考解如果过不了,那这关就是不可能完成的。下表是全部实测值。

两处按数据改过设计,值得写出来:①「一个不够,两个才行」原本把线画在 8 s, 结果单敲也能过;实测逐次是——不干预 6.1/8.6/11.2 s、单敲 Clavicle 5.4/8.1/5.1、单敲 G2N-1 4.8/6.8/7.9、 两个一起 2.8/2.9/2.0,于是线改到 4 s,落在「单敲最低的 4.8」与「双敲最高的 2.9」之间。 ②「证明连接组真的在起作用」原本判闪避率,但打乱之后逐次是 0.07/0.79/0.43——波动大到有一次跟没打乱差不多, 所以判据换成起跳次数(打乱后 18/5/0 次 vs 不干预 33/32/33,完全不重叠),这也更贴近机制: 打乱先打断的是 looming → 巨纤维那一路。

为什么果蝇会顶着围栏一直走——以及「只给物理量、不写判断逻辑」这条原则的代价

有人看到果蝇走到场边会一直顶着围栏往前走。查下来不是它认不出墙:场地边界只是每一步末尾的 一次几何钳位,而手写的视觉前端只读球的世界坐标算张角增长率,场景里别的东西一概不看。 实测(无球、120 秒 × 3 种子):92.8% 的时间贴着墙,最长一次连续 111 秒。

那为什么不用摄像机?——那条路本来就有:eyecam 用一台立方体贴图摄像机从果蝇头部 渲染整个 3D 场景,墙、看台、别的果蝇都在里面,以后往场里加任何物体也都会自动在里面。 左上角复眼视窗里那条新的横条就是它的原始投影(采样成 721 个小眼之前的样子)。 实测它确实看得见墙(静止时读数恒为 0,一走就有),「朝墙走」(LPLC2 中位 34.2 Hz) 和「背墙走」(23.5 Hz)几乎分不开——读到的主要是走路本身造成的全场光流。 真果蝇有平衡棒与传出拷贝来抵消自体运动,这个模型里没有。

那就只给它物理量,让它自己决定。这是最干净的做法,我们试了:触角碰到围栏 → 驱动机械感觉神经元。 结果是果蝇完全没反应(贴墙率一个百分点都没动)。原因在连接组里:游戏用的子回路里唯一的 机械感觉是触角 JO,而 JO 只通到梳理指令 aDN1,根本不通到转向或逃逸

于是去全脑里查出路dodge/sensor_reach.py,只走 ≥3 突触的边): 脑连接组里有 305 个头部刚毛(真正的触觉感受器)、29 个温度、74 个湿度, 而且它们到 DNa01/DNa02/巨纤维/MDN 只要 2–3 跳。通路是有的,只是我们裁子回路时没把它们当输入。 重裁一版 v3(5,563 个神经元)把这三路加进去之后:

这就是那条原则的完整答案:只给物理量、不写判断逻辑是可行的—— 触感送到头部刚毛之后,果蝇走过的格子从 9 涨到 31.5、路程从 156 mm 涨到 603 mm, 中间没有任何我们写的判断。但前提是子回路里真的有那条通路; 送错地方(JO)就等于没给。游戏页现在仍然跑 v2 + 那个手写的「看得见围栏」, 是因为切到 v3 会让已经发布的任务关卡、突变体、敲除比值全部需要重测——这是工程取舍,不是科学结论。

加了新的感觉通道,就得先量它有没有落点。把 v3 的每一路输入单独驱动 1 秒, 看六个运动读出组里有没有哪个被推起来(判据:任一读出 > 5 Hz):

几点必须说明:苦味「没有落点」是对的——它是抑制性的,单独给它本来就不该驱动任何东西, 它的作用是把糖驱动的 MN9 压下去(论文 Fig 3B–C,我们复现过:最强糖下苦压掉 82.4%)。 湿度是真的没有落点,给多强都推不动运动输出。温度要 400 Hz 以上才有, 在合理的刺激强度下等于没有。触感在 100–200 Hz 就有,这也是为什么它能让果蝇绕开围栏。 还有一条有意思的:触感双侧同时给反而更弱(200 Hz 时六个读出全是 0)—— 左右对称的驱动把转向所需的左右差抵消了,这不是 bug,是回路本来的样子。

还有一处是我们自己的指标错误,值得写出来:第一版用「贴墙时间占比」当指标, 而它分不出「钉死在一点」和「贴着墙滑行」——两种情况这个标志都为真。 按那个指标,触感「没用」(85.6% → 85.6%);换成走过的格子数才看出来果蝇其实一直在沿墙移动。 指标选错,阴性结果就是假的。

「生活」模式:五感接到真实的感受器上,剩下的交给连接组

顶部第三个标签。这是单独的一只果蝇,用的是新裁的子回路 v4(),比球场那只多了听觉、两个嗅小球(醋 / 土臭素)和 4 个内感受神经元。 世界自己运转——昼夜、烂果子、水洼、气味、嗡嗡声、热源、风、灰尘、来球——每一样都只作为物理量送到对应的感受器上;没有一条"闻到就过去""听到就跑""卡住就掉头"的规则。 所以下面这张表不是功能清单,而是实测:每一路感觉,在这个模型里到底通不通到行为。

声音单独不让它起飞,但让它对逼近更敏感

让它自己过 10 分钟

哪些是手写的:世界的节奏与强度;声强随距离的衰减与左右耳差;气味羽流的形状;身体状态(能量、水分)的消耗速率,以及它对味觉感受器灵敏度的调制幅度(方向有文献依据); 「MN9 超过 10 Hz 算开吃」这条读出阈值;「它在想什么」的句子模板与阈值(每个词的触发量是实测的频率)。它不是语言模型,也不是果蝇会说话。

大自然:一个有重力、有天气、没有边界的 3D 世界

第一个标签默认进的就是它(「世界」开关可以切回篮球场)。地形、石头、蘑菇、花、草、浆果丛、水洼都按种子确定性生成,游戏核心和 3D 渲染用的是同一个世界对象——画出来的石头就是它撞上的石头。 物理用真实单位:重力 9,810 mm/s²,浆果从枝头掉下来、按地形法线反弹、顺坡滚、停稳后发酵;石头不可穿透;上坡走得慢;太阳把深色石板晒烫;洼地下雨蓄水、晴天蒸发;风把气味吹向下风。 果蝇只通过真实的感受器知道这一切(坡度 → 步速,实心物件 → 挡路 + 触角被压,石板 → 温度感受器,雨 → 湿度感受器和听觉神经元,发酵的浆果 → 嗅觉感受神经元,甲虫和下落的浆果 → 逼近视觉),没有新增任何一条行为规则。 威胁从「飞来的球」换成了「爬过来的甲虫」:毫米尺度上,60 mm/s 的抛体 0.1 秒就落地、滚几毫米就停,慢慢滚过来的球不物理。

玩法像「我的世界」:场景底部的工具栏(数字键选、点地面用)可以放果子、挖水洼、放石头、种蘑菇 / 浆果丛 / 花、放甲虫、抬高 / 挖低地形、拆除——改的是同一个世界对象,放下的石头真的挡路,改动按种子存在本机。世界分草甸 / 林下 / 砾石滩 / 湿地四种生物群系。 C 切视角(轨道 / 跟在身后 / 第一人称);按住 A D S 空格 = 直接驱动真实的转向 / 后退 / 巨纤维神经元(遥控的是神经元,不是身体);F 把场景铺满窗口,大脑和实时 spike 改在同一个 3D 画面里渲染。

物理自检(判据写在跑之前,都是能手算的量)

它在里面过得怎么样

如实说明:默认的视觉前端仍是手写的逼近检测,石头、草、地形不在它的视觉里——它绕开石头靠的是被挡住之后贴着滑过去加上触角触碰。复眼视窗里「只靠眼睛躲」那条真实像素通路现在也接到了这一只上,但它一走动满地的草就成了假逼近,所以默认关。 果蝇自己的身体是运动学,不会摔倒;受重力支配的是浆果。水洼是水位,不是流体。地形的幅度、物件的密度、恢复系数 0.35、滚动阻力 0.15、坡度系数、天气节奏全部手选。

学习、多巴胺、六条腿:哪些成立,哪些是阴性

「生活」模式的果蝇现在用子回路 v5()= v4 + 连接组里的蘑菇体学习回路:嗅觉感受神经元 → 投射神经元 → 凯尼恩细胞蘑菇体输出神经元(MBON), 外加 多巴胺神经元(PAM、PPL1)和全局抑制 APL。接线全部来自 FlyWire;唯一会变的是凯尼恩细胞 → MBON 这一层突触: 凯尼恩细胞刚放过电、同一隔室里又有多巴胺,这条突触就被压低(文献里果蝇嗅觉记忆的位置与规则:Hige 2015;Cohn 2015;Handler 2019)。 哪个多巴胺神经元管哪个隔室,由连接组里 DAN → MBON 的突触数决定,我们没有写对照表。下面每一张表都是实测,判据都写在跑之前。 想在 3D 篮球场里看这一只:「篮球场」控件最上面的「大脑」开关切到「五感全开」——同一只、同一份记忆,气味、热源、声音都画在球场里,六条腿各按自己的相位走;任务、突变体、光遗传仍属于 5,563 个神经元的球场版(那些已发布的数字是在它上面量的)。

① 先得解决一个老问题:一闻到气味就全脑失控

② 它学得会吗

③ 记住了,会改变行为吗——阴性

④ 在身体里学:页面引擎(六条腿的运动学身体)与物理仿真(NeuroMechFly,MuJoCo)

⑤ 六条腿

哪些是手写的:① 触角叶局部神经元一律取抑制性(递质符号的纠正,有文献依据);② 碰到蘑菇体的边只留 ≥2 个突触的(页面装不下全部 129 万条); ③ 糖 / 烫 → 多巴胺这根线(连接组自己叫不起多巴胺神经元,见 ②);④ 可塑性规则的三个数(学习率、资格迹 200 ms、不遗忘);⑤ 气味 A / B 是合成的(各 20 个嗅小球),不是真实气味的受体谱; ⑥ 下行神经元 → 「步频 + 左右步幅」的映射,以及六条腿之间的相位耦合——腹神经索连接组给不出三足步态(上面「腹神经索」一节);⑦ 「记忆 → 转向的桥」整个是手写的,所以默认关。

训练果蝇下五子棋:它怎么学的、学到了什么、哪些不是它的功劳

把「主画面」切到「五子棋」就能和它下(含黑方禁手:长连、四四、三三)。先说清楚分工:

① 每条线值多少——果蝇脑 + 一层线性读出。对一个候选点,沿 4 个方向各取一条线(两侧各 4 格, 每格 = 空 / 我方 / 对方 / 边界)。这样的线型一共只有 14,641 种,少到可以让这颗 5,563 个神经元的果蝇脑把每一种都跑一遍:8 个格子 × 3 种状态 = 24 个通道,各接约 61 个真实感觉神经元, 泊松驱动 300 ms,数下游神经元的脉冲,对 3 个随机种子取平均脑子里一个突触都不训练, 训练的只有一层线性读出,它把放电换成这条线的对数价值。落子分 = 4 条进攻线 + λ × 4 条防守线的价值之和。

② 「推理 N 步」也是训练出来的,不是下棋时现算的。数据集里每个局面都标了「向前搜 1–6 步、8 步」各自的最佳着; 对每个 N,用它当标签各训练一个读出层。棋盘上方的推理步数选的就是用哪一个——下棋时只有模型推理:线型 → 果蝇脑的放电 → 这个读出层 → 落子分最大的点,没有任何搜索。 随时可切,正在想的那一步会立刻按新的重想。棋盘上画的就是它的想法:橙色圆圈 = 模型给各候选点的概率,编号的虚子 = 它预想的后续(同一个模型替双方轮流走 N 步)。 ③ 禁手与胜负由规则引擎判。每走一步,页面会把这一步的线型放进真脑里现场重跑,与价值表比对—— 这时左边的 spike 图、下行神经元、感觉输入显示的就是下棋这只果蝇的放电。 ④ 外挂搜索(默认关)才是 alpha-beta 算法:把同一张价值表交给它往前搜,只懂规则、不含棋形分值。它强得多,但那是算法的功劳,页面上单独标着。

训练过程

两个阶段。阶段一:把「落子评分算法」当训练数据——让读出层去拟合老师对全部线型的对数分值 (训练局面里只出现过其中 6,266 种,只靠着法标签的话,其余的价值会随意漂移,搜索一走进没见过的局面就崩)。 阶段二:用深度 8 搜索的最佳着做微调()。 下图左:果蝇从没见过「活三」「冲四」这些类别,类别只用来给学到的价值分组——看它自己排出的顺序对不对。 右:阶段二的验证集一致率随训练步数的变化,起点就是阶段一的成绩。

训练完成

同一套训练,换六种特征各做一遍

真下几盘(外挂搜索:同一个搜索引擎,换不同的价值表)

v1 的全部数字作废。这张卡片以前写的是「果蝇脑的一致率 0.025、只比随机高一点;连接组对下棋没有贡献」。 那一版有一个 bug:给大脑换输入时上一批刺激没清掉,输入一个接一个地叠上去。 于是所有棋盘喂进去几乎是同一个输入,读出层学到的是噪声。 另一半原因是架构:v1 把整盘棋随机铺到感觉神经元上,丢掉了五子棋最要紧的局部性与平移不变性。 游戏本身不受这个 bug 影响(每一步都整组重写输入;修复前后 7 个任务、10 个突变体的结果逐项相同)。

盲盒突变体:十个敲除里,只有七个能从行为上看出来

「抽一只突变体」会随机敲掉十个真实神经元中的一个而不告诉你是哪个,你靠喂糖、喂水、落灰、扔球把它诊断出来。 但在把这个玩法做出来之前得先回答一个问题:这些敲除,行为上真的看得见吗? node dodge/mutant_test.js 把每一个都跑了 60 秒 × 3 个种子,六个指标全测, 判「可诊断」用的是最保守的口径——该指标三次的区间与不敲的三次完全不重叠

看不出来的那三个才是重点:G2N-1、Zorro、CB0883 敲掉之后,六个行为指标全都落在正常范围里。 这不是游戏做得不好——这正是全脑筛选反复得到的那条结论的行为版:单敲除常常什么也看不见。 CB0883 在全脑里是枢纽(单独敲 0.76,配上 Clavicle/G2N-1 塌到 0.24),Zorro 是真实实验证实「伸喙必需」的类型, 可它们单独被敲掉时,果蝇看起来完全正常。要发现它们,必须成对地敲——那就是「一个不够,两个才行」那一关。

别人拿数字果蝇做了什么,我们差在哪

2026 年 9 月 3 日 Janelia 与 Google 公开雄性果蝇全中枢神经系统连接组 MaleCNS v1.0(约 16.6 万神经元)之后的两周里, 网上冒出一批"把果蝇脑接进游戏"的项目:接《Minecraft》、接《DOOM》、接《Beat Saber》、当桌面宠物。 下面这几个是我自己打开仓库页核实过的(星数与说明取自各自 README,2026-09-19):

它们的共同套路:①把像素亮度灌进 R1–R6 光感受器;②手选几个下行神经元当按键(左右差转向、某个 DN 前进、DNp01 触发跳); ③在读出端加一层训练出来的网络(CEM / 岭回归 / 反向传播)。第三条是关键——一旦读出层足够灵活, 连接组是不是真的在起作用就说不清了。

一篇值得读的批评:Patrick Mineault,Deconstructing viral fly sims(2026-09-14,neuroai.science)。 他的核心质疑是:多数演示并没有建立从感觉到运动的完整闭环,视觉输入的作用近似于随机种子; 而"足够大的网络本来就能学会很多事,是不是果蝇都一样"。他提出的严格标准是: 行为要由感觉真正驱动、活动要在电生理上说得过去、并且能表现出个体性。

我们和它们的差别,只有一条,但是这条最贵:这个页面里没有任何训练出来的读出层。 从 LC4/LPLC2 到巨纤维、从糖味受体到 MN9、从 JO 到 aDN1,中间全是连接组里的真实突触, 参数就是论文那一套 LIF。代价是它笨——躲球靠一个手写的 dθ/dt 前端,真让它只用像素看,45 mm 外就完全是瞎的(我们把这个负结果也做成了开关)。 换来的是可以做对照:任务模式里的「证明连接组真的在起作用」那一关,就是打乱接线之后看它垮掉。 论文补充表 1D 做的是同一件事,我们实测糖 → MN9 从 82.9 Hz 掉到 3.5 Hz。

复现台账:我们对着论文验了什么

这个项目不是"跑通了一个模型",是拿已发表的论文逐条对答案。下表是全部结果, 包括做不到的结果是阴性的——那两类同样是结果。 每条都能追到产出它的脚本与结果文件;完整过程日志在仓库的 docs/log/report.md

本项目自己的结果——不是对某篇论文的复现,是这个项目自己做出来的结论,阴性的也在里面

让果蝇看你的图

上传一张图,果蝇用它真实的视觉系统看一遍,再画出来。链路上没有示意图:你的图先变成 450×512 的相机画面,按 FlyGym 的真实复眼采样表(含鱼眼畸变)归并到 721 个小眼,再喂进 flyvis 的预训练连接组视觉网络(65 类细胞 × 721 柱,2355 个卷积抽头),最后由六边形卷积解码器还原。

一个必须说明的限制:flyvis 自带 50 个预训练集成成员——连接组约束下解不唯一,作者本人强调成员间差异。我们用的是其中一个(flow/0000/000)。把 50 个全跑一遍之后:「整个网络是六边形卷积」这条 50/50 成立;但 T4/T5 各亚型的偏好方向在成员之间离散极大,我们基于成员 000 得到的若干视觉结论不能当作 flyvis 的普遍性质(详见报告 §33)。

果蝇会扫视,原因有两个,都不是为了好看。其一,T4/T5 是运动检测器,盯着不动的图几乎不响应。其二:相邻小眼间距 16.4 像素,不动的话永远只采到同一批点,就是 721 块马赛克;一动起来,同一个小眼会落到世界的不同位置(真果蝇也这么干,叫微扫视超敏锐度)。

但光把多帧平均起来没有用——实测提升 −0.2%,因为每次测量本身就是小眼的模糊平均,再叠加只是把更多张模糊图平均。真正管用的是反投影:解一个反问题,找出一张高分辨图,使得"用小眼去测它"能复现全部 69,216 次测量。实测与原图的相关:单帧 0.851 → 朴素累积 0.849 → 反投影 0.920,超过了单次接受角决定的天花板 0.883 —— 说明它真的从多帧里解出了单帧不含的信息。用的全是实测值和已知几何,不引入训练数据的统计,和右边四栏的解码器不同

但别指望变成照片:果蝇单眼 721 个小眼、角分辨率约 4.5°;人眼约 1.2 亿感光细胞、中央凹 0.008°。差五个数量级,低分辨不是模拟得糙,就是果蝇眼里的世界

关于颜色:果蝇有色觉,而且比人多一维——R7 是紫外(pale 型 Rh3 ~345 nm / yellow 型 Rh4 ~375 nm),R8 是蓝(Rh5 ~437 nm)或绿(Rh6 ~508 nm),但几乎看不见红。小眼分 pale / yellow 两型,实测比例 216 : 505 ≈ 30% : 70%,和真果蝇一致。第二栏按这个分型上色:蓝格子是 pale(取蓝通道),绿格子是 yellow(取绿通道),像拜耳滤镜的彩色马赛克。试试「色块」预设——红色那块在果蝇眼里几乎是黑的。

两个诚实的限制:紫外无法从照片恢复(sRGB 里根本没有紫外,而那是果蝇最重要的通道之一);flyvis 每个小眼只吃一个标量(它是用光流任务训练的,没有颜色通道),所以颜色在进入神经网络那一步必然被压成一维——后面几栏是黑白,是模型的限制,不是果蝇的。

或用预设: 直显类型
你的图
721 个小眼
蓝=pale 型,绿=yellow 型
扫视累积 + 反投影
r 0.851→0.920,只用实测值
神经活动直显
未经解码
解码自 R1–R8
解码自 L1–L5
解码自 Mi/Tm
解码自 T4/T5

后四栏是解码重建,会「补」出神经活动里其实没有的细节——解码器学的是训练图像的统计规律。第三栏是未经解码的原始活动,两者的差距本身就是要看的东西(同 §15 解码器压力测试)。越往右的层,画面丢得越多:到 T4/T5 只剩大形状,细条纹没了。

舞台右上角那个小窗是同一套东西的实时版:把 3D 世界从果蝇眼睛的位置再渲一遍(左右眼各偏前方 50°),走同一张采样表成像。为了实时跳过了 flyvis 和解码器,只到「视网膜接收到什么」为止。原来每秒只刷 3 次,因为同步读回 GPU 像素要等一次约 4–5 ms 的往返;实测那次停顿与读几个面、读多少像素、贴图多大全都无关(读 1 面 6.3 ms ≈ 读 6 面 6.8 ms;边长 192→48 不降),所以降分辨率没用。改成 WebGL2 的 PBO 异步读回(下一帧取数,已验证与同步逐字节相同)后,单次从 10.6 ms 降到 2.9 ms,现在实测 23 fps,主画面仍 60 fps。
「只靠眼睛躲」这个开关把游戏的视觉前端换掉:平时果蝇是直接读球的世界坐标算张角增长率(手写规则,而且只看球、无视场景里其它一切);打开之后它只能靠这 721 个小眼的读数——真实像素 → Rh1(R1–R6)→ flyvis → 按 Klapoetke et al. 2017 的形式汇集背离中心的 T4/T5 → LC4/LPLC2。结果是负的,而且可以量:球半径 2.5 mm、小眼间角 5.7°,所以球在 60 mm 外还不到一个小眼;实测这套真实视觉在 45 mm 外完全瞎(读数恒为 0),要到 25 mm 以内才把球从噪声里分出来,而手写前端从 110 mm 就在跟。更要命的是果蝇光是走路,自体运动的全场光流就能给自己造出峰值 126 Hz 的假「逼近」——所以开着它果蝇会一边走一边被自己吓得起飞。这不是模型坏了,是真果蝇要靠触角和平衡棒去抵消的那个问题,而这个模型两样都没有。开关默认关,打开会掉帧(每次约 12 ms)。渲染时会把果蝇自己的网格藏起来——摄像机在身体前方,不藏的话它自己的头会糊满半个视野。
每一类感光细胞按自己的光谱积分:以前是「pale 小眼取蓝通道、yellow 取绿通道」的类拜耳近似,现在用 Govardovskii et al. 2000 的视色素模板,按各自 λmax 逐 1 nm 积分出对三个渲染波段(紫外 300–400、蓝 400–500、绿 500–700)的权重:Rh3 (R7p, 345nm) 99.8% 来自紫外、Rh4 (R7y, 375nm) 88.3%、Rh5 (R8p, 437nm) 蓝占 66.4%、Rh6 (R8y, 508nm) 绿占 43.4%。更要紧的是补上了 Rh1 (R1–R6)——八个感光细胞里的六个、运动与亮度的主通道,此前完全没有建模,而它正是 flyvis 的主输入。两个假设要说明:场景是 sRGB 著作的、没有真实光谱,三个通道被当作各波段的平均反射率;果蝇 R1–6 还有一个 3-羟基视黄醇增感色素在 ~350nm 给第二个峰,不在该模板里,这里按手选幅度补上(两套权重都写在 results/vision/opsin_weights.json 里,可以对比)。
视野边缘不再是近似:一开始用透视投影 + FlyGym 的鱼眼校正,但透视投影天生到不了 180°,157° 时边缘已经被拉得极扁,鱼眼公式还有个奇点。现在改成立方体贴图——六个 90° 面覆盖整个球面,每个小眼按自己的视线方向精确采样,不需要任何畸变补偿。小眼方向按六边形点阵定义:半径 15、小眼间角 5.7°,单眼视野 171°,接近真果蝇(文献 ~160–180°)。几何自检实测:9 个已知方位平均误差 0.7°
紫外(R7)那一路是手写设定:sRGB 渲染里没有紫外,所以给场景里每类物体指定了一个紫外反射率。依据是真实的紫外世界结构——自然界里天空是压倒性的紫外源、绝大多数表面紫外很暗,果蝇正是靠这个对比找开阔空间、判断上下。但数值没有任何测量依据,只是结构上说得通;上传的照片里更是完全没有紫外信息。
为什么看着一片绿:一开始整场都用页面主题色(地板 #1a211e),绿蓝两通道只差 11%,果蝇眼里等于没有颜色;加上每个小眼只测一路,画面按感受器类型上色,70% 是 yellow 型 → 天生 70% 的格子是绿的。现在球场改成真实配色(木地板、白线、蓝红座椅),并做去马赛克——把每个小眼缺的那一路用邻居里另一型的平均补上,和拜耳滤镜同一回事(果蝇本身并没有在每个小眼上同时拿到两路,这是显示用的插值,可以关掉看原始马赛克)。
改完实测:场景 G/B 从 36.6/32.5 变成 76/49,眼内 G/B = 0.310/0.205,41 个小眼偏蓝(蓝座椅)。但整体仍然偏绿,而这是对的——木地板是橙色,果蝇没有红色感受器,橙色只刺激它的绿通道、几乎不刺激蓝通道。篮球场在果蝇眼里就是一片暗绿。
仍然是近似的:小眼间角取了常数 5.7°(真果蝇从正前 ~4.5° 到侧面 ~8° 渐变);天空是游戏的 CSS 渐变、不在 3D 场景里,离屏渲染时用舞台天空色补了个底。

进食与苦味

每 4 s 在果蝇前方放一颗:糖 → 苦 → 混合 轮换(3 局 × 120 s,无球)。头部碰到颗粒 → 两侧糖/苦味觉神经元 200 Hz;MN9 平均 > 30 Hz 就停下伸喙吃。

梳理

每 5 s 随机一侧触角落灰 → 该侧 JO 神经元 200 Hz,直到梳理完。aDN1 > 20 Hz 就停下梳理 0.6 s。

吃到糖,会分泌多巴胺吗

真实果蝇里,糖的奖赏信号由 PAM 簇多巴胺神经元送到蘑菇体(Liu et al. 2012;Burke et al. 2012),惩罚信号主要走 PPL1 簇。这里直接在全脑模型(138,639 个神经元,0.5 s × 4 次独立试次)里数这两群神经元有没有放电。事先定的判据:只有不失控(活跃神经元 < 2,000)时 PAM 放电才算数。

正面威胁时后退(LC16 → MDN):重做实验

依据 Sen et al. 2017:LC16 对 looming 反应,经 MDN 引发后退。先在全脑模型里测 LC16 刺激强度与 MDN 的关系,再按事先写定的设计重做对局:正前方 ±10° 一次来一个威胁,3 档速度 × 2 种威胁 × 5 种切除 × 3 档 LC16 编码强度,每格 24 个威胁,原地起跳模式。

全脑:LC16 刺激 → 下行神经元(Hz)

对局:正面威胁的击中率(括号内 = 出现后退的威胁比例)

第 3 轮的对局(自动发球,已被上表取代)

嗅觉导航:连接组给不出转向信号,游戏里的“闻着找”是手写的

全脑模型刺激一侧嗅觉受体神经元。降低刺激强度、只看全脑失控之前的窗口、找出失控的“点火”神经元并沉默它们,判据都事先写定:左右刺激的下行神经元侧化 LI =(左 − 右)/(左 + 右)符号相反且 |LI| ≥ 0.2。

降低强度:失控起点与失控前的脉冲(每次试验的脉冲数,左/右)

沉默点火神经元(探索性)

游戏里的“闻着找”(手写:高斯气味场 σ = 15 mm,左右触角比浓度,以 120°/s 转向;吃不吃仍由连接组的糖/苦 → MN9 决定)

第 3 轮:150/50 Hz 刺激的侧化与 Fly-Brain-AI 复现

腹神经索与腿

换成 Pugliese et al. 2025 的腹神经索发放率模型(作者代码本机调用,仓库无许可证,未复制代码):MANC 前足神经节 4,604 个神经元,含左右前腿运动神经元各 72 个;每个条件 16 组随机神经元参数,2 s。节律判据用作者的振荡得分(≥ 0.5)。再把运动模块的摆动/支撑发放率换成 NeuroMechFly 前腿关节角(方向取自 FlyGym 真实步态数据),和 CPG 步态对照。

第 3 轮:BANC 腹神经索 + 全脑同款 LIF 参数(没有节律)

接线到底重不重要?——把论文的对照实验做成滑块

舞台下方「动脑子」那一排,对应论文(Shiu et al. 2024)自己做过的对照。 打乱接线是补充表 1D:保持每个神经元的出度和它那串权重,只把突触后靶点随机重排—— 接线一乱,糖味觉到伸喙运动神经元 MN9 的信号几乎全没了。 权重 / 抑制滑块是补充表 11B–E(论文取 ±30%),谷氨酸改兴奋性是表 11F (默认模型把谷氨酸当抑制性;子回路里有 635 个谷氨酸能神经元,占 13.8%)。

打乱接线:正确接线 vs 随机重排

参数扰动下,10 个神经元的敲除判定还一样吗

全腹神经索:6 条腿、巨纤维电突触、本体感觉闭环

同一个发放率模型换成完整的 MANC 腹神经索(23,532 个神经元,6 条腿)。作者代码是稠密矩阵、要约 9 GB,这里改成稀疏存储,方程、参数抽样和求解器不变;先在前足网络上和作者稠密版逐点对齐。刺激强度按“DNg100 发放率与前足网络默认实验相同”标定。

全腹神经索:刺激条件

主条件(DNg100 双侧 I = 350)每条腿

驱动 NeuroMechFly 6 条腿(腿尖前后摆幅 · 4–30 Hz 节律占比;< 0.1 mm 记为基本不动)

巨纤维 → TTMn(跳跃肌运动神经元)电突触(4 个副本的中位数)

本体感觉闭环(手写感受器编码;DNg100 双侧 I = 350)

用作者的全突触数据重跑(W_20260522_allSynapses:多 393 万条弱连接,中位 1 个突触;原有连接权重完全相同)

除了玩游戏:虚拟敲除筛选与“脑机接口”压力测试

和真实实验对照:10 个做过光遗传沉默的细胞类型(比值 = 沉默后 MN9 放电 ÷ 不沉默;≤ 0.8 判为“伸喙必需”)

穷举 50 Hz 下全部活跃神经元:效应最强的 12 个

沉默后 MN9 反而升高最多的 5 个

双敲除:有没有“单独敲都没事、一起敲就断”的备份通路

换一条通路:水味觉(论文表 5 的实验全是这篇论文自己做的)

糖和水用的是同一批神经元吗(同一个 MN9 读出,两条输入通路)

三条通路:味觉糖、味觉水、触角机械感觉(同一套方法,不同输入与读出)

什么时候可以不跑模拟:只看连线能预测到什么程度

解码器压力测试:只看得到一部分神经元时,还能读出“果蝇在说哪些词”吗(12 个词的平均平衡准确率,0.5 = 瞎猜)

记录有噪声

通道中途坏掉(1,000 个有信号的通道)

自动对局:视觉编码与起飞方式 (第一版 1,294 神经元子回路测得)

同一批球(提前量瞄准:果蝇若毫无反应必被击中),每个条件 5 局 × 120 s、球速 60 mm/s,由本页同一套引擎无画面运行。

主设定下的损毁(新编码 + 真实逃逸飞行,第一版子回路)

真视觉接进连接组:LPLC2 能分辨逼近和平移吗

固定的 NeuroMechFly 看一个黑球,FlyGym 复眼(200 Hz)逐帧驱动 Lappalainen et al. 2024 预训练视觉网络 flyvis。30 种柱状细胞在每个柱上的活动,按 Codex 柱坐标(Matsliah et al. 2024)注入 FlyWire 全脑 LIF 里对应的 44,252 个神经元,读出下游的 LPLC2 与 LC4。这一步不是游戏里的手写视觉前端,游戏也没有用到它(离线运行,1.2 s 刺激约 90 s)。每个条件后来重复跑了 3 次:“逼近强于远离”8/8/8、“强于远距平移”8/7/8、“弱于近距平移”0/0/1,方向都不变;条件之间真正的不确定性来自格子对齐方式(朝向间标准差是重复间的 3–4 倍)。

事先声明的指标:刺激后 0.5–0.95 s 平均发放率(Hz),左侧神经元,8 种格子朝向的中位数

探索性指标(看过曲线后才加):碰撞前最后 150 ms 的峰值(Hz)

LPLC2、LC4、巨纤维在四种刺激下的发放率时间曲线(8 种格子朝向的中位数与范围)

裁出来的子回路,和全脑算得一样吗

同样给 LC4 + LPLC2 注入 100 Hz、0.5 s × 4 次独立试次,对比子回路与 138,639 个神经元的全脑 Brian2 模型(全脑参考已按独立试次重跑)。左右转向与逃逸的模式一致;数值上子回路高估巨纤维、低估 DNa02。

全脑(Brian2)子回路(本页引擎)

哪些是连接组,哪些是手写的

  • 连接组当前子回路 4,599 个神经元、338,837 条连接:LC4/LPLC2/LC16/糖/苦/JO 到 DNa01、DNa02、巨纤维、MN9、aDN1、MDN 之间突触数 ≥ 3、最多 3 跳的所有路径(在 3 组候选里按“≤ 6 MB、≥ 3× 实时、平均偏差最小”事先规则选定)。神经元参数与 Shiu et al. 2024 全脑模型相同,dt = 0.1 ms,在你的浏览器里逐步计算。
  • 手写进食/梳理/后退:颗粒在头下 → 糖/苦 200 Hz;灰尘在触角上 → 该侧 JO 200 Hz;LC16 = 0.5 Hz/(°/s) × 角速度;阈值 MN9 30 Hz、aDN1 20 Hz、MDN 20 Hz;优先级 起飞 > 后退 > 进食 > 梳理 > 行走。伸喙是喙部网格的纯视觉旋转;梳理姿态由逆运动学让前足尖触到触角生成(参照 flybody 梳理姿态数据集的前足梳理类型)。
  • 手写 · 非连接组“闻着找”(默认关):颗粒散发高斯气味场(σ = 15 mm),左右触角各采一次浓度,浓度 > 0.01 时朝浓度高的一侧以 120°/s 转向,叠加在 DNa 转向上;尝过的颗粒不再吸引。全脑模型里单侧气味刺激给不出转向信号,所以这一步完全手写;碰到颗粒后吃不吃,仍由连接组的糖/苦 → MN9 决定。
  • 连接组“左侧威胁 → 右侧 DNa 活跃、左侧巨纤维更强”这个模式,是连接权重自己算出来的,全脑模型里也一样。
  • 手写 · 有文献依据视觉前端(默认):按 Ache et al. 2019 Curr Biol,LC4 编码角速度、LPLC2 编码角尺寸。函数形式来自文献;参数为手选:LC4 = 0.5 Hz/(°/s),LPLC2 = 200 Hz × 高斯(中心 45°、宽 15°)。原文参数未能获取。可切回第一版(两者都用 dθ/dt)。
  • 手写运动映射:转向角速度 = 6°/s × (左 DNa − 右 DNa),依据 Rayshubskiy et al. 对 DNa02 的描述(哪侧活跃往哪侧转),左右以 FlyWire 注释的胞体侧为准。巨纤维 20 ms 平滑发放率超过 90 Hz 即起飞。
  • 真实数据 · 训练好的控制器 · 手写过渡飞行由 flybody 论文公开的训练好的飞行控制器(Vaxenburg et al. 2025 Nature,figshare trained-fly-policies/flight)在 MuJoCo 物理仿真里完成:控制器看着真实果蝇逃逸飞行的记录(Muijres et al. 2014 Science,只用原始轨迹)去跟飞,身体的位置和姿态是仿真结果,跟踪误差中位约 0.3 mm、机体朝向约 4–6°,左转那条在最后约 30 ms 偏离到 7 mm。翅膀按控制器的实际输出扇动:控制器设定拍翅频率(约 219–221 Hz),再给左右翅每个关节加一份修正力,翅膀真实角度由物理引擎算出,左右不再完全同步(翻滚角左右相关 0.52–0.59)。起飞和降落仍是手写过渡(起飞 20 ms 抬升 5 mm、结束后 50 ms 线性降落):没有找到可公开下载的果蝇起跳动作数据,Card 实验室 FlyPEZ 的起跳跟踪数据需向作者申请。起飞方向按 Card & Dickinson 2008 设为背离威胁,威胁在左就选右转轨迹。飞行不受连接组控制。
  • 连接组敲真实神经元:这一排开关是虚拟敲除筛选(报告 §14–24,24,250 段全脑仿真)选出来的真实神经元,不是功能分组。切断它们的传出突触与全脑筛选用的是同一套语义。 开关上标的两个数是同一个神经元在糖通路和水通路里的比值,在这个子回路里实测(node dodge/measure_neurons.js:颗粒钉在头前 1.3 mm 持续接触 → 味觉神经元 200 Hz,3 个种子,2.5 s 取后 1.25 s 平均);悬停可以看到全脑的对应值(python dodge/measure_neurons_fullbrain.py,138,639 个神经元,同样一起敲 + 零参照归一化 + 6 个实现)。 但必须说清楚:这个子回路只有 4,599 个神经元(全脑的 3%),单个神经元上会有明显失真。差别最大的是 CB0883——全脑里它是“单独敲 0.76、配上 Clavicle/G2N-1 才塌到 0.24”的枢纽,子回路里完全没作用(糖 ×1.07、水 ×1.00),配对也只是可加,因为它依赖的旁路在裁剪时被切掉了。反过来,Clavicle + G2N-1 在子回路里仍是协同(各自 0.91 / 0.67,一起 0.50,独立预期 0.61)。 最值得看的是糖和水的反差:Clavicle 对糖几乎没影响(×0.91)、对水直接归零(×0.01);CB0051 同样(×0.80 / ×0.02);G2N-1 反过来只管糖(×0.67 / ×1.07);Phantom 是抑制性的,敲掉它水通路的 MN9 反而翻倍(×2.06)。这正是报告 §21 那个结论在游戏里的样子——同一个伸喙动作,背后是两套线路。 把这 10 个神经元在全脑和子回路里的比值整体对照【探索性,n = 10】:糖 Pearson +0.75、水 +0.89,子回路整体上重现了全脑的敲除模式,水那一侧更贴近一些。最漂亮的一行还是 Phantom:敲掉它,全脑水通路的 MN9 变 3.02 倍、子回路 2.06 倍,方向和量级都对上了。 (这一格先前写的是“糖 +0.21、水 +0.92,子回路只重现水通路”,是错的:糖用了未归一化的值、又只取了 6 个有命名的类型;统一口径并补全 10 个后糖升到 +0.75。)
  • 连接组 · 半起风:游戏里的 JO 输入组正是 JO-C/E 亚型——风觉 / 重力觉神经元(Yorozu et al. 2009),也正是“果蝇说中文”实验里“风”对应的那群。所以“起风 → 这群神经元放电 → 下游读出”这条链是连接组算的;风速到放电频率的换算、触角迎风几何、风把果蝇吹偏的力都是手写的(没有该模型下的定标数据)。
  • 手写阳光:只按手写规则调节视觉前端增益(暗 → 对比度低 → 逼近检测弱,最低降到 35%)与画面明暗。直接刺激光感受器在这个 LIF 模型里驱动不了下行神经元(实测),所以没有假装亮度是连接组算出来的。
  • 连接组 · 半“果蝇在想什么”气泡:触发每个词的量是真实的——甜 / 苦 / 风 / 痒是当前注入那些感觉神经元的频率,吃 / 梳理 / 后退 / 飞是读出神经元的平滑发放率;但词的阈值与中文句子模板是手写的。这是报告第 13 节的简化版:那里用 121,706 个神经元 + 岭回归解码 12 个词,这里子回路只有 4,599 个神经元、也没有醋 / 霉味 / 温湿度那些通路,所以只能用游戏里真实存在的这几路输入,且用阈值而非回归。
  • 显示行走腿部动作是 FlyGym 物理仿真录下的 25 Hz 步态周期循环;果蝇模型显示放大 2 倍,碰撞仍按真实尺寸(体半径 1.4 mm、球半径 2.5 mm)计算。
  • 许可行走身体 NeuroMechFly/FlyGym(Apache-2.0);飞行身体 flybody(Apache-2.0);飞行轨迹、拍翅模式与训练好的飞行控制器来自 flybody 的 figshare 数据集(GPL-3.0+);连接组与细胞类型为 FlyWire v783(按非商用处理)。