tools/epro2/kicad: Phase-1 .kicad_pcb exporter — 6/6 boards open in KiCad 8
Phase-1 scope: produce a .kicad_pcb that kicad-cli loads cleanly and
that has the right geometry (nets, footprints, tracks, vias, board
outline) — not a 1:1 EDA round-trip. Skipped on purpose for Phase 2:
copper pours (POUR/POURED), manual FILL, teardrops, board-level
strings/images, ARC circle-center recovery.
What lands:
- pcb_writer.write_pcb(): header/general, data-driven layer table
(F.Cu = ord 0; B.Cu = ord 31; SIGNAL inner ids 15+ allocated to
In1.Cu/In2.Cu/... in EPRO2-id sorted order so used inner layers
stay contiguous), net-name → integer id map (id 0 reserved for the
empty net per KiCad convention), LINE→segment / LINE→gr_line on
Edge.Cuts, layer-11 POLY paths walked into Edge.Cuts gr_line chains
(the actual board outline lives on POLY here, not LINE — without
this stats showed edge=0), VIA→via.
- footprint_writer.write_footprint_placement(): inline (footprint ...)
blocks per PCB COMPONENT. EPRO2 RECT/ELLIPSE/OVAL/POLYGON pad
shapes mapped to KiCad rect/circle/oval/custom; SMD vs THT detected
by PAD.hole presence; SLOT holes use (drill oval w h). Pad nets
resolved cross-doc via the existing PCB.PAD_NET → footprint.pad
chain in ProjectRelations. layerId=2 component → (layer B.Cu) +
text on B.SilkS so bottom-side parts render correctly.
Smoke test on ESP-VoCat (6 PCBs): all 6 pass `kicad-cli pcb export svg`
and render. DRC on smallest (MicBoard) reports 145 violations + 75
unconnected — most of the unconnected are GND nets that the EPRO2
source resolves through POUR copper, which Phase 2 will export.
CLI: `python -m tools.epro2.kicad <project> --all-pcb --out <dir>`
emits one .kicad_pcb per PCB doc.
52 → 65 unit tests pass. Float comparisons in tests use math.isclose
because the s-expr 6-decimal trim doesn't preserve strict equality
through `value * MIL_TO_MM` round-trips.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
log.md
67
log.md
@@ -4,6 +4,73 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-29 01:30 KiCad 导出 Phase 3 PCB:6/6 .kicad_pcb 全部 kicad-cli 通过
|
||||
|
||||
**Claude 会话**
|
||||
|
||||
接 `ff5553f` 后续。Forge 投影最后一块——schematic 已经够用,做 `.kicad_pcb` 导出。一上来就铺了 Phase-1 最小可解析 scope(要先有"能在 KiCad 8 里打开 + kicad-cli 处理"的底线),不追全保真。
|
||||
|
||||
### 做的
|
||||
|
||||
1. **`tools/epro2/kicad/pcb_writer.py`** — 单 PCB doc → `.kicad_pcb` 主入口
|
||||
- 数据驱动的层映射 `_build_layer_map`:扫所有 primitive 的 `layerId`,把真正用到的 SIGNAL 内层(id 15+)按 EPRO2-id 顺序铺成 `In1.Cu`/`In2.Cu`/...;F.Cu 永远 ordinal 0、B.Cu 永远 ordinal 31(KiCad 硬约定)。
|
||||
- net 表:从 NET op 取名字,从 1 开始分配 id(0 留给 KiCad 的 "no net")。
|
||||
- 走 LINE:copper 层 → `(segment ...)`,layer 11 OUTLINE → `(gr_line layer Edge.Cuts)`;走 VIA → `(via)`;走 layer-11 POLY → 把 path 拆 polyline 段 emit 成 Edge.Cuts 上的 gr_line 链(实测板子轮廓全在 POLY 不在 LINE,少这步 edge=0)。
|
||||
- ARC 现在按弦近似(Phase 1 不解 EPRO2 ARC.path 的圆心/半径表示,KiCad 收得下,DRC 报 invalid_outline——下一轮改)。
|
||||
|
||||
2. **`tools/epro2/kicad/footprint_writer.py`** — inline `(footprint ...)` 块
|
||||
- 走 FOOTPRINT.PAD:`RECT/ELLIPSE/OVAL/POLYGON` → KiCad `rect/circle/oval/custom`;hole 存在则 SMD→thru_hole + `(drill ...)`,SLOT 走 `(drill oval w h)` 二参形式。
|
||||
- layer 映射:layerId 1 → `F.Cu+F.Mask+F.Paste`、2 → `B.Cu+B.Mask+B.Paste`、12 (MULTI/THT) → `*.Cu+*.Mask`。
|
||||
- net 解析借 `pcb_rel.pad_nets_by_pad[pad_id]`(之前 `ProjectRelations` 已经为 PCB→FOOTPRINT cross-doc 攒过的索引现在派上用场)。
|
||||
- bottom-side COMPONENT (`layerId=2`) 整个 footprint 走 `(layer B.Cu)`,Reference 标签同步落到 `B.SilkS`。
|
||||
|
||||
3. **CLI 加 `--all-pcb`**(`tools/epro2/kicad/__main__.py`):每个 PCB doc 一文件,文件名取 META.title。
|
||||
|
||||
### ESP-VoCat 实测(6 个 PCB)
|
||||
|
||||
| 板 | nets | footprints | segments | vias | edge_cuts |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| BaseBoard | 38 | 59 | 391 | 126 | 4 |
|
||||
| CoreBoard | 84 | 87 | 1131 | 262 | 4 |
|
||||
| MicBoard | 13 | 17 | 88 | 47 | 16 |
|
||||
| LCD-BD | 7 | 5 | 66 | 35 | 2 |
|
||||
| Mainboard | 24 | 33 | 340 | 181 | 4 |
|
||||
| Sub-board | 4 | 5 | 10 | 0 | 4 |
|
||||
|
||||
**6/6 都过 `kicad-cli pcb export svg`**——文件解析无 error、SVG 输出正常。footprint/track/via/edge cuts 全部可见。
|
||||
|
||||
`kicad-cli pcb drc` 对最小的 MicBoard 跑:145 violations + 75 unconnected。分布:
|
||||
- 28 clearance(trace/pad 间距)+ 19 track_dangling + 39 via_dangling + 75 unconnected — 真连接问题,部分是 EPRO2 源里靠 POUR 解决但我们 Phase 1 没导
|
||||
- 21 silk_overlap + 8×2 edge_clearance + 3 invalid_outline — 边角 + silk 美化
|
||||
- 17 lib_footprint_issues — facere 库没注册(cosmetic,跟 schematic 一样)
|
||||
|
||||
### Phase 1 砍掉的 / 下一轮再做
|
||||
|
||||
- **POUR / POURED** — 铜皮覆铜,导了 75 unconnected 的大头会消掉(GND 大面积在 POUR 里走,没导出来 trace 当然报 unconnected)
|
||||
- **FILL** — 元件下方手工 fill 块
|
||||
- **ARC 圆心/半径解析** — 现在按弦近似,invalid_outline 警告就这来的
|
||||
- **TEARDROP** — pad/via 接 trace 处的圆角,纯美化
|
||||
- **STRING / IMAGE** — 板上文字和 logo 图
|
||||
|
||||
### 决策(Why)
|
||||
|
||||
- **板子原点平移到 (100, 100) mm**:EPRO2 板能在任意坐标(含负),KiCad 画布原点在 (0,0),不平移开 KiCad 时板子可能根本不在视野里。100mm 是经验值,留够 silkscreen 边距。
|
||||
- **footprint inline 不走外部 .kicad_mod**:跟 schematic lib_symbol 同思路,自包含,能直接 `kicad-cli` 处理;缺点是同款 footprint 重复写多份,但 EPRO2 一个 SCH 内的 FOOTPRINT 数量级(几十)摊开来文件膨胀也不大。
|
||||
- **ELLIPSE 当 circle 不当 oval**:实测 ELLIPSE 的 width/height 经常相等(=圆形 pad),KiCad 没有真椭圆 pad type,circle 取 max(w,h) 比 oval 更接近圆。
|
||||
- **ARC 暂时按弦**:算 EPRO2 ARC 圆心要先反推三点定圆,浮点精度敏感。Phase 2 单独处理。
|
||||
|
||||
### 测试
|
||||
|
||||
52 → 65 单测全过:pcb_writer 8 个(layer map / net id / segment / via / outline POLY / 零长 skip / 非 PCB doc reject)+ footprint_writer 5 个(SMD pad layers / 圆 vs slot drill / pad net 跨 doc 解析 / bottom-layer 翻转 / unresolved skip)。
|
||||
|
||||
### 下一步建议
|
||||
|
||||
- **POUR/POURED 导 zone**(中工作量):消掉大部分 75 unconnected,CoreBoard 这种 4-layer + 大量 GND/POWER 覆铜的板真实连通性会贴近完整。
|
||||
- **schematic + PCB 同时跑** 一个 export 命令(`--all`),生成完整 KiCad project 目录。
|
||||
- **Phase 4: KiCad project 文件 .kicad_pro**:让用户双击就能在 KiCad GUI 里打开 schematic + PCB 配对。
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-29 01:00 科普文档:爬取 per-doc .epro2 vs 网页端 .epro2 ZIP 整包
|
||||
|
||||
**Claude 会话**
|
||||
|
||||
Reference in New Issue
Block a user