R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""按 `pcb_place.plan()` 的新摆位,把**改摆位之前**板上抓的焊盘表刚体变换成新焊盘表(离线,不连 EDA)。
|
||||
|
||||
/c/Python314/python .claude/r2/synth_pads.py # 两趟收敛,写 _blade_pads.json + _blade_pad_offsets.json
|
||||
|
||||
为什么要有:2026-09-11 晚主板转 180°(方案 §35)要先在服务器上试布线、比完再下发;
|
||||
而 `net_peers()` / 布线器吃的都是焊盘表。改摆位前的表 + 当时的摆位在
|
||||
`archive_blade_prerot_0911/`(`_blade_pads.json` / `old_pos.json`)。
|
||||
|
||||
变换:新焊盘 = 新原点 + R(Δrot)·(旧焊盘 − 旧原点)。Δrot 只允许 0 / 180(180 的时候顺/逆时针没区别,
|
||||
底层「先镜像再转」也不影响);出现 90 / 270 直接报错 —— 那种情况底层的旋转方向要实测,不许猜。
|
||||
⚠ **合成的表只用于离线试布线**。摆位真下发之后必须 `pcb_route.py --dump` 重抓,并和这份逐脚对账
|
||||
(对不上 = 旋转语义推错了,不许拿合成表下发铜)。
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.dirname(HERE))
|
||||
sys.path.insert(0, HERE)
|
||||
ARC = os.path.join(HERE, 'archive_blade_prerot_0911')
|
||||
OLD_PADS = os.path.join(ARC, '_blade_pads.json')
|
||||
OLD_POS = os.path.join(ARC, 'old_pos.json')
|
||||
OUT = os.path.join(HERE, '_blade_pads.json')
|
||||
OFFS = os.path.join(HERE, '_blade_pad_offsets.json')
|
||||
|
||||
|
||||
def synth(new_pos):
|
||||
old = json.load(io.open(OLD_PADS, encoding='utf-8'))
|
||||
op = json.load(io.open(OLD_POS, encoding='utf-8'))
|
||||
out, bad = [], []
|
||||
for row in old:
|
||||
ref = row[0]
|
||||
if ref not in new_pos or ref not in op:
|
||||
bad.append(ref)
|
||||
continue
|
||||
ox, oy, orot = op[ref]
|
||||
nx, ny, nrot = new_pos[ref]
|
||||
d = (nrot - orot) % 360
|
||||
if d % 90:
|
||||
raise SystemExit(u'%s 旋转变了 %d°,不是 90 的倍数' % (ref, d))
|
||||
dx, dy = row[2] - ox, row[3] - oy
|
||||
# 方案 §25.3 实测:底层先 y 镜像、再 rot=θ 按 R(+θ)(**逆时针**)转;顶层同为逆时针。
|
||||
# 镜像在旋转之前 ⇒ 改 rot 只是在已有图案上再左乘 R(+Δ),两面同一个式子。
|
||||
for _k in range(d // 90):
|
||||
dx, dy = -dy, dx
|
||||
r = list(row)
|
||||
r[2], r[3] = round(nx + dx, 3), round(ny + dy, 3)
|
||||
out.append(r)
|
||||
return out, sorted(set(bad))
|
||||
|
||||
|
||||
def offsets(pads, pos):
|
||||
o = {}
|
||||
for r in pads:
|
||||
if r[0] in pos:
|
||||
o.setdefault(r[0], []).append([round(r[2] - pos[r[0]][0], 4), round(r[3] - pos[r[0]][1], 4)])
|
||||
return o
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import importlib
|
||||
import pcb_place as PL
|
||||
for k in range(2):
|
||||
importlib.reload(PL)
|
||||
pos, _r, un, _sz = PL.plan()
|
||||
pads, bad = synth(pos)
|
||||
io.open(OUT, 'w', encoding='utf-8').write(json.dumps(pads, ensure_ascii=False))
|
||||
io.open(OFFS, 'w', encoding='utf-8').write(json.dumps(offsets(pads, pos)))
|
||||
# ⚠ 每一趟都写:pcb_place.flip_2pin 要知道焊盘表是按哪一版 rot 做的(只在最后写的话第二趟会翻回去)
|
||||
json.dump({r: list(v) for r, v in pos.items()},
|
||||
io.open(os.path.join(HERE, '_blade_place_synth.json'), 'w', encoding='utf-8'), indent=0)
|
||||
print(u'第 %d 趟:%d 焊盘 / %d 件;缺: %s;排不下: %s' % (k + 1, len(pads), len(pos), bad or u'无', un or u'无'))
|
||||
在新工单中引用
屏蔽一个用户