2026/2/26 18:44:15
网站建设
项目流程
uc网站怎么做,2021全国大学生营销大赛,怎样做网站检索数据分析,wordpress 开启缩略图从Gerber到PCB#xff1a;一场硬核的硬件逆向之旅你有没有遇到过这样的场景#xff1f;手头有一块老旧电路板#xff0c;想复制或改进它#xff0c;但原厂早已停产#xff0c;设计文件也无处可寻。这时候#xff0c;唯一能拿到的数据可能就是一叠.gbr文件——也就是我们常…从Gerber到PCB一场硬核的硬件逆向之旅你有没有遇到过这样的场景手头有一块老旧电路板想复制或改进它但原厂早已停产设计文件也无处可寻。这时候唯一能拿到的数据可能就是一叠.gbr文件——也就是我们常说的Gerber文件。这些文件原本是用来给PCB工厂“照着打样”的图纸只包含图形信息不带任何电气逻辑。而我们的目标是把它们还原成像 KiCad 或 Altium 中那样可以编辑、仿真、改版的完整 PCB 工程文件。这听起来像是“读图重建”其实是一场融合了文本解析、几何建模、图像处理和电气推理的技术挑战。今天我们就来亲手实现一个从零开始的Gerber 转 PCB 逆向系统原型带你一步步揭开这块“黑盒”背后的秘密。Gerber不是图片但它比代码还难懂很多人第一次打开 Gerber 文件时以为它是某种图像格式结果用看图软件打不开转头用记事本打开看到满屏类似X12345Y67890D01*的字符又以为是乱码。其实不然。Gerber 是一种二维矢量绘图语言准确地说是 PCB 行业专用的“数控机床指令集”。它的正式名称叫RS-274X 扩展格式由一系列坐标命令 图形模板D码构成告诉光绘机在哪个位置画什么形状。举个例子%FSLAX24Y24*% %MOMM*% %ADD10C,0.3*% D10* X10000Y20000D02* X15000Y25000D01*这段文字的意思是- 坐标格式为整数2位、小数4位即10000表示10.0000 mm- 单位制为毫米- 定义第10号D码为直径0.3mm的圆形- 移动到 (10.0000, 20.0000)- 画一条线到 (15.0000, 25.0000)你看它不像 Python 那样直观也不像 SVG 那样结构清晰。它是一种状态机驱动的语言当前的操作依赖于之前设定的单位、精度、D码和极性模式。所以第一步我们必须写一个鲁棒的解析器能读懂这种“工业方言”。构建你的第一台 Gerber 解析引擎下面这个 Python 类虽然简陋但已经具备了解析大多数标准 Gerber 文件的能力。它不做渲染只做一件事把原始文本流转换成机器可处理的事件序列。import re class GerberParser: def __init__(self): self.apertures {} # 存储D码定义 {10: {shape: C, size: 0.3}} self.events [] # 输出事件流 [(FLASH, x, y, dcode), ...] self.current_dcode None # 当前使用的D码 self.unit INCH # 默认单位 self.format (2, 5) # 坐标格式2位整数5位小数 self.x, self.y 0.0, 0.0 # 当前坐标 self.interpolation G01 # 插补模式 def parse_line(self, line): line line.strip().rstrip(*) if not line or line.startswith(G04) or line.startswith(;): return # 跳过注释 # 解析格式指令 FSLAXxxYyy if line.startswith(%FSLAX): match re.search(rFSLAX(\d)Y(\d), line) if match: self.format (int(match.group(1)), int(match.group(2))) # 单位切换 elif line %MOMM: self.unit MM elif line %MOIN: self.unit INCH # D码定义 ADDxD,s... elif line.startswith(%ADD): self._parse_aperture(line) # 切换D码 elif re.match(rD\d$, line): self.current_dcode int(line[1:]) # G指令 elif line in (G01, G02, G03): self.interpolation line # X/Y坐标动作 elif line.startswith((X, Y)): self._process_coord_line(line) def _parse_aperture(self, line): # 支持简单圆形/矩形 D码 %ADD10C,0.5* 或 %ADD11R,1.0X0.5* pattern r%ADD(\d)([CRO]),([\d.])(?:X([\d.]))?(\*?)$ match re.match(pattern, line) if not match: return dnum int(match.group(1)) shape match.group(2) size1 float(match.group(3)) size2 float(match.group(4)) if match.group(4) else size1 self.apertures[dnum] {shape: shape, size: (size1, size2)} def _process_coord_line(self, line): # 提取X和Y值可能只有其中一个 x_match re.search(rX(-?\d), line) y_match re.search(rY(-?\d), line) if x_match: raw_x x_match.group(1).zfill(self.format[0] self.format[1]) self.x float(f{raw_x[:-self.format[1]]}.{raw_x[-self.format[1]:]}) if y_match: raw_y y_match.group(1).zfill(self.format[0] self.format[1]) self.y float(f{raw_y[:-self.format[1]]}.{raw_y[-self.format[1]:]}) cmd line[-3:] if cmd D01: # 曝光画线 self.events.append((LINE_END, self.x, self.y, self.current_dcode)) elif cmd D02: # 移动抬笔 self.events.append((MOVE_TO, self.x, self.y, self.current_dcode)) elif cmd D03: # 独立曝光焊盘 self.events.append((FLASH, self.x, self.y, self.current_dcode))✅提示实际项目中建议使用gerber-parser这类成熟库但自己动手写一遍才能真正理解那些“莫名其妙”的星号和百分号到底代表什么。这个解析器输出的是一个事件列表比如[ (MOVE_TO, 10.0, 20.0, 10), (LINE_END, 15.0, 25.0, 10), (MOVE_TO, 30.0, 40.0, 10), (FLASH, 30.0, 40.0, 10) ]接下来的任务就是把这些“动作”变成真正的图形对象走线、焊盘、填充区……把线条变回“电路”几何重建与层对齐有了事件流还不够。我们需要构建更高层次的几何模型FLASH 事件 → 焊盘Pad连续 LINE 事件 → 多段线Track封闭区域 → 铺铜Copper Pour但这一步有个大坑不同层的坐标系可能不一致。比如顶层.gtl和底层.gbl的原点偏移了几毫米或者旋转了一个角度。如果不校准后续的过孔连接分析就会全错。如何自动对齐各层最常用的方法是找基准点fiducial marks——那些板子上常见的三个大圆点。它们通常出现在同一位置的所有层上可以用作配准锚点。算法思路如下在每一层提取所有直径大于1mm的圆形焊盘计算每组三点之间的距离和夹角匹配具有相同几何关系的一组点作为 fiducial使用最小二乘法求解仿射变换矩阵将其他层映射到参考层坐标系下。def align_layers(layers, ref_layerTOP): 基于基准点对齐所有层 ref_fiducials find_fiducials(layers[ref_layer]) for name, layer in layers.items(): if name ref_layer: continue src_fiducials find_fiducials(layer) if len(src_fiducials) 3: T solve_affine_transform(src_fiducials, ref_fiducials) apply_transform(layer, T)一旦完成对齐我们就可以放心地进行跨层分析了。没有网络表那就自己造一个这才是整个逆向工程中最关键也最难的部分如何从一堆图形中恢复出电气连接关系要知道Gerber 文件本身不含网络信息。同一个 VCC 网络上的五个焊盘在 Gerber 里只是五个独立的圆圈。要把它们重新归为一类必须依靠两种线索物理连接同一层内相连的铜皮属于同一网络过孔连接通过金属化孔连通的不同层焊盘属于同一网络。第一步识别导体岛Conductor Island我们可以把每个信号层当作一张黑白图白色是基材黑色是铜皮。然后使用连通域分析Connected Component Analysis来找出每一个独立的导电区域。这里有个技巧不要直接栅格化而是用矢量合并算法。例如两条共线且端点重合的线段应被合并为一条长线多个相邻焊盘若被走线连接也应视为同一导体。def extract_conductors(events, tolerance0.1): segments [] current_start None for evt in events: if evt[0] MOVE_TO: current_start (evt[1], evt[2]) elif evt[0] LINE_END and current_start: end (evt[1], evt[2]) segments.append((current_start, end)) current_start end elif evt[0] FLASH: dcode parser.apertures[evt[3]] radius dcode[size][0] / 2 pad create_circle(evt[1], evt[2], radius) segments.append(pad) # 视为微小线段或区域 # 合并接近的线段与焊盘 return merge_segments(segments, toltolerance)最终得到的是一个字典{ TOP: [cond1, cond2, ...], # 每个cond是一个Point集合或Polygon BOTTOM: [condA, condB, ...] }第二步引入钻孔文件Excellon没有.drl文件几乎不可能准确重建网络。因为插件孔、过孔的位置就藏在这里。解析 Excellon 其实比 Gerber 更简单主要是 NCDrill 格式M48 FMAT,2 INCH,LZ T01C0.635 T02C1.0 % T01 X000100Y000100 X000200Y000100 T02 X000150Y000200 M30我们关心的是- 每个孔的位置(x, y)- 是否金属化Plated- 直径大小有了这些信息就能判断哪些导体是通过过孔连在一起的。第三步构建网络表Netlist现在万事俱备。我们可以遍历所有导体检查是否有共享的过孔或重叠的焊盘。def build_netlist(conductors_by_layer, vias): nets {} net_id 0 visited set() for layer_name, conds in conductors_by_layer.items(): for i, cond in enumerate(conds): if (layer_name, i) in visited: continue # 发现新网络 net_name fNET{net_id} net_id 1 nets[net_name] [] stack [(layer_name, cond)] while stack: lyr, c stack.pop() if (lyr, get_index(conds_in_layer[lyr], c)) not in visited: visited.add((lyr, get_index(conds_in_layer[lyr], c))) nets[net_name].append((lyr, c)) # 查找连接的过孔 connected_vias find_intersected_vias(c, vias) for via in connected_vias: # 查找其他层中包含该过孔的导体 for other_lyr in conductors_by_layer: if other_lyr lyr: continue for j, other_c in enumerate(conductors_by_layer[other_lyr]): if (other_lyr, j) not in visited and \ distance(via[pos], centroid(other_c)) via[diameter]/2 0.1: stack.append((other_lyr, other_c)) return nets最终输出的就是一份标准的网络表可以导出为.net文件供 EDA 工具导入。能不能一键生成 KiCad 项目当然可以虽然不能做到 100% 完美但我们已经拿到了最关键的信息几何图形 → 转为kicad_pcb中的gr_line,pad,zone网络表 → 写入.net并关联到封装引脚层堆叠 → 根据文件名推断 TOP/BOTTOM/CREAM/SILK 等KiCad 的 PCB 文件本质上是 S-表达式文本结构清晰。你可以用模板引擎如 Jinja2批量生成。简化示例template (pcbnew_file_version 20220706) (kicad_pcb (version 20220706) (layers (0 F.Cu signal) (31 B.Cu signal) ) {% for pad in pads %} (pad {{ pad.name }} smd {{ pad.shape }} (at {{ pad.x }} {{ pad.y }}) (size {{ pad.sz_x }} {{ pad.sz_y }}) ...) {% endfor %} ) rendered Template(template).render(padstop_pads bottom_pads) with open(output.kicad_pcb, w) as f: f.write(rendered)虽然缺少元器件封装和原理图但至少你能看到完整的布线结构并手动添加元件进行修改。实战中的坑与避坑指南别以为跑通上面几步就万事大吉。真实世界远比理想复杂得多。问题原因应对方案走线明明连着却断开Gerber 分割过细两点间距略超容差设置动态连接阈值0.05~0.15mm阻焊层误识别为走线忘记过滤.gts/.gbs层明确区分 Copper / Mask / Silk负片层无法处理电源平面用“挖空”表示走线特殊处理先填满再减去负形自定义宏D码AM看不懂使用 AM 定义异形焊盘需递归解析%AMxxx*...%块微型BGA焊盘粘连密度过高导致合并错误引入密度聚类DBSCAN分离还有一个致命问题没有元件轮廓怎么办答案是只能靠猜。你可以根据焊盘布局反推可能是 QFP、SOP 还是 BGA然后手动绑定封装。未来或许可用 CNN 模型辅助识别常见封装类型。这项技术到底有什么用也许你会问“抄别人板子合法吗”重点不在“抄”而在“理解”与“延续”。设备维修医院里的老式监护仪坏了厂家不再提供备件只能靠逆向复刻国产替代某进口模块停供需快速克隆功能并适配国产芯片教学研究学生学习优秀设计的布局布线技巧安全审计确认某军用设备中是否存在后门电路。甚至有些开源硬件项目只发布了 Gerber你需要把它还原成可编辑版本才能二次开发。结语我们离全自动还有多远目前还没有任何一个开源工具能做到“一键 Gerber 转 PCB”。商业软件如PCBReverse或ScanCAD也仍需大量人工干预。但这条路并非遥不可及。随着以下技术的发展AI 图像分割U-Net 可用于精准提取铜皮边界图神经网络将导体与过孔建模为图节点预测连接关系知识库匹配建立常见封装库自动推荐元件型号EDA 插件生态在 KiCad 内集成逆向模块实现闭环编辑未来的某一天我们或许真的能对着一块旧板子拍照扫描 Gerber几分钟后就在电脑里看到可编辑的原理图草稿。而现在正是打好基础的时候。如果你愿意不妨从今天开始运行一下那段 Python 解析器看看你手上的第一个.gbr文件里究竟藏着怎样的电路故事。 想试试看欢迎在评论区留下你的逆向经历或者分享你在解析过程中遇到的奇葩格式。我们一起打造更强大的开源逆向工具链。