尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用Besiege打造机械三进制全加器:三态逻辑的物理实现

用Besiege打造机械三进制全加器:三态逻辑的物理实现 三进制计算机可以说是计算机史上一个反复被提起、又始终没有真正进入主流的话题。各大论坛上讨论三进制原理的帖子不少但真正动手把三进制逻辑做成实物的人非常少原因倒不难理解三进制逻辑门、三态存储、三进制进位——这些东西在真实电路里做起来既贵又没必要在普通编程环境里又只能停在模拟层。但如果告诉你在一个物理沙盒游戏里就能搭出“能跑的”机械三进制全加器而且确实能看到它在做三进制加法这个门槛就被拉到了大多数玩家都能尝试的位置。本文要记录的正是在 Besiege 中搭建机械三进制计算机、并完成三进制全加器模块的部分开发过程。先说判断在 Besiege 里做三进制全加器真正难的从来不是“三进制比二进制多一个状态”这个概念而是把数学上的三态逻辑翻译成物理世界里的位移、碰撞、弹簧回程和结构稳定。三进制计算机想要从图纸变成能运行的机械核心是解决“第三个状态如何被物理区分”和“机械逻辑门如何级联成组合逻辑”这两件事。读完这篇文章你会掌握三进制全加器的完整原理与真值表推导也会看到一套可落地的 Besiege 机械实现方案、调试路线和辅助验证脚本。整个过程不是照着电路图抄一遍而是把抽象逻辑重新发明一遍。1. 为什么要在 Besiege 里做三进制全加器1.1 三进制不是“被淘汰的古代技术”很多第一次听说三进制计算机的人第一反应是三进制不是早就被二进制淘汰了吗这里其实有个误区。苏联上世纪五十年代研制过实验性的三进制计算机采用的是平衡三进制也就是数字位可以是 -1、0、1 三种状态。这种表示法在数值运算上有一些很有意思的性质比如负数不需要单独的符号位加减法天然可以对称处理。从信息密度角度看三进制每一位的信息量是 log₂3 ≈ 1.585 比特也就是同样数量的位数三进制能表达的信息比二进制多 58.5%。问题在于真实硬件上实现一个可靠的三态电路比如区分 -1V、0V、1V 三个电平性价比远不如把两个二进制电平做得又快又准。所以三进制不是“错了”而是“贵了”。但 Besiege 这个环境不一样它的成本模型不是晶圆面积和光刻精度而是木块、弹簧、铰链和物理引擎的碰撞计算。在这里做三态和不做三态的成本差异小得多因此它成了验证三进制逻辑的绝佳试验场。更重要的是在 Besiege 里做三进制机械逻辑你要面对的是真实的重力、摩擦、行程和延迟这些约束和真实数字电路设计非常相似。1.2 它解决了哪些“纯展示”解决不了的问题网上关于三进制计算机的展示大多数有两种一种是 PPT 式的概念图画一个三态缓冲器符号讲几句平衡三进制的优点另一种是软件模拟用 Python 或 Verilog 跑一个三进制加法器输出结果对比二进制。这两种方式都能说明“三进制可行”但都漏掉了最核心的问题——三态信号在物理上怎么产生、怎么传递、怎么稳定识别。在 Besiege 里做机械全加器本质上是在做一个物理层实现。你需要回答-1、0、1 分别对应机械结构里的什么位置状态两根信号杆怎么组合出第三个状态进位信号在机械上怎么从一个模块传递到另一个模块这些问题一旦想清楚再回头看电路图全加器整个抽象符号就变得非常具体了。所以这个项目最大的价值不是“在游戏里做了个稀奇玩意儿”而是提供了一条从三进制数学概念到三进制物理实现的完整路径。对于学习数字逻辑的开发者来说它的教学价值不亚于用手搭一个真实二进制加法器。1.3 什么样的读者最适合关注这个项目如果你是正在学数字逻辑课程、看到加法器真值表就头疼的学生这篇文章会给三进制全加器电路图之外的真实物理视角如果你本身就是 Besiege 的玩家想在游戏里建造复杂的逻辑机械这台三进制机器是一个比“做个投石机”高阶得多的进阶目标如果你是硬件爱好者对平衡三进制算法感兴趣但又没有条件流片在物理沙盒里验证逻辑也是性价比极高的入门路径。2. 三进制计算机的基础平衡三进制与三态逻辑2.1 三个状态分别代表什么三进制数字系统使用 0、1、2 三位状态是最直觉的思路但历史上的三进制计算机采用的是平衡三进制状态集是 {-1, 0, 1}。在书写时通常把 -1 记作 T比如十进制数字 2 在平衡三进制里写作 1T因为 1×3 (-1) 2十进制数字 5 写作 1TT因为 9 - 3 - 1 5。这个表示法的好处在加减法里体现得最明显正数和负数共用同一套运算规则不需要单独设计减法器。比如 1T 1T即 2 2 4在平衡三进制中结果是 11也就是 1×3 1 4。整个运算过程中没有任何“借位”的特殊分支只有向高位的进位。2.2 三进制的进位为什么有 -1二进制加法器里进位只有 0 和 1 两种可能因为任何两个二进制位相加和最大是 112超过 1 才产生进位。平衡三进制不是这样两个三进制位相加取值范围是 -2 到 2。当结果是 -2 时本位应该输出 1同时向高一位进位 -1当结果是 2 时本位应该输出 -1同时向高一位进位 1。举个直观例子1 1 2而平衡三进制里 2 1T所以本位和是 -1进位是 1。再比如 (-1) (-1) -2-2 在平衡三进制里写作 T1本位和是 1进位是 -1。这意味着三进制全加器的进位输出不是一个简单的“是否大于等于某值”的判断而是一个同样包含 -1、0、1 三态的信号。这个特性决定了机械实现时进位链不能做成简单的开关节流需要能够传递一个反向的复位信号。2.3 真的三进制与“模拟三进制”的分界线很多号称三进制的演示底层其实还是用多个二进制位去编码三态这种做法严格说只是“三进制接口二进制内核”。真正意义上的三进制计算机应该是信号通路、逻辑门、存储单元都物理支持三种状态。在 Besiege 这个项目里判断是否“真三进制”的标准很简单机械结构是否真的有三种可稳定区分的物理状态。如果只是用两根杆的伸出和缩回组合出 00、01、10 三种编码那本质上是并行二进制如果一根杆本身有三个稳定的行程位置那就是真正的物理三态。项目中两套方案都值得尝试但为了让初学者更容易上手下面会以“双轨编码”作为起步方案因为调试更直观、逻辑门实现更容易再利用三态判定层来确保最终运算符合三进制全加器语义。3. Besiege 机械逻辑与三态信号编码方案3.1 Besiege 里的逻辑门是怎么做出来的把 Besiege 当电子设计自动化工具用之前需要先把游戏内的物理组件和数字逻辑概念对应起来。Besiege 中有铰链、弹簧、活塞、压力板、球关节等组件它们可以构成基本的布尔逻辑。一个简单的“与门”可以这样想两根输入杆必须同时推动到位输出杆才会被推到终点一个“或门”则让两根输入杆共同推动同一根输出杆任何一路到位都能把输出推过去“非门”则利用一个常闭挡块输入杆不推时输出杆在弹簧作用下保持伸出输入杆推入后反而把输出杆释放。机械逻辑与电子逻辑有一个本质区别电子信号的传播几乎是瞬时的而机械逻辑天然有延迟、有回程、有抖动。Besiege 的物理引擎还会引入随机性两个结构完全相同的装置多次运行后的位移可能不完全一致。因此在设计逻辑模块时需要给每个信号留出足够的行程余量否则输入组合明明正确输出却会在边界处抖动。3.2 机械逻辑的编码设计三种可选方案要在机械世界里表示 -1、0、1 三个状态常见方案有三种各有取舍方案物理实现优点缺点双轨编码用两根信号杆表示两个二进制位组合成三态调试直观、逻辑门实现简单本质是二进制的并行编码需要额外排除非法状态单热编码用三根信号杆分别表示三个状态的“激活位”状态清晰、抗干扰强需要的信号杆数量多、机械体积大角度位置编码用一根杆的铰链旋转角度区分三态真正意义上的物理三态、节省空间角度识别困难对物理引擎的精度要求高3.3 为什么第一阶段选择双轨编码考虑到机械全加器需要级联、需要反复调试第一阶段选择了双轨编码两根信号杆 A、B 分别用“缩回/伸出”表示 0/1组合对应三态。编码表如下状态A 轨B 轨-110000101非法11这里有个关键细节双轨编码天然存在一个非法状态 (1,1)必须在编码输入端做机械互锁避免两根轨同时伸出。一旦出现非法状态后续逻辑门的判定就会混乱而且排查起来非常隐蔽因为它不是随机故障而是编码器本身的物理约束没做够。单热编码虽然体积大但好处是每个状态独立不需要排除非法组合。如果后续做更大的位宽或者更高速的机械逻辑角度位置编码更接近“真三进制”的物理本质但它的识别机构复杂本项目第一阶段没有采用。4. 三进制全加器原理与真值表4.1 全加器到底在算什么一位全加器接收三个输入当前位的被加数 A、加数 B以及来自低位的进位 Cin输出当前位的和 Sum 与向高位的进位 Cout。与二进制全加器不同的是三进制全加器的每个输入都是 -1、0、1 三态之一所以 A、B、Cin 一共会产生 27 种组合输出 Sum 与 Cout 也都是三态。很多人第一次看到三进制真值表时会犯一个思维惯性错误以为进位只有“0/1”两态。实际上平衡三进制的进位是三态的因为两个三进制位相加可能产生负向溢出。这个特性在机械实现里非常重要进位链需要能传递“负进位”也就是反方向的信号。4.2 三进制全加器的核心规则设 raw A B Cin那么 Sum 和 Cout 的映射如下raw 值Sum 输出Cout 输出-30-1-21-1-1-100001102-11301这个规则和二进制全加器的本质逻辑完全一致先求总和超出当前位表示范围的部分向高位移余下的留在这个位。区别在于二进制“超出范围”的判断是 raw ≥ 2而平衡三进制是 raw 1 或 raw -1且超出量本身可能是正也可能是负。验证几个例子A1, B1, Cin1 时raw3Sum0Cout1表示十进制 3“A-1, B-1, Cin-1”时raw-3Sum0Cout-1在级联加法器里这个负进位会传给更高位最终把负数正确表示出来。4.3 为什么连续进位是三进制最容易出错的地方二进制加法里进位最多连续传递几位理论上可以很多位比如 1111 0001 这样的模式。三进制也一样但容易出错的原因是进位不仅是“ 1 ”还可能是“ -1 ”。级联时高位模块必须能接受来自低位的三态进位而不是只接一根“有进位/无进位”的杆。这意味着在 Besiege 机械实现里进位信号需要和 A、B 输入使用同一套信号标准和行程规格。否则低位的 Cout 传到高位的 Cin 时如果行程衰减高位模块就会把“ 1 ”误判为“ 0 ”整个加法结果瞬间错乱。这个设计要点比优化任何一个具体逻辑门的结构都重要。5. 逻辑门到机械结构的映射设计5.1 基本逻辑门的机械实现思路在 Besiege 中构建逻辑门核心是理解“位移即信号”。下表是项目里采用的逻辑门到机械结构的映射参考逻辑门机械实现思路注意事项与门两根输入杆串联推动同一输出杆两路都必须到位才会触发输入杆的行程必须一致否则一路到位另一路不够时会输出 0或门两根输入杆并联共用同一个输出杆的推动点需要防止两路同时推动时输出杆受力不平衡非门常闭挡块结构输入杆推到后释放输出杆弹簧力度需要调稳否则回程不到位三态判定门用两根输入杆的组合去启用对应输出通道核心是排除非法组合加机械互锁这些门组合起来可以表示任何一种组合逻辑因为任何逻辑函数都能用与或非三种门的组合来表达。5.2 从布尔逻辑到三态逻辑的组合设计三进制全加器的求和逻辑实际上可以拆成多个布尔子逻辑判断 raw 是否等于 -3、-2、-1、0、1、2、3。在机械结构里每个判断子结果可以驱动一根专用的输出杆再通过或门汇总到 Sum 输出通道。为什么强调“每个子结果独立驱动”因为如果试图用一根杆直接映射 raw 的大小机械结构会极其复杂而且不同 raw 区间需要多级比较物理误差会累积。拆成独立判断之后每个判断器的职责单一调试时可以用手动推动输入杆的方式逐组验证出问题时只需要查对应子模块。5.3 模块化设计原则机械逻辑和软件代码一样最忌讳把所有功能堆在一块大结构里。项目里采用的模块化原则是输入模块负责把 A、B、Cin 的物理位置转成统一规格的信号杆判定模块负责根据输入组合拉出对应的结果杆进位模块负责把 Cout 信号转成下一级需要的进位杆规格输出模块负责把结果杆映射到清晰的显示位置上。每个模块之间用标准接口连接接口的定义就是“信号杆的行程长度、方向、触发阈值”。这样在调试时可以单独拆下任何一级模块用人力推动信号杆来模拟输入而不是每次都启动整台机器观察。6. 全加器机械实现与布线规划6.1 整体模块划分机械全加器可以划分为五个部分输入区、编码区、求和判定区、进位生成区、输出显示区。输入区接收外部设置的三态信号编码区把三态转换成双轨信号求和判定区根据编码结果按真值表生成求和结果进位生成区生成进位信号输出显示区把 Sum 和 Cout 转换成玩家可视的指示结构。模块作用关键接口输入区设置 A、B、Cin 三态三根输入杆每根有 3 个稳定位置编码区把三态转成双轨编码两根输出杆行程 0/1求和判定区根据双轨编码判断 raw 区间输出 Sum 对应的双轨信号进位生成区生成 Cout 三态信号输出一根可表示 -1/0/1 的进位杆输出显示区可视化最终结果颜色块或可读位置6.2 布线规划与物理约束Besiege 的物理引擎让“布线”这个词多了不少工程含义。规划信号杆路径时下面几个约束非常关键一是信号杆路径尽量短避免长距离推动杆带来的弯曲和弹性形变。二是信号杆交叉越多越容易发生意外碰撞建议优先用上下铺层的方式来替代交叉。三是电源和信号分离Besiege 里没有真正意义上的电源但驱动逻辑门的动力源和输入信号杆如果挨得太近动力源的震动会影响信号组件的位置判定。四是要为每根输出杆预留足够的回程空间不能让弹簧压缩到极限否则长时间运行后输出会漂移。6.3 建造顺序建议不要一上来就照着电路图全加器搭全量结构。建议按下面的顺序推进先做输入区和编码区验证三态到双轨编码的转换是可靠的再做求和判定区中最简单的一条路径比如 A0、B0、Cin0 时的输出是否为 0然后逐步加入其它 raw 区间每加一个分支就重新跑一遍全部 27 组输入组合最后再做进位生成区和输出显示区。这个顺序的核心思路是“每增加一个逻辑分支都保证整机仍能通过全部真值表测试”。如果跳到全量搭建再排错面对一个纠缠成一团的大型机械结构排查效率会低很多。7. 开发调试记录从半加器到全加器7.1 先做半加器再做全加器项目调试过程中最值得分享的经验是先做一个三进制半加器也就是不含进位输入的 AB 加法模块验证三进制求和的基本规则。半加器没有进位输入只有 9 组输入组合很容易手工逐组测试而且半加器的进位生成逻辑和全加器几乎一致做好半加器之后全加器只是多接一根进位输入杆。半加器的验证标准比较具体A1、B1 时 Sum-1、Cout1A-1、B-1 时 Sum1、Cout-1。如果这两个关键组合不过关说明进位逻辑的处理有问题这是全加器里最容易被忽略的隐含需求。7.2 调试时如何快速定位故障模块机械逻辑排错和软件排错有一个通用的方法二分定位。先断开进位链路单独测试求和判定区如果求和输出正确再单独测试进位生成区如果进位输出不正确就检查进位生成区的输入信号杆是否与求和判定区的标准一致。这样每次只需要排查一个模块。Besiege 物理引擎的一个麻烦点在于结构在运行几次之后可能因为累积位移产生微小的位置偏移。因此调试时不要只测一次建议每组组合至少运行三次如果三次结果不一致大概率是某个信号杆的行程余量不足而不是逻辑错误。7.3 使用测试用例清单逐项对照在机械结构调试过程中手工逐一设置输入会很疲惫而且容易漏项。项目维护了一份 27 行的测试用例清单把 A、B、Cin 三态的每一种组合与预期的 Sum、Cout 列出来。这份清单不只是给机械调试用的也是下面会提到的 Python 辅助脚本的输出依据。实际的调试现象往往很有趣有时候机械结构看起来“逻辑正确”但某一个输入组合总是不稳定。经排查发现那是一根用于传递进位信号的杆太长中间发生了轻微弯曲导致高位模块收到的是半个行程的信号。削减杆的长度并增加一根支撑梁后问题消失。这类问题在图纸上看不出来只有真实物理环境才会暴露。8. 用 Python 脚本验证机械逻辑的正确性8.1 为什么需要“数字孪生”验证Besiege 机械结构的调试成本不低每次改动结构、重新瞄准、运行都要时间。因此项目先把三进制全加器的逻辑在 Python 里彻底验证清楚生成一份“标准答案”再对照机械结构的实际输出。这样物理部分只需要解决“结构能不能稳定复现逻辑”不需要再怀疑逻辑本身有没有问题。下面这个脚本用平衡三进制完成全加器真值表验证可以直接运行TRITS [-1, 0, 1] def balanced_add_with_carry(a, b, carry_in): raw a b carry_in if raw -3: return 0, -1 elif raw -2: return 1, -1 elif raw -1: return -1, 0 elif raw 0: return 0, 0 elif raw 1: return 1, 0 elif raw 2: return -1, 1 elif raw 3: return 0, 1 else: raise ValueError(fUnexpected raw value: {raw}) def full_trit_adder(a, b, carry_in): return balanced_add_with_carry(a, b, carry_in) print(A B Cin | Sum Cout) for a in TRITS: for b in TRITS: for carry_in in TRITS: sum_val, carry_out full_trit_adder(a, b, carry_in) print(f{a:2d} {b:2d} {carry_in:2d} | {sum_val:3d} {carry_out:3d})运行后会输出完整的 27 行结果这份表格就是机械结构调试时逐项对照的依据。脚本本身也包含了对 raw 合法性的检查如果计算过程中出现超出 -3 到 3 的值会立即报错这对应到机械结构上就是非法状态。8.2 用脚本生成机械测试用例人工站在 Besiege 前对着真值表反复拨动输入杆容易漏项。项目写了一个测试用例生成脚本把所有输入组合以 JSON 文件的形式输出可以打印成纸质清单也可以直接导入到复盘记录中import json TRITS [-1, 0, 1] cases [] for a in TRITS: for b in TRITS: for carry_in in TRITS: cases.append({ a: a, b: b, carry_in: carry_in }) with open(trit_adder_test_cases.json, w, encodingutf-8) as f: json.dump(cases, f, indent2, ensure_asciiFalse) print(fGenerated {len(cases)} test cases.) print(json.dumps(cases[:3], indent2, ensure_asciiFalse))这个脚本不是必须的但它可以让机械调试变成“对着清单打勾”的机械工作减少遗漏。对一个需要逐组验证 27 种组合的机械项目来说这种工具价值很高。8.3 双轨编码状态检查双轨编码方案中有一个非法状态 (1,1)。机械互锁结构在理想情况下不会产生这个状态但物理引擎跑久之后结构可能变形导致两根轨意外同时伸出。因此项目额外写了一个编码检查函数用于在调试日志中快速标记非法状态def decode_dual_rail(rail_a, rail_b): 输入两根轨的状态0 表示缩回1 表示伸出 返回三态值 -1/0/1非法组合返回 None pair (rail_a, rail_b) mapping { (0, 1): 1, (0, 0): 0, (1, 0): -1, } result mapping.get(pair) if result is None: print(f非法双轨编码A{rail_a}, B{rail_b}请检查编码器互锁) return result print(decode_dual_rail(0, 1)) # 1 print(decode_dual_rail(0, 0)) # 0 print(decode_dual_rail(1, 1)) # None非法状态把这段代码放到项目的调试工具目录里每次机械测试完可以把实测的轨状态录入脚本快速判断是否有非法编码混入逻辑链。9. 常见问题与排查清单问题现象可能原因排查方式解决方案某些输入组合输出总是不稳定信号杆行程余量不足多次运行同一组合观察输出杆是否在边界抖动加长信号杆的推程或增加弹簧限位输出杆无法复位弹簧力度不够或摩擦力过大手动推动输出杆测试回程换更大弹力弹簧或减少接触面摩擦出现非法双轨状态 11编码器缺少机械互锁运行多组输入观察两根轨的状态增加互锁结构让两轨无法同时伸出进位信号传到高位后丢失进位杆太长发生弯曲检查进位信号杆的直线度与支撑缩短进位杆增加支撑梁同一输入组合结果不一致结构发生微位移或部件碰撞录制运行录像逐帧观察增加固定结构检查路径是否被阻挡逻辑结果和真值表总差一两项某个判定子模块的物理阈值偏移单独测试该子模块比较行程规格统一所有信号杆的行程与触发阈值这套排查清单几乎覆盖了项目调试过程中遇到的所有主要问题。其中“统一信号杆的行程与触发阈值”这条是机械逻辑项目里最容易忽视但影响最大的工程决策。10. 工程建议与后续扩展方向10.1 从本项目沉淀的工程建议机械逻辑项目与软件项目的工程管理很相似但也有特有的注意事项。第一所有输入杆、输出杆的行程规格必须全局统一否则各模块之间无法可靠对接。第二每次修改结构之前保存一个可用的存档快照否则一次物理引擎的意外崩溃可能毁掉大量工作。第三优先完成一条“最简通路”并验证它再逐步增加逻辑分支这和软件的“垂直切片”思路一致。第四把 Python 脚本当作项目规格说明书任何逻辑层的修改先改脚本、跑通证据再动机械结构。10.2 未来可以继续深入的方向全加器只是三进制运算部件的第一步。后续可以尝试的方向包括把多个全加器级联成多位三进制加法器这一步需要解决的是进位链的长距离传输问题实现一个简单的三态锁存器为三进制存储模块打基础尝试把双轨编码升级为单热编码或角度位置编码对比不同编码方案在物理空间与可靠性上的差异。二进制机械计算机在玩家社区里已经有不少作品三进制机械计算机的路线图目前还远未被探索完。10.3 给同样在 Besiege 里做机械逻辑的玩家如果这篇文章能让你产生自己动手的冲动建议先别急着照搬全加器的完整结构。先做一个三态编码器再做一个三进制半加器把基础模块的可靠性打磨好剩下的只是耐心和组合。机械逻辑最有意思的地方就是它让抽象的真值表变成了看得见、摸得着的运动轨迹。如果你在做类似的结构欢迎在评论区分享你的进位链设计方案特别是如何处理负进位那段结构。三进制这条路自己在物理世界里走通一遍比看一百篇概念科普都要来得深刻。
返回列表