--- name: altium-kicad description: > 把 Altium Designer 的 PCB 设计工程整体搬到 KiCad,并验证搬完的板与原设计电气 等价、制造等价。驱动 E:\LangGraph_Design\Altium-Kicad 里的 LangGraph 工作流 (11 节点 / 4 个判定回环),进程内 + SQLite 持久化,不用 langgraph dev。图在每 个需要 MCP、CLI 或人在 GUI 里点的地方 interrupt 回到 Claude 会话,由本 skill 执行真实动作再回填结论推进。附带把两次真实迁移里反复重写的脚本通用化:ASCII 转换脚本生成、BGA 平面焊盘网络回填、覆铜 zone clearance 批改、逐页网表 partition 比对、走线/过孔矢量等价比对。 只要用户提到 Altium 转 KiCad、AD 转 KiCad、把 PrjPCB 导入 KiCad、迁移/搬移 PCB 工程、altium-kicad 工作流、"Header already parsed"、原理图转 ASCII、 导入后 DRC 一堆短路、BGA 焊盘丢网络、铺铜避让变大、网表比对、Gerber 比对、 或者说 "启动 altium-kicad"、"跑迁移流程"、"继续上次那个迁移"、"convert my Altium project to KiCad"、"import PrjPCB into KiCad"、"migrate PCB from Altium",就必须用这个 skill —— 哪怕他们没说 skill、没提 LangGraph,只说了 "把这块板转成 KiCad"、"这板子迁移到一半,接着弄"。 --- # Altium → KiCad 工程迁移 ## 这个 skill 是什么 一次完整迁移 = 5 个阶段:**桥接 → 转 ASCII 并导入 → 修 PCB → 网表对齐 → 几何对齐 → 验收**。 这些步骤有严格的先后依赖、有失败要退回重做的回环、单次会话跑不完(用户要去 Altium 和 KiCad 里手动点很多次)。所以状态由 LangGraph 图持久化在 SQLite 里, 你负责把图停下来的每一步翻译成真实动作,做完回填结论让它往下走。 **MCP 边界(必须理解,否则会白折腾)**:eda-agent(altium MCP)和 KiCAD-MCP-Server 跑在**你这个 Claude 会话进程里**,不在 LangGraph 的 Python 进程里。所以图不可能 自己去 ping Altium、自己去数文件、自己去改板 —— 它只做编排,凡是要碰真实世界的 地方一律 `interrupt()` 交回给你。这是设计,不是缺陷。 ## 环境 | 项 | 值 | |---|---| | 图工程根 | `E:\LangGraph_Design\Altium-Kicad`(可用 `--project-root` 或 `AK_PROJECT_ROOT` 改) | | Python | conda env `langgraph`(`~\skills_env.md` 的 `CONDA_LANGGRAPH_PYTHON`) | | KiCad python | `~\skills_env.md` 的 `KICAD_PYTHON`,本机 `D:\KiCad\10.0\bin\python.exe` | | checkpoint | `<图工程根>\runs\skill_checkpoint.sqlite`,按 thread 隔离 | | thread 命名 | 由 target_dir 目录名推导,如 `E:\Kicad_design\XMOD_V1p0` → `ak-xmod-v1p0` | | 驱动脚本 | `%USERPROFILE%\.claude\skills\altium-kicad\scripts\ak_flow.py` | 需要的 MCP:**eda-agent**(Altium 侧取数)、**kicad**(可选,KiCad 侧大多走 `kicad-cli` 和 pcbnew 更稳)。 ## 驱动命令 统一用 `conda run -n langgraph python` 跑 `ak_flow.py`。**退出码 2 = 停在 interrupt 等你回填**(不是错误),0 = 图跑完,1 = 出错。 ```powershell $AK = "$env:USERPROFILE\.claude\skills\altium-kicad\scripts\ak_flow.py" # 新建 / 续跑(同一个 target-dir 重复跑就是续跑,不会重置) conda run -n langgraph python $AK start ` --source-prj "E:\Altium_design\FOO\FOO.PrjPCB" ` --target-dir "E:\Kicad_design\FOO" # 看现在停在哪 conda run -n langgraph python $AK status --target-dir "E:\Kicad_design\FOO" # 回填结论、推进(--set 免转义,true/false 转 bool,纯数字转 int) conda run -n langgraph python $AK resume --target-dir "E:\Kicad_design\FOO" ` --set bridge_ok=true # 列出所有迁移线程 / 清掉某个线程重来 conda run -n langgraph python $AK list conda run -n langgraph python $AK reset --target-dir "E:\Kicad_design\FOO" ``` `--set` 的值含空格要整体加引号:`--set "report_path=E:\My Board\report.md"`。 `start` 还可以调回环的重试上限(默认 桥接/导入 3 次、网表/几何 5 次,超限走 safety_end): `--max-bridge-retry N` `--max-import-retry N` `--max-netlist-retry N` `--max-gerber-retry N`。 `[INTERRUPT]` 后面的 JSON 里 `action` 字段告诉你该做哪一步,下面逐个说。 > **回填不能是空的**。langgraph 把 falsy 的 resume 当作「没提供」,会在同一个 > 节点再中断一次,看起来像卡住。至少给一个键。 --- ## 逐步执行 开始之前先问清楚两件事(没有默认值,猜错了会把工程写到别处): **源 `.PrjPCB` 的绝对路径**、**目标 KiCad 目录的绝对路径**。 ### `prompt_altium_mcp` — 让用户开启 Altium 桥接 告诉用户在 Altium 里:`DXP/Preferences → Scripting System → Global Projects → Install` 选 `Altium_API.PrjScr`,然后 `File → Run Script → Dispatcher.pas → StartMCPServer → Run`,状态窗显示 `MCP: idle` 即通。 提醒两条脾气:桥接**约 10 分钟空闲自停**,**一次只能跑一个脚本**。 用户确认后:`--set acknowledged=true` ### `check_altium_bridge` — 验证桥接 调 eda-agent 的 `app_ping`。通了 `--set bridge_ok=true`,不通就 `false` (图会回到上一步让用户重开,最多 3 次)。 ### `convert_ascii` — 转 ASCII(顺带做基线快照) **先做基线快照,趁桥接还活着。** 后面 `check_file_count` 要页数、`rebuild_rules` 要设计规则、网表比对要真值网表,这些全都只能从 Altium 侧取;而转 ASCII 的第一步 就是停掉 eda-agent,停了再想取就得重开桥接。所以顺序是: 1. 用 eda-agent 落盘基线到 `\_migration\altium_baseline\`: `proj_get_stats` `proj_get_nets` `proj_get_bom` `pcb_get_board_statistics` `pcb_get_layer_stackup` `pcb_get_design_rules`(存成 `pcb_design_rules.json`, 后面查 InPolygon clearance 要用) 2. `proj_export_netlist`(**tabular**)导到 `_migration\reference\netlist_tabular.csv` —— 这是后面回填焊盘网络和比网表的**唯一真值**,别漏 3. 记下原理图页数和 PCB 数(`check_file_count` 要用) 然后转 ASCII: ```powershell python "$env:USERPROFILE\.claude\skills\altium-kicad\scripts\stage_ascii.py" stage ` --source-prj "E:\Altium_design\FOO\FOO.PrjPCB" ` --migration-dir "E:\Kicad_design\FOO\_migration" ``` 它把整个工程复制到 `<工程目录>_ascii` staging(**原始工程一个字节都不动**), 枚举出实际的 `.SchDoc` 并生成 `convert_to_ascii.pas`。 接着让用户:**先停掉 eda-agent**(不停会和 StartMCPServer 脚本冲突挂起),再在 Altium 里 `File → Run Script` → 选生成的 `.pas` → `ConvertSchToAscii` → Run。 跑完验证: ```powershell python ...\stage_ascii.py verify --staging-dir "E:\Altium_design\FOO_ascii" ``` **verify 通过了再回填**:`--set ascii_ok=true --set ascii_staging_dir=E:\Altium_design\FOO_ascii` > ⚠️ `convert_ascii` 后面没有判定回环,回填 `ascii_ok=false` 图照样往下走。 > 没转干净就进 KiCad 导入必然报 `Header already parsed`,白跑一轮。 ### `prompt_kicad_import` — 用户在 KiCad 里手动导入 这一步**没有任何 CLI/MCP 通道**,只能人点。给用户精确路径: 打开 **KiCad 主程序(工程管理器)** → `File → Import Non-KiCad Project → Altium Project...` - 源:staging 里的 **`_ascii` 版** `.PrjPCB`(选错成原始工程就前功尽弃) - 目标:target_dir - 点 OK 等待,十几页 + 多层 PCB 要几十秒到几分钟 导完常见现象:报告里只有 `IEEE_SYMBOL` 未处理记录(无害);结构是扁平的, 可能多出重复的 `.kicad_sch`(确认无引用后删掉)。 用户确认导完:`--set imported=true` ### `check_file_count` — 文件数核对 Altium 侧基线(原理图页数 + 1 块 PCB)对比 target_dir 下 `*.kicad_sch` + `*.kicad_pcb` 的计数。payload 里的 `kicad_glob_hint` 是图那侧 glob 出来的预填值(`-1` 表示它数不到),你要自己核实。 `--set altium_count=16 --set kicad_count=16` 不一致会退回重导(最多 3 次)。不一致时先看是不是多了重复页,或者某页导入失败。 ### `rebuild_rules` — 设计规则重建 + PCB 修复 三件事,顺序不能反: **1) 改 `.kicad_pro` 的规则**:按基线 `pcb_design_rules.json` 设间隙/线宽/过孔/ 孔/网络类。导入只搬几何,规则用的是 KiCad 默认(比 Altium 严),不改的话 DRC 会有几千条误报。 **2) 回填焊盘网络 + 批改 zone clearance**(先干跑看量): ```powershell $KP = "D:\KiCad\10.0\bin\python.exe" # ~\skills_env.md 的 KICAD_PYTHON & $KP "...\scripts\fix_pcb.py" --board "E:\Kicad_design\FOO\FOO.kicad_pcb" ` --netlist-csv "E:\Kicad_design\FOO\_migration\reference\netlist_tabular.csv" ` --zone-clearance-mm 0.254 --dry-run ``` `--zone-clearance-mm` 要去基线 `pcb_design_rules.json` 查 **InPolygon 规则**的 polygon clearance,别照抄 0.254。确认后去掉 `--dry-run` 真改(会自动备份板文件)。 **3) 重填铺铜**:让用户在 KiCad GUI 里按 `B`,或走 IPC 后端。 **大板不要用 SWIG/pcbnew refill**,会超时甚至段错误。 跑一次 DRC 确认数字降下来了(`run_drc` 或 `kicad-cli pcb drc`), 然后 `--set rules_ok=true`。 > 这两个坑的根因写在 `references/playbook.md` §4,值得先读: > **BGA 平面焊盘丢网络**(假短路的真凶,Altium 靠负片内电层隐式连接)、 > **zone 本地 clearance 被写成 0.5mm 且会盖过网络类**(只改网络类完全无效)。 > `rebuild_rules` 后面同样没有回环,别在没修好的时候回填 true。 ### `compare_netlist` — 网表对齐(可回环 5 次) ```powershell conda run -n langgraph python "...\scripts\netcmp.py" ` --kicad-dir "E:\Kicad_design\FOO" ` --netlist-csv "E:\Kicad_design\FOO\_migration\reference\netlist_tabular.csv" ` --out "E:\Kicad_design\FOO\_migration\netcmp" ``` 脚本会**逐页**导出 kicadxml(扁平结构下整体导出会漏页)、剥掉 KiCad 网名的前导 `/`、去掉 Altium 的 `net=?` 重复行,然后按 **net partition** 比等价类 —— 不比 网络名,两个工具命名规则不同,逐名比必然一片红。 一致(退出码 0)→ `--set netlist_identical=true`。 不一致 → 看 `diff.json` 改 KiCad 对齐,`--set netlist_identical=false` 会回到这一步 再来一轮。改不动的时候,节点级差异往往不是真错,先按 playbook §5 的三条归一化 自查。超过 5 轮会走 safety_end。 > payload 里写的是「调 `/goal` 让 Claude Code 或 Codex 自主迭代」。愿意的话可以 > 那样做;直接用 `netcmp.py` + 自己改板通常更快也更可控。 ### `compare_gerber` — 几何等价(可回环 5 次) 判定基准先说死:**逐层制造几何等价**。离散铜(走线/过孔/焊盘/孔/板框)要零差异; 铺铜纹理、丝印字形、负片内电层是**工具固有容差项,不是缺陷、修不掉**。 决定性证据走矢量比对,不是栅格 XOR: ```powershell & $KP "...\scripts\dump_kicad_vec.py" --board "...\FOO.kicad_pcb" ` --out "...\_migration\gerber\kicad_vec" # Altium 侧用 eda-agent 导同名 JSON 到 altium_vec\(格式见 references\vector-compare.md) python "...\scripts\cmp_vec.py" --altium-vec ...\altium_vec --kicad-vec ...\kicad_vec ` --layer-map ...\layer_map.json --report ...\vec_result.json ``` `TOTAL unmatched primitives: 0` → `--set gerber_aligned=true`。 要出 Gerber 图片证据的话:KiCad 侧 `kicad-cli pcb export gerbers/drill`; **Altium 侧 eda-agent 生不了 Gerber**(工程没有 OutJob,`proj_run_output` 是空操作), 必须让用户在 `File → Fabrication Outputs → Gerber Files` 手动导,参数见 playbook §7。 ### `acceptance_report` — 验收报告 写 `\verification_report.md`,必须包含: 1. 各阶段 PASS/FAIL 与关键数字(页数、元件数、节点数、走线/过孔匹配数) 2. **明确列出三项工具固有差异**(负片内电层 / 铺铜填充纹理 / 丝印字体) 及其根因,注明是容差项不是缺陷 —— 不写清楚,后面看报告的人会当成没做完 3. 证据文件索引 `--set report_path=E:\Kicad_design\FOO\_migration\verification_report.md` 回填后图结束,`ak_flow.py` 打印 `[DONE]`。 --- ## 铁律速查 1. **原理图必须先转 ASCII**。AD26 二进制 `.SchDoc` 会让 KiCad 报 `Header already parsed`。PCB 侧没这问题,只有原理图。 2. **转 ASCII 之前先停 eda-agent**。Altium 一次只能跑一个脚本,桥接自己就是个脚本。 3. **基线快照要趁桥接活着做完**,尤其 tabular 网表 —— 后面回填网络和比网表都靠它。 4. **KiCad 导入只能人工点**,源必须选 `_ascii` 版。 5. **`kicad-cli pcb import` 不能替换主板**,那会切断原理图↔PCB 关联,只能当参考。 6. **zone 的本地 clearance 会盖过网络类**,只改 `.kicad_pro` 无效。 7. **网表比 partition 不比网络名**,且必须逐页导 kicadxml。 8. **跨工具像素级一致不可能**,判定基准是离散几何等价 + 三项固有容差。 9. **原始 Altium 工程只读**,所有写操作只作用于 staging 副本和 target_dir。 ## 排错 | 症状 | 处理 | |---|---| | resume 后又停在同一个节点 | 回填是空的或全 falsy。langgraph 把它当没提供。加一个非空键。 | | `app_ping` 突然不通 | 桥接空闲 10 分钟自停了。让用户重跑 `StartMCPServer`。 | | Altium 脚本跑起来卡死 | eda-agent 没停,两个脚本冲突。停掉再跑。 | | 导入报 `Header already parsed` | ASCII 没转干净,或者源选成了原始工程。`stage_ascii.py verify` 查。 | | 导入后 DRC 几千条 | 正常。先按基线改 `.kicad_pro` 规则,再回填焊盘网络、重填铺铜。 | | 一堆 "GND vs 空网络" 短路 | BGA 平面焊盘丢网络。`fix_pcb.py` 回填。playbook §4.1。 | | 改了网络类但铺铜避让没变 | zone 本地 clearance 盖过了。`fix_pcb.py --zone-clearance-mm`。playbook §4.2。 | | refill 超时/段错误 | 大板别用 SWIG。GUI 里按 `B`,或 IPC 后端。 | | 网表比对差一堆节点 | 多半是某页 kicadxml 没导出成功,看 `netcmp.py` 的 `[export] N/M`。 | | 矢量比对某层全 unmatched | `layer_map.json` 配错了。不带 `--layer-map` 跑一次看两侧每层走线数。 | | `[SAFETY-END]` | 某个回环超了重试上限,图优雅退出了。修好卡住的那步再 `start --restart` 重跑;确实需要更多轮次就加 `--max-netlist-retry 8` 这类参数。 | | kicad-cli 输出 `UnicodeDecodeError` | 别用 `text=True` 收 kicad-cli 的输出,中文 Windows 会按 GBK 解 UTF-8。`netcmp.py` 已处理。 | | `ModuleNotFoundError: langgraph` | `conda run -n langgraph pip install langgraph langgraph-checkpoint-sqlite` | | `ImportError: pcbnew` | `fix_pcb.py` / `dump_kicad_vec.py` 必须用 KiCad 自带 python 跑,不是 conda 的。 | ## 深入 - `references/playbook.md` — 每个坑的根因、XMOD/GQ 两次迁移的实测数字、 各阶段该有的判定基准和证据目录约定。卡住了先读这个。 - `references/vector-compare.md` — `altium_vec/` 各 JSON 的字段格式、 `layer_map.json` 怎么定、结果怎么读。 - 图的拓扑与 interrupt 契约:`E:\LangGraph_Design\Altium-Kicad\Notes\Task04.md`