免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

用Interactive Html Bom把PCB版图与BOM变成可交互HTML

用Interactive Html Bom把PCB版图与BOM变成可交互HTML 简介InteractiveHtmlBomForAD 是一款面向 ADAutoDesk Inventor的交互式物料清单生成工具基于前端技术开发服务电子设计与结构工程等需要频繁维护 BOM 的岗位它把 AD 工程数据解析为可在浏览器中直接查看和编辑的清单避免手工整理表格效率低、易出错的问题。压缩包共 30 个文件以十七个脚本文件为主另有网页、文档、样式、批处理等类型整体约139KB脚本承担核心逻辑与轻量模块网页用于界面展示文档说明配置方法批处理用于环境初始化和清理目录结构清晰便于按需修改和二次开发。目前已有1035人学习下载。借助该源码用户可同时获得源码级核心库、可直接部署的打包产物以及配套说明快速搭建属于自己的 BOM 生成流程无论是常规选型、成本核算还是生产备料都能大幅减少漏项和格式错误提升数据一致性与协同效率。1. Interactive Html Bom 是什么AD 项目为什么需要单独的工具常规流程里AD 的 BOM 是一张 CSV 表格位号、数量、封装齐全可真到返修和装配环节你得把表格里的 R27 翻译成板子上焊盘旁边那个点表格做不到。Interactive Html Bom 把 PCB 版图和 BOM 拼进同一个 HTML左侧是带网格的板面图右侧是按值/封装分组的 BOM 列表点任意元件或 BOM 行另一侧的对应位号或整组元件全部高亮坐标、旋转、层信息同步显示。这份 HTML 是自包含单文件浏览器直接打开不用起服务、不依赖在线库适合发给 SMT 工厂或塞进设计评审文档。AD 自己没有这个输出所以从业者通常攒一个小工具把 AD 的放置文件、BOM 文件按位号对齐再灌进 iBOM 渲染模板。Layout 自检、备料核对、评审时定位嫌疑元件都靠它。2. AD 侧数据源放置文件、BOM 与板框尺寸2.1 放置文件是元件坐标的唯一完整来源在 PCB 编辑环境用File → Assembly Outputs → Generates pick and place files导出格式选 CSV、单位选 Metric得到的就是每个元件一行坐标的文件。这是 iBOM 的骨架没有它HTML 里画不出任何东西。列名含义注意事项Designator位号如 R12与原理图一致BOM 合并时靠它做主键Footprint封装名如 C0603不同 AD 封装库写法可能不同别用来当值Mid X / Mid Y元件中心坐标单位由导出对话框决定默认可能是 milRef X / Ref Y元件参考点坐标与原点设置有关iBOM 一律用 MidLayerTop / Bottom底层元件需要额外标识不能丢Rotation旋转角度逆时针为正范围 0-360Comment元件值常放容值/阻值也可能放型号导出对话框里有两个高频坑单位误选成 mil 会让整个板子尺寸放大 25.4 倍Format 选成文本制表符分隔后某些 AD 版本会把带逗号的 Comment 直接切成多列。我都用 CSV 格式并在脚本里先做列名校验发现解析异常立即退出而不是带着错数据往下跑。2.2 BOM 文件里的位号是折叠的BOM 用Reports → Bill of Materials生成AD 默认会按 Comment Footprint 分组并把同一组的所有位号写进一个单元格例如C1,C2,C4-C6或R1-R10。这个行为对人工看非常友好对 iBOM 却是个大麻烦因为渲染阶段需要的是每个位号独立一行。导出时至少保留四个字段Designator、Comment、Footprint、Description。Quantity 列不要直接信任后面脚本会用展开后的位号数量重新算一遍——这是防呆设计因为原理图里贴了不焊接的元件位号时BOM 的 Quantity 可能与展开结果不一致。2.3 板框尺寸和封装外形怎么补iBOM 的底板并不需要 Gerber最简单的方式是拿放置文件全部 Mid 坐标算 min/max得到一个外接矩形作为板框。对矩形板够用异形板就得在 AD 里确认原点后把板框层导出为 DXF/DWG再转成 SVG 或一组折线坐标。板框的作用有两个视觉上知道元件在哪以及后面验证坐标归一化是否正确。封装外形是另一个缺口。放置文件里没有封装尺寸只有封装名。常见做法两种要么内置一张常见封装的近似尺寸表0603、0805、SOT-23 这类要么写一个 AD 脚本在 PCB 上遍历 Footprint 对象把每个位号的包围盒取出来一并导出。前者上手快后者画出来的元件形状和实物一致SMT 核对时更直观。3. 快速 BOM 工具的核心从 CSV 到 JSON 的合并管线3.1 以位号为主键对齐两份文件放置文件逐行一个位号BOM 逐组一个折叠位号串合并顺序错了就会丢元件。正确顺序是先读放置文件建索引再展开 BOM 位号去索引里查坐标。import csv, re, json, sys def read_pnp(path): rows {} with open(path, newline, encodingutf-8-sig) as f: for row in csv.DictReader(f): ref row[Designator].strip() rows[ref] row return rows def expand_refs(token): refs [] for part in re.split(r[\s,;], token.strip()): if - in part: left, right part.split(-, 1) prefix re.sub(r\d$, , left) n1 int(re.findall(r\d, left)[-1]) n2 int(re.findall(r\d, right)[-1]) refs [f{prefix}{i} for i in range(n1, n2 1)] else: refs.append(part) return refsread_pnp把放置文件变成{位号: 行数据}的字典因为后面要被 BOM 组的位号反复查询expand_refs负责把C4-C6这类区间展开成三个独立位号同时容忍空格、逗号、分号混合分隔。这段代码对 AD 导出的 BOM 做的唯一假设是位号列存在凡是列名带designator、Designator或位号都先归一化再使用避免不同模板之间来回改。3.2 分组、数量重算与位号压缩拿到每个位号的坐标和值之后按Comment Footprint重新分组再用压缩算法把同组位号折叠回区间表示这样右侧 BOM 列表不会出现几十行单条位号。def compress_refs(refs): parsed [] for r in refs: pre re.sub(r\d$, , r) num int(re.findall(r\d, r)[-1]) parsed.append((pre, num)) parsed.sort(keylambda x: (x[0], x[1])) out, cur [], [] for pre, num in parsed: if cur and (pre ! cur[-1][0] or num ! cur[-1][1] 1): out.append(cur); cur [] cur.append((pre, num)) out.append(cur) parts [] for group in out: pre group[0][0] nums [n for _, n in group] parts.append(pre str(nums[0]) if len(nums) 1 else f{pre}{nums[0]}-{pre}{nums[-1]}) return ,.join(parts) def build_json(pnp_path, bom_path, unitsmm): pnp read_pnp(pnp_path) groups [] with open(bom_path, newline, encodingutf-8-sig) as f: for row in csv.DictReader(f): refs [] for token in row[Designator].split(,): refs expand_refs(token) refs [r for r in refs if r in pnp] if not refs: continue key (row[Comment].strip(), row[Footprint].strip()) groups.append({ value: key[0], footprint: key[1], refs: sorted(refs), qty: len(refs) }) return {units: units, groups: groups}这段代码把 BOM 的每一组展开后逐个位号去放置文件字典里查存在性查不到的直接丢弃并留下隐患所以在丢弃前我会往终端打印WARN: C90 not in PnP。重新计算的qty以实际存在于 PCB 上的位号数为准避免 BOM 里残留了不参与装配的元件。压缩函数里R1-R10这类连续区间被合并位号乱序也会先排序再压缩HTML 侧列表可读性会好很多。3.3 坐标转换、底层元件与旋转方向AD 的坐标系以左下角为原点、Y 轴向上而浏览器画布的 Y 轴向下渲染前必须翻转。常用的做法是先算全部 Mid 坐标的 min/max再把 Y 翻转后平移到画布内。def normalize_components(pnp_rows, board): components [] for ref, row in pnp_rows.items(): x float(row[Mid X]) y float(row[Mid Y]) if row[Layer].lower().startswith(bottom): layer bottom else: layer top components.append({ ref: ref, layer: layer, x: x, y: y, rot: float(row[Rotation]) % 360, value: row.get(Comment, ).strip(), footprint: row.get(Footprint, ).strip() }) xs [c[x] for c in components] ys [c[y] for c in components] board.update({minX: min(xs), minY: min(ys), maxX: max(xs), maxY: max(ys)}) return components归一化时注意两点旋转角度对 360 取模避免出现 450 这样的脏数据底层元件不在这里翻转坐标只标记layer: bottom翻转交给渲染层统一处理。这样顶层元件和底层元件在同一个板面坐标系里做自检时切到“仅底层”视图也不会有一半元件跑到板外。4. 用 InteractiveHtmlBom 风格的 HTML 模板把 JSON 渲染成单文件4.1 数据内联与 SVG 画布初始化渲染层不必依赖任何框架把第 3 章生成的 JSON 直接内联进 HTML再用原生 SVG 画元件矩形即可。单文件的关键在于数据不经过外部请求全部写在script typeapplication/json里。!DOCTYPE html html headmeta charsetutf-8titleiBOM/title style body{margin:0;display:flex;font-family:Consolas,monospace} #panel{width:60%;height:100vh} #bom{width:40%;height:100vh;overflow:auto;font-size:13px} rect.comp{fill:#d4e6f1;stroke:#2e86c1;cursor:pointer} rect.comp.hl{fill:#f39c12;stroke:#e67e22} /style/head body div idpanelsvg idboard width100% height100%/svg/div div idbom/div script typeapplication/json idbom-data { units: mm, board: {minX: 0, minY: 0, maxX: 100, maxY: 80}, groups: [ {value: 100nF, footprint: C0603, refs: [C1, C2], qty: 2} ], components: [ {ref: C1, layer: top, x: 10.16, y: 20.32, rot: 90, value: 100nF, footprint: C0603} ] } /script script const raw JSON.parse(document.getElementById(bom-data).textContent); const svg document.getElementById(board); const B raw.board, pad 12; const sx (svg.clientWidth - pad * 2) / (B.maxX - B.minX); const sy (svg.clientHeight - pad * 2) / (B.maxY - B.minY); const scale Math.min(sx, sy); const px x pad (x - B.minX) * scale; const py y pad (B.maxY - y) * scale; /script /body /htmlpx和py完成从毫米到像素的映射py里的一次maxY - y就是解决 AD 与浏览器坐标系差异的关键。数据 JSON 中 board 的外接矩形由脚本计算HTML 模板只负责按比例缩放。这样只要 AD 导出的坐标没错渲染位置就不会系统性偏移。4.2 点击联动、BOM 分组高亮与过滤器元件渲染和 BOM 面板联动是交互体验的核心逻辑拆成三个函数渲染元件、渲染 BOM 行、选中状态同步。for (const c of raw.components) { const r document.createElementNS(http://www.w3.org/2000/svg, rect); let w 3 * scale, h 1.5 * scale; // 无封装尺寸时的默认占位 r.setAttribute(class, comp); r.setAttribute(data-ref, c.ref); r.setAttribute(x, px(c.x) - w / 2); r.setAttribute(y, py(c.y) - h / 2); r.setAttribute(width, w); r.setAttribute(height, h); r.setAttribute(transform, rotate(${-c.rot} ${px(c.x)} ${py(c.y)})); r.addEventListener(click, () highlightRef(c.ref)); svg.appendChild(r); } function highlightRef(ref) { document.querySelectorAll(.comp).forEach(el el.classList.toggle(hl, el.dataset.ref ref)); const group raw.groups.find(g g.refs.includes(ref)); renderBom(group ? group.refs : [ref]); } function renderBom(visitedRefs) { const bom document.getElementById(bom); bom.innerHTML raw.groups.map((g, i) { const hit g.refs.some(r visitedRefs.includes(r)); return div classbom-row>python ad2json.py pick_and_place.txt bom_export.csv \ --out iBOM_MOTOR_V2.html \ --units mm \ --group-by comment:footprint \ --compress-refs \ --check-duplicate-refs参数含义分别是--out指定输出文件最终是自包含 HTML--units声明放置文件单位mils 时内部先除以 25.4 再参与计算--group-by控制 BOM 分组维度默认按值和封装--compress-refs把连续位号压缩成区间--check-duplicate-refs在检测到同一 bit 位号出现两次时直接报错。这个脚本生成时把 JSON 嵌入 HTML 的同时会额外写一份iBOM_data.json供后续自动化校验用不参与渲染。配置项默认值作用unitsmm决定坐标是否做 mil 换算scaleauto画布内自动适配不建议手工指定highlightColor#f39c12选中元件填充色按封装类型可扩展showRefTextfalse是否在每个元件旁显示位号文本bottomLayerColor#b0bec5底层元件独立配色避免与顶层混淆大板子几千个元件时SVG 节点数量会明显拉高内存占用。如果发现拖动卡顿我一般把showRefText关掉只保留点击后显示位号再不行就把元件渲染从rect换成circle节点更轻视觉上也够用。5. 拿到 HTML 后怎么确认它没有张冠李戴5.1 用钻孔文件校板框打开 AD 导出的 NC Drill 文件Excellon 格式里所有钻孔的 X/Y 坐标范围理论上应该和放置文件的外接矩形吻合。用一个三五行的小脚本读一遍和iBOM_data.json里的board字段对比容差留 0.05mm。python - PY import json, re d json.load(open(iBOM_data.json)) xs, ys [], [] for line in open(drill.TXT, errorsignore): m re.search(rX(-?\d\.?\d*)Y(-?\d\.?\d*), line) if m: xs.append(float(m.group(1)) / 25.4) ys.append(float(m.group(2)) / 25.4) b d[board] assert abs(min(xs) - b[minX]) 0.05 assert abs(max(xs) - b[maxX]) 0.05 print(board ok, min(xs), max(xs), min(ys), max(ys)) PY这段命令把钻孔文件的 mil 坐标统一除以 25.4 转成毫米再与 iBOM 板框对比。板框对不上时多半是放置文件单位选错或 AD 原点与钻孔原点不一致趁早发现比焊完料再核对成本低得多。5.2 位号抽查与重复位号拦截随机抽十个位号在 AD 里用快捷键跳转再回到 HTML 点击同一元件观察高亮位置是否一致。注意看底层元件AD 里旋转 90 度的 Bottom 元件HTML 里应该显示为旋转负 90 度这是镜像关系决定的不是 bug。把这条写进检查清单里因为绝大多数人第一次看到 Bottom 元件方向都会怀疑工具坏了。批量验证的习惯是保留ad2json.py的--check-duplicate-refs这是我最常开的一个开关。AD 工程中复制粘贴模块时容易产生重复位号普通 BOM 看不出来iBOM 一渲染就会出现两个元件抢一个位号点谁都是高亮俩。5.3 大板性能与文件体积的控制几千个元件的板子HTML 文件体积主要来自内联 JSON 和 SVG 节点。--compress-refs开启后BOM 分组里的位号数组会以区间字符串存储渲染时再展开文件体积能省 30% 左右。若还想再快把丝印文本、板框以外的装饰层去掉只保留元件矩形和 BOM 列表交互流畅度提升明显。做完这些把 HTML 和iBOM_data.json一起放进工程仓库的 docs 目录设计评审时浏览器一开翻三份文件的功夫全省了。本文还有配套的精品资源点击获取
返回列表