Files
2026-09-09 09:51:06 +08:00

11 KiB
Raw Permalink Blame History

Altium → KiCad 迁移 Playbook

两次真实迁移(XMOD_V1.0:10层/1191元件/15页;GQ_EXCT7G_V1:ADRV9009 板)踩出来的东西。 SKILL.md 只写「怎么走」,这里写「为什么」和「踩了会怎样」。遇到卡壳先回来查这里。


1. 为什么必须先转 ASCII

KiCad 10.0.4 的 Altium 原理图解析器不支持 AD26 的二进制 .SchDoc。直接 File → Import Non-KiCad Project → Altium Project 会报:

Header already parsed

PCB 解析器是好的,只有原理图这一侧不行。二进制 .SchDoc 是 CFB 复合文档 (文件头 D0 CF 11 E0),ASCII 版则以 |HEADER=Protel for Windows 开头。

Altium 侧的出路是 IServerDocument.DoFileSave('ASCII'),没有 CLI,只能跑 DelphiScript。两个坑:

  • 不要经 MCP 驱动这个脚本。obj_run_process(ScriptingSystem:RunScript) 会与 桥接自己的 StartMCPServer 脚本冲突挂起 —— Altium 一次只能跑一个脚本。 正确姿势:先停掉 eda-agent,再在 Altium GUI 里 File → Run Script 手动跑。
  • DelphiScript 的 FindFirst 枚举不可靠,脚本里的文件名必须是硬编码列表。 stage_ascii.py stage 就是干这个的:用 Python 枚举出实际的 .SchDoc,把名字 灌进生成的 .pas 里。

转换只对 staging 副本做,原始工程一个字节都不能动。


2. eda-agent(altium MCP)桥接的脾气

桥接不是常驻服务,是 Altium GUI 里跑的一个轮询 DelphiScript:

  • 约 10 分钟空闲会自己停。跑长任务中途回来发现 app_ping 不通是常态, 重跑 Dispatcher.pas → StartMCPServer 即可,图里 check_altium_bridge 的 回环就是为这个留的。
  • 一次只能跑一个脚本,所以任何别的 DelphiScript 都要先停桥接。
  • 开启路径:DXP/Preferences → Scripting System → Global Projects → Install 选 Altium_API.PrjScr,然后 File → Run Script → Dispatcher.pas → StartMCPServer → Run,状态窗出现 MCP: idle 就是通了。
  • eda-agent 生不了 Gerber。proj_run_output(gerber) 在没有 OutJob 的工程上 是空操作。Altium 侧的 Gerber 只能让用户在 GUI 里 File → Fabrication Outputs → Gerber Files 手动导。

3. KiCad 导入这一步没有自动化通道

整工程导入必须在 KiCad 主程序(工程管理器) 里点: File → Import Non-KiCad Project → Altium Project... 源选 staging 里的 _ascii 版 .PrjPCB,目标选 target_dir。

kicad-cli pcb import --format altium 只能转 PCB 单板,不要拿它替换主板 —— 那样会切断原理图↔PCB 的关联。它有用的地方是另转一份到 _migration/cli_pcb/ 当几何参考/对照。

导入完成后常见的两个结构问题:

  • 扁平层次:导入器不生成 sheet 符号,所有页平铺;还可能把某一页当成根图。 Save All 时可能生成重复的 .kicad_sch(例如 12.XC7Z100_HP.kicad_sch 多一份)。 确认无引用后删掉重复文件即可,页数要能对回基线。
  • IEEE_SYMBOL 未处理记录:IEEE 装饰符号被跳过,无害,不用管。

文件数核对就是拿「Altium 原理图页数 + 1 块 PCB」对 target_dir 下的 *.kicad_sch + *.kicad_pcb 计数。


4. 导入后 PCB 的两个必修项

4.1 BGA / 平面焊盘丢网络(假短路的真凶)

