首页 / 资讯中心 / 文章详情

2D板材排版套料开源代码实战:从算法选型到利用率优化

2D板材排版套料开源代码实战:从算法选型到利用率优化 ★ FEATURED ARTICLE
简介这是一份面向制造业与CAD/CAM开发者的2D板材排版套料开源代码聚焦激光切割、水切割等场景下的排样优化问题。项目以遗传算法为核心通过选择、交叉与变异迭代求解板材利用率近似最优解适合具备一定前端与算法基础、希望研究自动排样系统的技术人员。压缩包为rar格式共88个文件、约2.53MB以JavaScript源码为主体辅以SVG矢量图形、字体文件eot/woff/ttf/woff2、CSS样式与HTML页面另含少量配置与说明文档整体结构接近一个可直接运行的Web应用。目前已有6261人学习下载。读者可从中获取完整的遗传算法排样实现、SVG图形解析与布局渲染逻辑、适应度评估与并行计算模块以及可二次定制的工程骨架便于理解算法落地细节并扩展切割工艺约束。1. 2D板材排版套料开源代码从一张钢板到一份可执行排样方案车间里最常见的浪费不是切坏了而是切剩了。一张 2440×1220 的板材排得好能出 12 个件排得差只能出 9 个中间差的不是手艺是算法。2D板材排版套料开源代码要解决的就是这件事在给定板材尺寸、零件形状、数量、纹理方向和间距约束下自动算出怎么摆最省料。它适合三类人——做激光切割/等离子下料的工艺员、想给自家 MES 或 CAM 加排样模块的开发者、以及做钣金家具或广告字牌的小厂技术负责人。你不需要从零推导几何算法开源社区里已经有可跑的骨架关键是知道选哪套、怎么接自己的数据、参数怎么调才不翻车。2. 套料问题的数学骨架与开源方案选型2.1 排样问题到底难在哪NP-hard 与三类约束2D 排样在学术上叫 2D Irregular Strip Packing 或 2D Bin Packing前者允许零件旋转、板材无限长后者板材固定、零件数量有限。钣金下料多数落在后者板材尺寸固定零件数量确定目标是最小化板材张数或最大化利用率。它的难度在于组合爆炸——10 个零件有 10! 种排列100 个零件就是天文数字所以没有多项式时间的精确解工程上全靠启发式加局部搜索。约束分三类必须先在代码里建模清楚几何约束零件之间不重叠、不超出板材边界、零件可旋转角度集合常见 0°/90°也有 0°/180° 或任意角。工艺约束零件间距kerf 补偿、边距、纹理方向锁定、共边切割是否允许。数量约束每种零件需求数量、可混排的板材规格集合。很多开源代码跑出来结果难看不是算法差是约束没建模对。比如忘了设间距排出来零件贴在一起实际切割直接报废。2.2 开源方案怎么选三类技术路线对比目前能拿到的 2D 套料开源代码大致分三类选型时按你的零件形状复杂度决定路线代表思路适合形状利用率上限上手难度矩形装箱MaxRects / Guillotine全是矩形件85%~92%低多边形 NestingNFP 启发式任意多边形88%~95%中高栅格/像素法位图布尔运算异形、含孔80%~88%中矩形件占多数的钣金厂直接用 MaxRects 类实现就够了代码短、跑得快。异形件比如法兰、支架必须上 NFPNo-Fit Polygon临界多边形路线核心是先算两两零件的不重叠可行域再用启发式在可行域里选落点。栅格法实现简单但精度受网格分辨率限制分辨率调高内存爆炸调低边缘锯齿导致实际切不出来一般只做快速预览。我一般建议先用矩形装箱跑通数据链路确认 DXF 解析、数量汇总、结果导出没问题再换 NFP 内核。这样出问题时能快速定位是数据层还是算法层。2.3 最小可跑骨架数据流与模块划分一套能落地的套料代码模块划分比算法本身更重要。常见做法是拆成四层# nesting_pipeline.py # 套料主流程骨架解析 - 预处理 - 排样 - 导出 from dxf_parser import load_parts # DXF 解析输出多边形顶点 from preprocess import normalize, merge_duplicates # 单位统一、重复件合并 from nester import MaxRectsNester # 排样内核可替换为 NFP from exporter import to_dxf, to_csv # 结果导出 def run(dxf_dir, sheet_w, sheet_h, gap, allow_rotate): parts load_parts(dxf_dir) # 每个零件: {id, poly, qty, rotatable} parts normalize(parts, unitmm) # 统一到毫米避免英寸混入 parts merge_duplicates(parts) # 相同几何合并数量减少搜索规模 nester MaxRectsNester( sheet(sheet_w, sheet_h), gapgap, # 零件间距等于割缝安全余量 allow_rotateallow_rotate # 是否允许 90 度旋转 ) layout nester.pack(parts) to_dxf(layout, out/nest_result.dxf) to_csv(layout, out/report.csv) # 利用率、板材张数、每张摆放清单 return layout if __name__ __main__: run(./parts, 2440, 1220, gap3.0, allow_rotateTrue)逻辑说明load_parts负责把 DXF 里的 LWPOLYLINE、CIRCLE 等实体转成闭合多边形这一步最容易出问题圆弧离散精度直接决定后续几何计算是否稳定。normalize统一单位很多翻车案例是图纸里混了英寸和毫米。merge_duplicates把几何相同的零件合并数量能把搜索规模降一个量级。MaxRectsNester是内核接口设计成pack(parts) - layout方便你后面换成 NFP 实现而不动上下游。参数说明gap不是随便填的它等于割缝宽度加安全余量激光切割一般 0.2~0.5mm 割缝但热变形和定位误差要留 2~3mm所以实际填 3.0 是常见值。allow_rotate对纹理件必须设 False否则排出来纹理方向乱了外观件直接报废。3. 用 NFP 内核把异形件排样跑起来3.1 NFP 的计算流程与关键几何操作NFP 的核心思想对零件 A 和 B把所有让 B 与 A 刚好接触的位置连起来形成一条闭合边界B 的参考点落在这条边界外就说明不重叠。有了 NFP排样就变成在可行域里找最左下角或最省空间的落点。计算流程分四步多边形预处理去自交、统一绕向逆时针、凸化分解凹多边形拆成凸块。两两算 NFP常用 Minkowski 和凸多边形用顶点归并凹多边形先分解再合并。求可行域板材边界内缩 gap减去已放置零件的 NFP 并集。选落点按启发式最低重心、最左、最小包围盒增量在可行域顶点里挑。凹多边形分解是性能瓶颈也是精度陷阱。分解算法选不好NFP 会出现自交排出来的零件看着不重叠实际有微小穿插切割时才发现。3.2 落点启发式与搜索顺序利用率差 5% 的地方同样的 NFP落点策略不同利用率能差 5 个百分点。常见启发式Bottom-Left选最低再最左的可行点实现简单适合矩形件。Bottom-Left-Fill在 Bottom-Left 基础上优先填洞异形件提升明显。Minimum Bounding Box选让整体包围盒增量最小的点适合板材张数敏感场景。搜索顺序同样关键。零件按面积从大到小排是常规做法但纯面积排序会让小件最后没地方塞。我一般用面积降序 长宽比分组把细长件单独一批避免它们在大件缝隙里卡住。# nester.py 片段落点选择与搜索顺序 def sort_parts(parts): # 面积降序长宽比接近的归为一组减少碎片 return sorted(parts, keylambda p: (-p.area, abs(p.aspect - 1.0))) def select_position(feasible_points, placed, sheet): best None for pt in feasible_points: # 优先最低其次最左最后看包围盒增量 score (pt.y, pt.x, bbox_growth(pt, placed, sheet)) if best is None or score best[0]: best (score, pt) return best[1]逻辑说明sort_parts先按面积降序再按长宽比与 1 的差值升序让接近正方形的件优先细长件靠后减少它们制造窄缝的概率。select_position用元组比较实现多级排序Python 里元组逐项比较天然支持最低优先、再最左、再包围盒增量最小。参数说明feasible_points是可行域顶点集合数量随零件数增长超过 200 个零件时要做剪枝只保留 Pareto 前沿上的点否则单次选点就卡住。bbox_growth是包围盒增量值越小说明这个落点让整体越紧凑。3.3 结果校验怎么确认排样真的能切排样结果不能只看利用率数字必须做几何校验。常见做法是写一个独立校验脚本把排样结果里所有零件多边形做两两布尔交集交集面积大于阈值就报错。# verify.py排样结果几何校验 from shapely.geometry import Polygon from shapely.ops import unary_union def verify(layout, sheet_w, sheet_h, gap, tol1e-6): errors [] for i, a in enumerate(layout.parts): pa Polygon(a.poly) # 边界检查 if not Polygon([(0,0),(sheet_w,0),(sheet_w,sheet_h),(0,sheet_h)]).contains(pa): errors.append(f零件 {a.id} 超出板材边界) # 重叠检查间距不足也算错 for b in layout.parts[i1:]: pb Polygon(b.poly) inter pa.buffer(gap/2).intersection(pb.buffer(gap/2)) if inter.area tol: errors.append(f零件 {a.id} 与 {b.id} 间距不足重叠面积 {inter.area:.3f}) return errors逻辑说明用buffer(gap/2)把两个零件各外扩半个间距再求交集这样能把间距不足也纳入重叠检查比单纯判重叠更严格。contains检查边界注意浮点误差必要时给板材边界加tol容差。参数说明tol是面积容差设 1e-6 是防止浮点误差误报。gap要和排样时一致否则校验标准对不上。这个脚本建议每次排样后自动跑作为出图前的最后一道关。4. 避坑套料代码落地最常见的 5 个翻车点4.1 圆弧离散精度不够导致零件缺肉现象排样结果看着正常导出 DXF 后零件边缘是折线圆弧处明显缺一块切割出来尺寸超差。原因DXF 解析时把圆弧按固定段数离散段数太少曲率大的地方弦高误差大。很多开源解析器默认 16 段对 R5 的小圆弧误差能到 0.1mm 以上。解决按弦高误差控制离散而不是固定段数。弦高误差取 0.01~0.02mm公式n ceil(pi / acos(1 - tol/r))r 越小段数越多。解析后立刻算多边形面积和 DXF 里圆弧理论面积对比偏差超过 0.5% 就报警。4.2 单位混用英寸图纸混进毫米板材现象排样利用率异常低零件小得离谱一张板上排了几百个件。原因部分 DXF 用英寸解析后没换算零件尺寸变成实际的 1/25.4自然排得下。解决解析时读 DXF 的$INSUNITS头变量为 1 是英寸、4 是毫米按它换算。读不到就按包围盒尺寸猜钣金件包围盒通常 10~2000mm如果算出来普遍小于 10大概率是英寸。统一在normalize里处理下游不再关心单位。4.3 间距设成 0 或只设割缝现象排样利用率很高但切割后零件边缘有粘连需要二次打磨。原因gap只填了割缝宽度0.2mm没留热变形和定位余量。激光切割热影响区、等离子切割挂渣、板材不平整都会吃掉余量。解决gap 割缝 工艺余量激光取 2~3mm等离子取 4~6mm火焰切割取 6~8mm。这个值按你的设备和材料试切确定不要照搬别人的参数。4.4 纹理方向没锁死外观件报废现象不锈钢拉丝件排样后纹理方向不一致成品外观有明显色差。原因allow_rotateTrue让零件旋转了 90°纹理跟着转。解决对纹理件单独分组allow_rotateFalse并且限制旋转角度集合为{0}。如果必须混排把纹理件排在同一张板上且方向一致非纹理件排另一张。代码里给零件加grain_locked字段排样前按它分组。4.5 大零件优先导致小件无处可放现象前几张板排得很满最后剩一堆小件单独开一张板利用率只有 30%。原因纯面积降序让大件先占满小件最后只能在剩余碎片里找位置碎片不够就开新板。解决用分层排样——先排大件预留一部分区域给小件或者用面积降序 小件预分配按面积把零件分成大中小三档每张板按比例混排。更简单的做法是最后一张板专门排小件用 Bottom-Left-Fill 填洞利用率能拉回 70% 以上。5. 把利用率再压 3%参数调优与批量验证的实操技巧排样代码跑通只是起点真正拉开差距的是参数调优和批量验证。我一般按这个顺序压利用率第一步固定几何内核只调搜索顺序。把sort_parts的排序键从纯面积换成面积 长宽比 周长跑 20 组真实订单看平均利用率变化。周长大的件优先能减少窄缝。这一步通常能提 1%~2%。第二步调落点启发式的权重。select_position里的(pt.y, pt.x, bbox_growth)元组顺序可以换试试(bbox_growth, pt.y, pt.x)对板材张数敏感的场景更有效。不要凭感觉写个批量脚本跑对比# batch_eval.py批量对比不同参数组合的利用率 import itertools, csv from nesting_pipeline import run orders [order_01, order_02, order_03] # 真实订单目录 strategies [ {sort: area, pos: bottom_left}, {sort: area_ar, pos: bottom_left}, {sort: area_ar, pos: bbox_growth}, ] results [] for order, strat in itertools.product(orders, strategies): layout run(f./orders/{order}, 2440, 1220, gap3.0, allow_rotateTrue, strategystrat) results.append({ order: order, strategy: strat, util: layout.utilization, # 利用率 sheets: layout.sheet_count, # 板材张数 }) with open(eval.csv, w, newline) as f: writer csv.DictWriter(f, fieldnames[order,strategy,util,sheets]) writer.writeheader() writer.writerows(results)逻辑说明用itertools.product遍历订单和策略组合每个组合跑一次排样记录利用率和板材张数。strategy字典传给run由它分发给sort_parts和select_position。跑完看eval.csv按订单分组对比选每个订单的最优策略或者选全局平均最优。参数说明gap固定 3.0 是为了公平对比调策略时不要动工艺参数。util是面积利用率sheets是板材张数实际生产更关心sheets因为每开一张板都是成本。如果两个策略利用率差 0.5% 但板材张数一样选跑得快的那个。第三步做敏感性分析。把gap从 2 到 6 每隔 1mm 跑一遍看利用率下降曲线。如果 gap 从 3 加到 4 利用率只掉 0.3%说明你的排样对间距不敏感可以适当加大 gap 换切割稳定性。如果掉 2% 以上说明排样太紧凑要考虑换内核或调搜索顺序。第四步建立回归测试集。挑 10~20 个有代表性的历史订单每次改代码后跑一遍记录利用率和板材张数。利用率掉超过 1% 或板材张数增加就回滚。这个习惯能防止你为了优化某个订单把整体效果搞坏。最后说个血泪经验不要迷信利用率数字。我见过排样利用率 94% 但实际切割报废率 8% 的方案原因是排得太紧热变形导致相邻零件粘连。真正该盯的指标是每张板的实际成品数这个数要拿试切结果校准。排样代码是工具工艺参数才是底线。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站