在 Besiege 里做机械三进制计算机,最容易产生“先试试”冲动的模块往往是全加器,因为它逻辑清晰、规模可控,又直接决定后续所有算术单元能否工作。真正动手之后会发现,三进制全加器不是简单换一张真值表,它要求整个信号系统从头设计:每一位信号如何表示,加法如何用机械结构完成,进位如何从连续旋转中提取出来,这三个问题在二进制场景里几乎不用考虑。
这里记录的是我在 Besiege 中搭建机械三进制全加器的部分开发过程。内容按“三态编码 -> 真值表 -> 机械结构 -> 测试台 -> 排错 -> 扩展”的顺序展开。如果你也在 Besiege 里做机械逻辑,或者想了解三进制计算机如何从一个算术单元开始落成实体,这份笔记可以作为一套可对照的实现方案。
1. 先想清楚:三进制机械计算机为什么从全加器开始
1.1 三进制在机械表达上的优势
二进制信号在机械世界里是最容易表达的:一个开关有开和关两个状态,一个轴有正转和停转两个状态。但二进制需要更多位数才能表达同样范围,机械零件数量会随位数增长而快速膨胀。
三进制每一位可以表达 0、1、2 三个状态。从信息密度看,三进制单位状态携带的信息量比二进制更高。更重要的是,三进制允许出现“无信号、正向信号、反向信号”三种语义,这和齿轮的正转、停转、反转天然对应。机械结构不擅长区分“符号位”,但很擅长直接反转运动方向,所以三态在物理层面反而比二进制更自然。
1.2 全加器是算术后端的地基
无论二进制还是三进制,加法都是最基本的运算。乘法可以拆成多次加法,减法可以按补码或对称码转成加法,计数器、累加器、状态机也都依赖加法器。
在整台三进制计算机里,全加器承担的是“一位加法 + 进位”的核心职责。如果全加器不能在物理上稳定工作,后续每个模块都会继承同样的误差。先把这个单元跑通,等于验证了三态编码、齿轮传动、角度取模、进位提取四条关键链路,之后的模块只是把这些链路组合起来。
1.3 Besiege 做机械逻辑的适用性和边界
Besiege 适合做这件事,因为所有零件都受物理模拟约束,齿轮、铰链、活塞、结构件可以自由组合,不需要写驱动代码就能观察真实的机械行为。玩家可以通过暂停、逐帧观察、手动旋转输入轴等方式完成调试。
限制也很明显。物理模拟会引入齿轮间隙、结构抖动、零件弹性,齿轮比稍大一点,角度误差就会被放大。零件数量增加后,帧率会下降,卡死和抖动也更频繁。因此,游戏内机械计算机的每一级结构都要尽量短、尽量轻,尽量少用超长传动链。这类限制和真实机械计算机设计中的误差控制、公差分析很相似。
2. 用角度定义三态信号
2.1 三态角度编码:0 度、120 度、240 度
我采用角度作为三态信号的物理载体。普通三进制数值 0、1、2 分别映射到 0°、120°、240° 三个旋转角度:
| 三进制数值 | 普通三进制角度 | 平衡三进制角度 | 语义 |
|---|---|---|---|
| 0 | 0° | 0° | 零 |
| 1 | 120° | +120° | 正一 |
| 2 | 240° | -120° | 负一或二 |
选择 120° 间隔而不是 90° 或 180°,原因在于圆周 360° 被三等分后,三个状态之间距离最大,物理读数不容易发生歧义。如果使用 0°、90°、180°,状态间隔只有 90°,齿轮间隙稍大就会让输出落在两态之间。
不要用 90° 作为三态间隔。120° 间隔会在取模和进位环节留下更大的容错空间。
在平衡三进制中,240° 可以理解为 -120°,本质上是同一个角度。机械齿轮天然支持反转,所以负角度不需要额外符号位。
2.2 输入轴的构造和稳定方法
每个输入信号由一个旋转铰链实现。铰链转轴上固定一个细长结构件作为指针,指针指向刻度盘上的 0°、120° 或 240°,就表示当前输入为 0、1 或 2。
为了让输入角度稳定,我在指针位置附近设置了限位块。手动调整输入时,先暂停游戏,旋转指针到目标刻度附近,然后恢复物理模拟,让结构在限位块的约束下稳定下来。测试阶段建议让输入轴保持较短的力臂,减少旋转零件的转动惯量,避免指针因为惯性来回摆动。
部件清单可以参考下面的文本配置:
输入轴模块: 1. 旋转铰链 x1 2. 细长木块 x1,作为指针 3. 带刻度底座 x1,固定在铰链下方 4. 限位木块 x2 或 x3,卡住指针的稳定位置2.3 信号传递的接口约定
模块与模块之间的信号通过同轴齿轮或皮带传递。上游模块输出角度,下游模块读取同一角度,因此传动比必须保持 1:1,否则角度会被缩放,三态位置会整体偏离。
接口约定如下:
- 上游输出轴角度等于当前三进制值对应的角度。
- 下游读取轴必须与上游保持同一转向。
- 齿轮咬合方向影响正负号,搭完第一个齿轮组后要先做一次 0° 到 120° 的验证。
- 每一级传动链尽量只经过一个齿轮对,减少反冲间隙。
这段约定是整台机械计算机的通信协议。没有统一接口,后续每一层模块都要单独校零,排查成本会成倍增加。
3. 全加器的运算关系与真值表
3.1 从二进制全加器到三进制全加器
二进制全加器计算的是:
S = (A + B + Cin) mod 2 C = (A + B + Cin) div 2三进制全加器把模数和除数从 2 改成 3:
S = (A + B + Cin) mod 3 C = (A + B + Cin) div 3这里的 A、B、Cin 都在 0、1、2 中取值。要注意,三进制进位的范围不是只有 0 和 1,而是可能取 0、1、2。例如:
2 + 2 + 2 = 6 6 mod 3 = 0,S = 0 6 div 3 = 2,C = 2进位为 2,在二进制全加器里不存在。这正是三进制全加器比二进制复杂的核心原因。
3.2 普通三进制全加器的真值表
完整真值表有 27 行。为了避免手工表格过长,我用 Python 脚本生成完整结果,搭建完机械结构后可以直接拿这份输出做对照。
def ternary_full_adder(a, b, cin): total = a + b + cin return total % 3, total // 3 def angle(value): return [0, 120, 240][value] print("A B C S C_out") print("-" * 24) for a in range(3): for b in range(3): for cin in range(3): s, c = ternary_full_adder(a, b, cin) print(f"{angle(a):3d} {angle(b):3d} {angle(cin):3d} " f"{angle(s):3d} {angle(c):3d}")运行这段脚本需要本机安装 Python 3,保存为adder_truth_table.py后执行:
python3 adder_truth_table.py它会输出 27 行角度对照表,例如:
0 0 0 0 0 120 120 0 240 0 120 120 120 0 120 240 240 0 120 120 240 240 240 0 240中间若干行代表其它输入组合。机械测试时需要逐行对照,不能只挑几个看着顺眼的组合验证。
下面是小样本检查表,适合搭建过程中快速筛查:
| A | B | Cin | S | C_out |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 2 | 0 |
| 1 | 2 | 0 | 0 | 1 |
| 2 | 2 | 0 | 1 | 1 |
| 1 | 1 | 1 | 0 | 1 |
| 2 | 2 | 1 | 2 | 1 |
| 0 | 2 | 2 | 1 | 1 |
| 2 | 2 | 2 | 0 | 2 |
验证小表时注意,S 和 C_out 都是角度编码,不是十进制数值。比如2 + 2 + 1等于 5,三进制表示为12,因此 S 等于“1”对应的 120°,C_out 等于“1”对应的 120°。
3.3 从普通三进制推广到平衡三进制
普通三进制数值范围是 0 到 2,平衡三进制数值范围是 -1、0、1。平衡三进制的好处是负数不需要额外符号位,正反转天然由角度符号表达。
平衡三进制的全加器同样满足:
total = A + B + Cin S = total 除以 3 的平衡余数,范围 -1、0、1 C_out = total 除以 3 的平衡商,范围 -1、0、1可以用 Python 验证平衡三进制的计算规则:
def balanced_ternary_adder(a, b, cin): total = a + b + cin q = (total + 1) // 3 r = total - 3 * q return r, q for a in (-1, 0, 1): for b in (-1, 0, 1): for cin in (-1, 0, 1): s, c = balanced_ternary_adder(a, b, cin) if a + b + cin in (-3, -2, 2, 3): print(f"A={a:2d} B={b:2d} C={cin:2d} -> S={s:2d} C={c:2d}")运行结果会显示边界情况,例如:
A=-1 B=-1 C=-1 -> S= 0 C=-1 A=-1 B=-1 C= 0 -> S= 1 C=-1 A= 1 B= 1 C= 0 -> S=-1 C= 1 A= 1 B= 1 C= 1 -> S= 0 C= 1平衡三进制在机械上的实现并不需要额外加结构,因为旋转角度 120° 和 -120° 其实就是同一个物理位置。实际开发时可以把普通三进制当作第一版验证对象,跑通后再把角度编码整体平移成平衡三进制。
4. 机械全加器的第一版结构
4.1 总体思路:三个输入角度先求和
机械全加器不需要做任何“判断”,它只需要做三件事:把输入轴角度加起来,取 360° 余数输出本位,再提取走过的整圈数输出进位。
在机械世界,角度叠加的基本结构是齿轮差速结构。简单理解,两个输入齿轮带动同一个输出机构,输出角度等于两个输入角度按一定比例合成后的结果。调整齿轮比,可以让输出接近输入之和,也可以让输出等于输入之差。
第一版结构采用两级差速:
第一级:A + B -> T1 第二级:T1 + Cin -> T_totalT_total 的最大值是三个输入的整数和。当 A、B、Cin 都取 240° 时:
240° + 240° + 240° = 720°也就是说,总角度输出轴最多转两整圈。这个范围对应三进制最大加法结果 6。
实际搭建时,游戏里没有现成的“差速器”零件,需要使用齿轮和转轴组合模拟。第一版验证时不要求输出严格等于 A + B + Cin,只要输出随输入单调、且同一组输入每次读数一致,就可以通过刻度盘标定出真实映射。
4.2 Sum 输出:利用 360 度循环实现取模
总角度 T_total 是一个连续旋转角,但任何机械轴旋转到 360° 时都会回到 0°。因此,直接在 T_total 轴上接一个指针,指针读数天然就是 T_total mod 360°。
这个取模过程不需要额外零件。S 输出角度就是:
S = (A + B + Cin) mod 360°例如:
A=240°, B=240°, Cin=0° T_total=480° 指针读数=120°120° 对应三进制数值 1,而2 + 2 + 0的三进制结果是11,本位是 1,符合预期。
取模是免费的,因为旋转轴本身就是一个循环计数器。但这也意味着进位信息丢失了,必须通过另一条路径提取。
4.3 进位提取:减法加 3 比 1 减速
进位 C_out 可以通过 T_total 与 S 的差值得到。因为:
T_total - S = 360° × 进位圈数进位为 0 时,差值为 0°;进位为 1 时,差值为 360°;进位为 2 时,差值为 720°。
为了让进位也变成三态角度,需要把差值除以 3。360° 除以 3 等于 120°,720° 除以 3 等于 240°,正好对应三态进位角度。
机械上实现这一分段的过程是:
- 用第二级差速结构输出 T_total 和 S 的差值。
- 将差值轴经过一组 3:1 减速齿轮。
- 减速后的输出接到进位指针。
结构关系可以写成:
T_total - S -> 3:1 减速齿轮 -> C_out 指针第一版落地时,T_total 和 S 来自两条不同传动链,齿轮间隙会在减法阶段被放大。实测中进位角度出现 3° 到 5° 的偏移属于正常范围,偏差超过 15° 时就要检查传动链长度和齿轮咬合松紧。
4.4 关键参数速查
下表是搭建过程中最重要的参数:
| 参数 | 建议值 | 说明 |
|---|---|---|
| 输入角度 | 0°、120°、240° | 三态信号编码 |
| 输入范围 | 3 个输入轴 | A、B、Cin |
| T_total 最大角度 | 720° | 三个输入都为 240° 时 |
| Sum 输出范围 | 0° 到 360° | 物理轴自动取模 |
| 进位提取齿轮比 | 3:1 | 将差值角度转成三态进位 |
| 测试用例数 | 27 组 | 三输入全排列 |
| 允许误差 | 5° 以内 | 超过需要检查传动链 |
第一版不用追求零误差。先把“逻辑对”跑出来,再通过缩短传动链、增加限位、加固齿轮座来降低误差。
5. 搭建测试台并验证真值表
5.1 测试台的结构
检查全加器之前,我先搭了一个独立测试台。测试台负责提供稳定的输入源,并把输出固定到便于读数的位置。
测试台包含:
1. 三个输入轴,手动旋转,带刻度盘 2. 一个 Sum 输出轴,带指针 3. 一个 C_out 输出轴,带指针 4. 固定底座,连接所有模块输入轴使用旋转铰链,输出轴使用同轴齿轮引出。所有轴承座都用结构件加固,避免高负载下整个底座翻转。
测试台的摆放原则是输入区和输出区分离。输入在左侧,输出在右侧,中间是齿轮传动模块。这样