Altium 用负片内电层隐式连接电源和地,焊盘本身不带网络信息。KiCad 没有这套 机制,导入时这些焊盘就空着网络。XMOD 实测 527 个 BGA 电源/地焊盘丢网络, DRC 里表现为 103 个「GND vs 空网络」短路。

修法:拿阶段 0 导出的 Altium 网表真值(netlist_tabular.csv 的 component/pin/net)程序化回填。fix_pcb.py 就是干这个的,先 --dry-run 看量。

回填后还会剩一批查不到真值的空网络焊盘 —— 多是 RJ45 屏蔽壳、机械/安装孔焊盘, 按实际情况单独指派 GND(XMOD 是 18 个)。

4.2 覆铜 zone 的本地 clearance(异 net 避让偏大的真凶)

这是最容易白费力气的一个坑。Altium 导入器把每一块 zone 的本地 Clearance 写成 KiCad 默认的 0.5mm,落在 .kicad_pcb 里长这样:

(zone ... (connect_pads (clearance 0.5)) ...)

而 zone 的本地值会盖过网络类 clearance。所以只改 .kicad_pro 的网络类 (比如 0.2mm)或板级规则,铺铜避让一点都不会变 —— 必须逐块 zone 改。

目标值取 Altium 的 polygon clearance(InPolygon 规则,去基线 pcb_design_rules.json 查;XMOD 和 GQ 实测都是 0.254mm,但别照抄)。

热焊盘不用动:KiCad 的 thermal_gap 0.1651mm 已经匹配 Altium PolygonConnect 的 AirGap 0.165mm。

改完必须重填铺铜。大板上 SWIG/pcbnew 的 refill 容易超时甚至段错误, 去 KiCad GUI 里按 B,或者走 IPC 后端。

4.3 DRC 数字的正常轨迹

导入只搬几何,规则用的是 KiCad 默认(比 Altium 严),所以初始 DRC 会很吓人。 XMOD 的轨迹供参考:按基线改完 .kicad_pro 规则 2878 → 1481(错误 1810→414,track/via/drill 三类规则误报各 199 清零);再回填 527 个焊盘网络 + 重填铺铜后 clearance 282→0、via_dangling 199→15、shorting 103→92。 残留的 92 是板角铺铜畸变和连接器,不是真短路。


5. 网表比对:比 partition,不比网络名

两个工具的命名规则不同,逐名比对必然一片红。要比的是等价类:把网表归约成 「每个引脚 → 它所在网络的全部节点集合」,两侧集合相等即电气一致。

三个归一化,少一个就出假差异:

  1. KiCad 网名有前导 /(层次路径),Altium 没有 → 剥掉。 XMOD 初比出的 16 个差异全是 R_I- vs /R_I- 这种。
  2. Altium tabular 导出有 net=? 的重复行(隐藏的电源/平面引脚被重复列出)。 XMOD 原始 9511 行里有 4500 行是 ?,去重后才是 5011 个真实节点。
  3. KiCad 扁平结构下整体导出网表会漏页 —— XMOD 整体导出只拿到根图的 98 个 节点。必须逐页 kicad-cli sch export netlist --format kicadxml 再按网名合并 (5011 节点 / 1186 元件 / 1477 网)。格式必须是 kicadxml;PADS 格式会漏掉 单节点网(只得 4154)。

XMOD 终判:5011=5011 节点全同、0 单边节点、等价类集合完全相等(1473=1473)、 0 实质差异 → 网表 100% 一致。

netcmp.py 把上面全部实现了,包括逐页导出。


6. Gerber / 几何比对:什么叫「100% 一致」

跨工具 byte/像素级一致不可能,这不是没做好,是物理上做不到:aperture、 坐标格式、填充算法、字体引擎、正负片全都不同。所以判定基准必须先界定清楚:

判定 = 逐层制造几何等价。 离散铜(走线/焊盘/过孔/孔/板框)要零差异; 铺铜填充纹理、丝印字形、负片内电层属于工具固有容差项。

6.1 决定性证据是矢量比对,不是栅格 XOR

