文件
zhou yang和Claude Opus 5 efcba30a0c R2 底板改 4 层(D15):规则 / 几何 / 背面摆位 / 4 层布线与铺铜,已下发
- 规则:铜层 2→4,差分 0.28/0.18,间距 0.15,过孔 0.3/0.2(rules_base)
- 几何:98×98 板框 + 4×M3,槽阵居中,L1 12V 大铜 + 指头(pcb_base)
- 摆位:五插槽留 L1,其余 133 件翻到 L4;背面镜像规则实测;Hub 放走廊 2、
  东移 2.5 mm;扇出圈 / 小件间距 1.5(去耦例外)/ 9 A 过孔阵禁区(place_base)
- 叠层按用户决定改为 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面
- 布线:D± 七对按构造布通(USB3 掉头、USB1/2 北去掉头、USB4/5 东绕、
  J23 桥),+3V3 / GND / +12V 平面网每脚一孔,0.125 网格 + PathFinder
  开关(route_base、remote_route.ps1);剩 37 个网待手工补
- 铺铜:4 层重写,入口链自南向北切段 + 断面校核(pour_base);
  已下发布线 / 缝合孔 / 铜面轮廓,填铜未成功(待查)
- 工具:plot_base / plot_routes / shot_base,board_check 通孔两层都算

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
2026-09-10 21:02:10 +08:00

105 行
4.4 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""离线画底板布线缓存(L4 / L1 并排,从上往下看),失败的脚标红叉。
/c/Python314/python .claude/r2/plot_routes.py <out.png>
/c/Python314/python .claude/r2/plot_routes.py <out.png> --zoom 30 25 65 50 # 只看一块(mm)
只读 `_base_routes.json` + `_base_pads.json`,不碰 EasyEDA、不跑 A*。
"""
import io
import json
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(HERE))
sys.path.insert(0, HERE)
import matplotlib # noqa: E402
matplotlib.use('Agg')
import matplotlib.pyplot as plt # noqa: E402
from matplotlib.patches import Rectangle, Polygon, Circle # noqa: E402
import pcb_base as B # noqa: E402
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
POWER = ('VIN_RAW', 'VIN_FUSED', 'VIN_SW', '+5V', '+3V3', '+3V3_HUB', 'FAN_12V_SW')
def color(net):
if net == 'GND':
return '#2e9e44'
if net == '+12V':
return '#d62728'
if net.endswith('_DP') or net.endswith('_DM') or net in ('UP_DP', 'UP_DM'):
return '#8e44ad'
if net in POWER:
return '#ff8c00'
return '#1f5fbf'
def main():
out = sys.argv[1]
zoom = None
if '--zoom' in sys.argv:
k = sys.argv.index('--zoom')
zoom = [float(v) for v in sys.argv[k + 1:k + 5]]
c = json.load(io.open(os.path.join(HERE, '_base_routes.json'), encoding='utf-8'))
pads = json.load(io.open(os.path.join(HERE, '_base_pads.json'), encoding='utf-8'))
pads = [p for p in pads if p[4] >= 0.05 and p[5] >= 0.05]
fail = set()
for m in c['logs']:
for ref, pin in re.findall(r'([A-Z]+[0-9]+[a-z]*)\.([A-Za-z0-9#]+)', m):
fail.add(('J1' if ref == 'J1dc' else ref, pin))
fig, axes = plt.subplots(1, 2, figsize=(19, 9.8), dpi=150)
for ax, lay, title in ((axes[0], 2, u'L4 器件面'), (axes[1], 1, u'L1 插槽面')):
ax.add_patch(Polygon(B.outline_pts(), closed=True, fill=False, lw=1.0, ec='black'))
if lay == 1:
for r in B.l1_12v_rects():
ax.add_patch(Rectangle((r[0], r[1]), r[2] - r[0], r[3] - r[1],
fc='#f7c6c6', ec='none', alpha=0.6))
for p in pads:
if p[6] not in (lay, 12):
continue
ax.add_patch(Rectangle((p[2] - p[4] / 2, p[3] - p[5] / 2), p[4], p[5],
fc='#bbbbbb' if p[6] == lay else '#dddddd', ec='none'))
for s in c['segs']:
if s[0] != lay:
continue
lw = max(0.3, s[5] * (3.0 if zoom else 1.2))
ax.plot([s[1], s[3]], [s[2], s[4]], color=color(s[6]), lw=lw, solid_capstyle='round')
for v in c['vias']:
ax.add_patch(Circle((v[0], v[1]), 0.3, fc=color(v[2]), ec='black', lw=0.2))
for p in pads:
if (p[0], str(p[1])) in fail and p[6] in (lay, 12):
ax.plot(p[2], p[3], 'x', color='red', ms=6 if zoom else 3, mew=1.2)
for _n, (hx, hy) in B.MOUNT_HOLES.items():
ax.add_patch(Circle((hx, hy), B.HOLE_D / 2, fc='white', ec='black'))
if zoom:
ax.set_xlim(zoom[0], zoom[2])
ax.set_ylim(zoom[1], zoom[3])
# 放大时标出焊盘的「位号.脚」,失败的那几只一眼能认
for p in pads:
if p[6] in (lay, 12) and zoom[0] <= p[2] <= zoom[2] and zoom[1] <= p[3] <= zoom[3]:
if p[0].startswith('J3') and p[7] in ('GND', ''):
continue
ax.text(p[2], p[3], '%s.%s' % (p[0], p[1]), fontsize=3.2, ha='center',
va='center', color='red' if (p[0], str(p[1])) in fail else '#333333')
else:
ax.set_xlim(-1, B.BW + 1)
ax.set_ylim(-1, B.BH + 1)
ax.set_aspect('equal')
ax.set_title(title, fontsize=10)
ax.tick_params(labelsize=6)
ax.grid(True, lw=0.2, alpha=0.4)
fig.suptitle(u'底板布线缓存:%d 段 / %d 孔 / %d 条未完成 紫 = D± 红 = +12V 绿 = GND 橙 = 电源 蓝 = 信号 红叉 = 失败的脚'
% (len(c['segs']), len(c['vias']), len(c['logs'])), fontsize=10)
fig.tight_layout()
fig.savefig(out)
print('->', out)
if __name__ == '__main__':
main()