做了这么多年硬件,我始终对一件事耿耿于怀:每逢项目后期,大把时间被PCB布线吞掉。手动拉线结果可控,但枯燥又费人;传统自动布线器在简单双面板上确实省事,一旦遇到高速信号、差分对、BGA扇出,它的“自动”立刻变成“自动添堵”。前几天在Hacker News上看到CircuitPilot这个项目——用物理引擎(physics engine)给PCB做自动布线(auto-route),并且把LLM拉进来参与决策,第一反应是“又一个噱头”,点进去认真看了设计思路之后,我觉得这个方向值得好好拆一拆。
这篇文章我想聊清楚三件事:传统布线器到底死在哪儿、物理引擎凭什么能在这个场景里翻身、LLM在里面扮演的又是什么角色。不管你是硬件工程师、EDA工具开发者,还是做AI应用落地的朋友,这套“LLM解析约束 + 物理引擎生成路径”的组合,都有不少值得拿走的东西。
1. 先想想传统自动布线器到底死在哪
1.1 “迷宫搜索”的困局:走一步看一步
传统自动布线器的底层核心,绝大多数是迷宫布线(Lee算法)及其变体。它的思路很直白:把PCB板面划成网格,从信号源点开始一圈一圈往外扩散,碰到终点之后按来路回溯,于是得到一条最短路径。在只有一两个网络的年代,这个算法堪称完美——它保证了连通性,也保证了长度最优。
问题出在多网络协同布线的时候。这套算法本质上是“走一步看一步”,它只知道现在这个网络怎么走最短,完全不知道后面还有几十个网络等着排队。前面网络留下了“漂亮”的最短路径,很可能把后面网络的最佳通道彻底堵死。这就是为什么老式自动布线器在简单电路上表现尚可,一旦网络数量上来,布线结果就变成一团乱麻。
打个比方:迷宫算法像一个只看脚下的外卖小哥,他总能找到最短的楼间小路,但小路被他占了之后,身后的同行全得绕远路。整个配送系统最后谁都没效率。
1.2 真实PCB的约束根本不是“找路”
更重要的是,真实PCB的约束,远远不是“两点之间能不能连通”这么简单。一块量产板子上同时压着好几类规则:
- 高速差分对要求等长误差控制在±5 mil以内,且两条线必须尽量平行;
- 敏感时钟线要远离大电流开关电源区域,避免串扰;
- BGA扇出区要按特定逃逸方向布线,不能横冲直撞;
- 禁布区域(器件下方、螺丝孔附近、拼板边沿)物理上不可进入;
- 走线拐角要是45°或圆弧,不能出现锐角,否则生产良率受影响。
传统算法面对这些规则,标准做法是给网格单元赋权重:禁布区权重设无穷大,等长约束简化成“尽量走蛇形线”,差分对约束干脆留到后期人工调整。权重一多,计算量指数级膨胀,而且很多约束之间的优先级冲突,权重根本表达不了。到这一步,行业里大量布线工作仍然依赖人工,原因不是工程师守旧,而是工具真的接不住这些约束。
1.3 破局点:物理直觉正好针对“全局博弈”
物理世界有个很妙的特性:系统会自发趋向能量最低状态。你把一堆橡皮筋拴在几个桩子之间松手,它们会自动找到一个稳定的受力平衡位置——没有任何中央规划,但每个橡皮筋都恰到好处。这在形态上,天然就适合处理“多网络互相制约”的布线问题。
CircuitPilot让我眼前一亮的地方就在这里:它没有沿着“改进寻路算法”的老路走,而是换了个赛道,把布线问题构造成一个物理模拟场景。禁布区是斥力源,目标引脚是引力源,走线是一串带弹簧连接的质点,规则被翻译成各种力。系统迭代之后收敛出来的结果,就是满足约束的走线路径。这个思路不是“找一个解”,而是“让系统松弛出一个解”,天然具备全局视野。
2. 物理引擎如何把“布线规则”翻译成“受力运动”
2.1 从网格搜索到势场梯度:走线变成“下山”的过程
理解了方向,接下来看具体怎么做。假设我们要在板面上布一条从A点到B点的线,物理引擎的做法不是搜索路径,而是建立一个势能场。目标点B处放一个“引力谷底”,板框边界、器件占位、禁布区都作为“斥力山峰”。然后我们模拟一个粒子从A点出发,粒子受到合力 F = -∇U 驱动,这里的 U 就是坐标点上的势能值。粒子沿着合力方向不断移动,最终滑进目标谷底。
用公式表达,每次迭代可以近似为:
- 粒子所受合力 F = F_attract + F_repulse + F_spring + F_damp
- 速度更新 v_{t+1} = v_t + (F / m) * dt
- 位置更新 x_{t+1} = x_t + v_{t+1} * dt
这一步的本质是把“路径搜索”变成“路径松弛”。粒子不需要知道整个地图的拓扑结构,它只感知周围梯度,但多个粒子同时在场上运动时,力场会逼着它们自动互相避让。传统迷宫算法是单线程思维,势场法是全局并行博弈,这是两代方法论的分水岭。
2.2 质点-弹簧-斥力:用“橡皮筋”表达等长和间距
势场解决了“避障寻路”的问题,但等长、间距这类约束还得另想办法。物理引擎在这里的杀手锏是质点-弹簧系统。
做法是把一条走线离散成一条“质点链”:一串小质点,相邻质点之间用弹簧连接。弹簧会尽量保持自然长度,所以质点链天然有“拉直”的倾向。这正好对应了“走线不要乱弯”的设计习惯。在此基础上:
- 等长约束:把需要等长的几条网络,加载弹簧约束。物理引擎里有专门的弹簧-阻尼器,可以让两条质点链的总长度被一个虚拟弹簧拉齐,误差收敛到给定阈值。
- 差分对间距:两条并行的质点链之间加入横向弹簧,距离太远拉回来,距离太近推开,保证两线始终平行且间距一致。
- 禁布区和器件占位:每个障碍物都被填充成斥力源。距离越近排斥力越大,理想情况下按 1/d² 增强。这就是物理引擎版的“敬畏之心”。
我在实际推演中比较喜欢用“橡皮筋图钉”来给非物理背景的朋友解释:把板子当成一块钉满图钉的软木板,图钉是障碍物,然后用橡皮筋把各个引脚之间连起来,松手后橡皮筋会绕过图钉、自动找平衡。物理引擎做的事,就是把这张“橡皮筋平衡图”高速迭代出来。
2.3 引擎选型不是越重越好
既然叫物理引擎,很多人第一反应是Box2D、Chipmunk、PhysX这类现成库。但真实做下来会发现,直接套用刚体引擎并不顺手。原因很简单:刚体引擎的设计目标是模拟不可变形的物体(刚体),而走线是柔性体,你需要的是质点、弹簧、阻尼器这套“软体”模拟能力,外加高效的碰撞检测。Box2D虽然有弹簧关节,但是大量质点链的稳定性、不同约束的优先级管理,做起来非常别扭。
更合理的路径是自己实现一个轻量的质点-弹簧系统,或者只借用刚体引擎中的宽相位碰撞检测部分,核心的势场求导和弹簧解算自己写。理由有三点:
- 布线求解需要精确控制每个质点的受力权重,自研解算器可以针对“等长”“间距”这类指标直接做归一化权重,现成引擎里没人给你留这个口子;
- 物理引擎的迭代步长、阻尼系数在游戏里追求“看起来真实”,在布线里要的是“快速收敛且不反弹”,两者参数哲学完全不同;
- 布线场景动辄上千质点,现成引擎的通用求解器为了鲁棒会牺牲速度,自研系统反而可以做空间哈希加速,只计算邻近质点之间的力,复杂度从O(N²)降到O(N)。
所以看到“We built a physics engine”这句话,我一点都不意外。这个场景,确实值得自己造一个专属引擎。
3. LLM在里面不是画线的,而是当翻译和指挥官
3.1 把坐标交给LLM是灾难
外行看到“用LLM做PCB布线”,第一反应往往是“让AI生成坐标路径”。凡是做过工程落地的人,听到这个想法就头大。LLM的数学能力、绝对坐标精度、复现稳定性,全都不适合干这种像素级精确的几何活。让它生成“从(100,200)绕到(350, 180)再到(500, 320)”这种路径,大概率结果是一堆非法几何图形,而且每次跑结果都不一样,完全不可控。
CircuitPilot如果真走这条路,根本撑不到在Hacker News上发布。它真正的聪明之处,是把LLM放在了“约束理解与策略决策”层——LLM不直接造几何,它分析人话、识别规则、生成结构化配置,再由确定性代码编译成物理引擎的参数。这个分工是非常清晰的。
3.2 一条自然语言约束的完整“翻译”过程
举个例子,你在板上看到U3和U4之间的差分对信号质量不行,想让它重新布,于是输入:
“U3和U4这对差分信号,走顶层,等长误差控制在±5 mil,尽量别靠近时钟振荡器CK1。”
LLM要做的,是把这句话拆成结构化指令。合理的设计是输出类似这样的JSON:
{ "nets": ["DIFF_P", "DIFF_N"], "layer": "TOP", "rules": [ {"type": "length_match", "tolerance_mil": 5}, {"type": "clearance_from_obstacle", "ref": "CK1", "clearance_mil": 25} ], "optimize": "length" }这段JSON再由规则引擎编译成物理场景参数:
- 从网表里找出DIFF_P和DIFF_N的起点终点坐标,生成两条平行质点链;
- 加载等长弹簧,误差设定在±5 mil,对应弹簧刚度系数取一个经过标定的值;
- 把CK1器件转化为半径25 mil的斥力源,周围走线自动绕开;
- 优化目标设为“长度最短”,对应引力势场的全局权重调高。
这一整套流程,LLM只负责“听懂人话并翻译”,真正干苦力活的是物理引擎。这和我做AI辅助工具的经验完全一致:语义理解是LLM的强项,数值计算和几何求解是传统算法的强项,两者结合,才能做出能落地的产品。
3.3 LLM会失误,所以必须配“规则校验层”
接入LLM之后,很快就会遇到它的三大经典故障:幻觉、漏读、误读。幻觉是它编造不存在的网络名或器件编号;漏读是它忽略了你明显提到的某个约束;误读则是把单位搞错(mil和mm混为一谈)或者把约束对象张冠李戴。
解决思路不能靠“让LLM更努力”,而要在LLM和物理引擎之间加一个确定性校验层。所有解析出来的规则,先跑一遍合法性检查:
- 引用的网络名、封装位号是否真实存在于网表/板文件中;
- 单位是否统一,数值范围是否物理合理(比如等长误差5 mil合理,500 mil就要报警);
- 不同规则之间是否冲突(比如同时要求严格等长和绝对最短,两者本身存在矛盾,需要提示用户做优先级选择)。
校验不通过,就直接打回让LLM重新解析,或者交给用户确认。这一步看似笨拙,却是整个系统稳定的基石。我在做类似AI辅助工具时的经验是:LLM给出的是“建议”,校验器给出的才是“约束”,两者缺一不可。
4. 工程实现管线与几个埋雷点
4.1 从板文件到物理场景的完整数据流
理解了角色分工,再看整条工程管线。完整的流程大致如下:
- 输入解析:读取EDA文件(KiCad的.brd/.kicad_pcb、Specctra DSN、Gerber等),解析出板框多边形、器件占位、网络连接关系、已有铜皮。
- 规则理解:LLM接收自然语言约束,输出结构化JSON规则。
- 校验编译:校验JSON规则,编译成物理引擎可执行的“势场+弹簧+阻尼”配置。
- 场景构建:把板框转为碰撞边界,器件占位转为圆形/矩形斥力体,禁布区转为斥力场源,每个待布线网络生成初始质点链。
- 物理求解:迭代运行力的计算和位置的更新,直到收敛或达到最大迭代次数。
- 后处理:对质点链做合法性过滤、拐角角度规整(把任意角度修正为45°/90°)、等长误差复核,最终输出标准EDA可导入的走线数据。
这套流程和传统自动布线器最大的不同在于:传统工具是“用户给一个笼统的目标,程序按内置规则办”;CircuitPilot是“人类用自然语言描述意图,系统自动翻译成物理约束再求解”。两种模式在灵活性上完全不在一个层级。
4.2 核心求解循环伪代码
物理求解是管线的心脏。我把核心循环简化出来,方便理解:
for iteration in range(max_iterations): for each_particle in chain: # 1. 目标引力:粒子被拽向目标引脚 f_attract = k_attract * (target_pos - particle.pos) # 2. 障碍物斥力:越近越强 f_repulse = sum(k_repulse / dist_sq for obstacle in nearby_obstacles) # 3. 弹簧约束:维持相邻质点间距、等长/并行关系 f_spring = spring_force(particle, neighbors) # 4. 阻尼:防止震荡,让系统稳定下来 f_damp = -k_damp * particle.velocity total_force = f_attract + f_repulse + f_spring + f_damp particle.velocity += total_force / mass * dt particle.pos += particle.velocity * dt这里有个工程细节值得展开:为什么要单独加阻尼项?因为纯弹性系统在迭代中会出现来回震荡,弹簧被拉过头就往回弹、弹回去又拉过头,形成永无止境的抖动。加入速度阻尼之后,系统才会像浸在蜂蜜里一样,稳稳地收敛到平衡态。从我调试经验看,阻尼系数取临界阻尼附近值时,收敛速度最快,跑偏风险也最低。
另外,迭代策略还可以配合“温度退火”:前期引导力权重调大,让粒子快速接近目标;后期约束力权重逐渐增加,让粒子在局部微调间距和等长。这种思路和模拟退火是相通的,实操中能明显改善结果质量。
4.3 几个容易被忽视的工程雷区
粒子密度:质点链的取样间距很关键。间距太疏,走线拐弯会异常生硬,等长约束也调不准;间距太密,计算量陡增,内存占用吓人。我见过有人踩坑,一上来就把间距设到0.1 mil,结果一个网络几百个质点,整块板几千个网络,求解器直接内存溢出。合理的起点是先按1 mil或2 mil设置,再根据结果微调。
层切换和过孔:物理引擎天然在二维平面上工作,遇到需要换层引出的信号,得在过孔位置设置“势阱”,让粒子主动经过这个点,否则路径不会自然产生过孔。这里还要额外处理过孔间距规则,复杂度会再上一个台阶。
后处理角度规整:物理引擎产出的路径是任意角度的,而PCB制造通常要求45°或90°拐角。这个后处理很考验工程能力,它不只是“把角度吸附到45°倍数”,还要保证吸附之后不产生短路、不闯入禁布区。我可以说这是整个管线中“看似简单,实际最磨人”的环节之一。
收敛判断:不能只看总力大小,因为可能有局部极小值。要同时检查所有网络是否到达目标点、是否满足最小间距、是否满足等长误差。我建议把合法性检查放在每N次迭代之后跑一次,不要放到最后一次性做,否则出了问题还得从头跑。
5. 实测效果、翻车现场和我的判断
5.1 跑得顺的场景:约束稀疏的板子
从公开Demo和同类方案的效果来看,物理引擎+LLM的这套方案,表现最好的是低到中等密度的双面板,网络数量几百个量级,高速约束不多。这类板子约束稀疏,物理引擎自由度高的优势能完全发挥:连通率高、走线自然、不需要人工干预的比率相当可观。传统自动布线器在这种板子上也不是不能用,但遇到不规则板框、多禁布区的时候,物理引擎绕障的灵活性明显更胜一筹。
5.2 翻车现场:高密度与严苛物理约束
真正残酷的考验在高端板卡上,翻车场景我也逐个复现过:
- 高密度BGA扇出:BGA下面的引脚排列太密,粒子链之间互相排斥,“谁都挤不进中间通道”,最终绕出一堆不合理的“S”形路径,PCB LVS还查不出来,肉眼一看惨不忍睹。
- 严格差分等长:差分对要求两条线严格平行,同时两对之间又要拉开间距,多个差分对挤在一起时,弹簧系统的收敛速度极慢,迭代几千次误差还是超过5 mil。
- 工艺敏感约束:板上已有大量45°走线,物理引擎新产出的路径会打乱原有布线风格,而且很容易在贴片器件焊盘附近生成锐角,被生产部门直接打回来。
这些翻车现场不是工程实现粗糙,而是方法本身的当前天花板:物理模拟擅长处理“软约束”,但面对高密度下的“硬冲突”(比如空间根本不够,必须牺牲某条规则),它缺乏人类工程师那种“踩下刹车、做优先级取舍”的决策能力。这时候,LLM倒是可以通过解析更高层的设计意图来补位,但整体成熟度还没到量产级别。
5.3 和同类方案对比,它现在更适合当“副驾”
我把这套方案和市面上的主流路线放到一张表里看,会更清楚:
| 方案 | 相对优势 | 明显短板 | 适合场景 |
|---|---|---|---|
| 传统迷宫自动布线 | 稳定、速度快、结果可预期 | 多网络交互时容易堵塞、复杂约束处理弱 | 简单双面板、单网络布线 |
| 商用专业布线器(Specctra/ Allegro等) | 支持高速差分对、等长、阻抗规则完善 | 需要人工预设大量规则和通道,学习成本高 | 高速数字板、多层复杂板 |
| 物理引擎+LLM方案 | 语义理解能力强、约束表达灵活、全局博弈 | 高密度场景收敛难、角度后处理不成熟、实测样本少 | 早期概念阶段、快速迭代、规则变化频繁的设计 |
从这张表可以看出,CircuitPilot目前的目标不应该是“替代资深工程师”,也不该是完全替代商用专业布线器,而应该是“把约束翻译成本和初期布线时间压到最低”。我自己判断,这套方案在EDA领域最有价值的切入点是半自动协作:工程师用自然语言把规则告诉系统,系统快速跑出一版可用的物理布线雏形,工程师再在高风险网络上做精细调整和最终裁决。比起从零开始手拉线,这个效率提升是实实在在的。
我个人在实际项目里试过类似思路之后,最深的体会是:这类方法的真正价值,不在于某一版布线结果有多惊艳,而在于它把“约束传递”这件事自动化了。过去是工程师把一本规则手册里的每一条翻译成手动操作,现在是LLM做翻译、物理引擎做演算、工程师做最后裁判。如果未来能把物理求解换成更高精度的电磁场/热场模型,再把LLM解析错误的样本收集起来做定向微调,这个方向的想象空间还会再大一截。至少我现在看自动布线,已经不会再想“为什么不能有个AI帮我想全局”了——物理引擎加LLM的组合,正是在往这个方向一点一点靠近。