栅格 XOR(pygerber 渲染后逐像素)只能给「像什么程度」的百分比,说服力有限。 矢量比对是逐条逐个匹配走线和过孔,对上就是同一块板。

XMOD 的矢量结论:走线 12144=12144、过孔 4808=4808 逐条逐个相同, TOTAL_DISCRETE_DIFF=0。

坐标变换只有 2 个自由参数(平移),由两侧过孔质心解出;用 2 个参数去对上 几千个图元,对上了就是实证而不是曲线拟合。Altium 与 KiCad 之间是纯平移, 无翻转无镜像(XMOD 实测 KiCad = Altium + (38.501, −180.004))。 注意 Altium 的 y 轴向上、KiCad 向下,变换里 y 要取负。

容差取 30um:eda-agent 读出的 Altium 坐标是整数 mil,读数精度本身 ±12.7um,两端各一次就接近 25um;KiCad 侧是精确的。

dump_kicad_vec.py + cmp_vec.py 实现这一套,Altium 侧的 JSON 格式见 vector-compare.md。

6.2 栅格比对的参考数值(XMOD 逐层)

层 差异 判定
焊盘 Paste 0.035% 完全一致(也用来验证平移变换的精度)
板框 Profile 0.34% 一致
外层铜 F_Cu 1.04% 基本一致
阻焊 Mask ~2% 扩展量微差
丝印 Silk 42–70% 字体引擎不同,固有
内电层 GND/POWER ×5 反相后 ~12% Altium 负片 vs KiCad 正片
内层信号 ×3 高 铺铜覆盖/填充差异

看到丝印 70% 不要慌,那是字体,不是错。负片内电层必须先反相才能比。

6.3 三个不可消除的工具固有差异(已查明根因,不是缺陷)

  1. 负片内电层:KiCad 架构里没有「内电层」这种层类型,也没有负片输出能力。 与 Altium 的负片格式无法一致,但物理铜等价——制板厂显影结果相同。
  2. 铺铜填充纹理:填充算法不同(热焊盘辐条/网格/反焊盘),即使轮廓和网络 完全相同也不可能逐像素一致。
  3. 丝印字体:字体引擎不同。

验收报告里要把这三条明确列为容差项,别当缺陷去修 —— 修不掉。


7. Altium 侧 Gerber 手动导出参数

eda-agent 生不了,让用户在 Altium 里导,参数要对齐 KiCad 侧:

  • File → Fabrication Outputs → Gerber Files:单位 mm、精度 4:6、 Embedded RS274X、不镜像、绝对原点
  • File → Fabrication Outputs → NC Drill Files
  • 输出落在 Project Outputs for <工程名>\,复制到 _migration/gerber/altium/

KiCad 侧对应:kicad-cli pcb export gerbers(精度 6、绝对原点)+ kicad-cli pcb export drill(Excellon、mm)。


8. 证据目录约定

全部落在 <target_dir>\_migration\:

altium_baseline/      阶段0 基线真值:stats / nets / bom / stackup / pcb_design_rules.json
reference/            参考网表(netlist_tabular.csv) + 原理图 PDF
altium_source_snapshot/  只读源副本
convert_to_ascii.pas  由 stage_ascii.py 生成
ascii_convert_log.txt DelphiScript 跑完的日志
cli_pcb/              kicad-cli 忠实导入的 PCB(仅作几何参考,不替换主板)
netcmp/               xml_*.xml 逐页网表 + diff.json + 报告
gerber/               altium_vec/ kicad_vec/ layer_map.json + 比对报告
backup/               每次改板前的 .kicad_pcb 快照
verification_report.md  最终验收报告

XMOD 的基线真值(作为「基线该长什么样」的样例):15 页 / 元件 1191(PCB 1186)/ 管脚 9511 / 网络 1474 / 10 层 / 走线 21296 · 过孔 4808 · 焊盘 5154 / 未布线 0 / 224×150mm / 41 条设计规则。