搞SMT产线的朋友应该都经历过这种场面:工艺那边把点胶路径、涂布轨迹画成了DXF图纸,到了设备这边还得靠人工一个坐标一个坐标往控制器里敲。我最近重构了一个SMT轨迹导入程序,核心思路就两个词——解析DXF、输出G代码。把CAD图纸里的线条、圆弧、多边形完整读出来,再转成控制器能直接跑的G代码文件,省掉中间所有手录坐标的环节。
这篇文章不打算写太虚的东西,就围绕这个程序的整体设计、DXF解析要点、G代码生成逻辑、核心代码实现以及我实际调试中踩过的坑展开,适合正在做设备上位机、SMT点胶程序导入、或者想搞明白DXF怎么转换G代码的工程师参考。如果你只是好奇这玩意儿有什么用,看完第一节心里大概就有数了。
1. 项目解读:SMT轨迹导入到底在解决什么问题
1.1 一个典型场景:从图纸到设备的最后一公里
SMT产线里有个非常常见的工序:点胶。无论底部填充、表面涂布还是FPC补强,工艺工程师都是在CAD软件里画轨迹,画完了输出DXF图纸,然后设备端手动录入坐标。对于简单产品,几条直线几个圆,手动敲也就忍了。但到了异形板、多拼板、多工序频繁切换的时候,人工录入的效率和出错率就是一个非常现实的问题。
我这边的实际使用场景是:操作员把客户提供的DXF图纸丢进程序,选择要用的图层,程序自动把所有线段、圆弧、多段线、圆形读取出来,经过排序优化和圆弧离散化处理,生成一份带安全高度、出胶速度和XYZ三轴联动指令的G代码文件。整个过程十秒内完成,图纸改版了重新导一次就行,不用再对着屏一点一点核对坐标。
这个程序的本质,是解决“图形数据”到“运动指令”之间的转换问题。CAD图纸描述的是几何形状,G代码描述的是工具轨迹,两者之间差着一个解释器。手动录坐标就是人肉解释器,程序做的就是自动化解释器。这也是为什么方向对了之后,这个工具能稳定提高产线切换效率,而不是仅仅省了几个按键。
1.2 方案选型:为什么是C# + DXF + G代码这个组合
先说我为什么用C#。设备上位机这层技术栈,Windows平台下C#确实是最省事的选择之一。串口、TCP通信、界面绘制、多线程控制,生态成熟,团队上手也快。如果再往上对接MES、对接扫描枪、对接数据追溯,C#能一路做到底。如果换成脚本语言做原型验证还行,但要做一个给产线人员稳定使用的工具,C#这种编译型、强类型的语言在可维护性和异常处理上会更稳。
再说解析对象为什么选DXF而不是DWG。DWG是封闭的二进制格式,公开解析难度大。DXF是公开的标准交换格式,本质是一个带组码标记的文本文件,哪怕不借助任何第三方库,纯手写解析器也不难搞定。DXF虽然版本迭代了不少,但ENTITIES段里的核心实体结构几十年没大变过,兼容性相对可控。
最后是目标输出G代码。虽然很多运动控制器有自己的专用指令,但G代码依然是事实上的通用语言,上位机生成G代码文件,控制器通过文件解释执行,这套流程在各类点胶机、焊锡机、锁螺丝机上都通用。把中间格式定义成G代码,也方便在真机调试之前用仿真软件先看一遍轨迹,降低试机风险。
2. DXF解析关键技术:从文本文件到结构化图形
2.1 先搞懂DXF的组码配对规则
DXF文件看起来是一堆数字和文本交替排列,其实结构非常死板:两行一组,第一行是组码,第二行是组码对应的值。组码是整数,用来告诉解析器“下一行的值是什么类型”。比如组码0后面跟着实体类型,组码8后面是图层名,组码10后面是X坐标,组码20后面是Y坐标,组码30后面是Z坐标。整个文件就是这种“组码+值”组成的键值对序列。
笔者的解析经验是:不需要把HEADER段、TABLES段全部看懂,大多数情况下只要从文件里找到ENTITIES段,然后从ENTITIES段开始往下逐行读取,遇到组码0就切换实体类型,按实体类型分别提取对应组码的数据,直到遇到ENDSEC为止。整个解析器的核心逻辑就这么多,剩下的全是细节处理。
这个设计其实非常巧妙。正因为DXF把每个点、每条线都拆成了人可读的文本行,才让“跨软件交换图纸”成为可能。对做导入程序的人来说,这种文本结构也意味着你可以按行读取,不占内存,十几兆的图纸也能平稳解析,性能压力比解析二进制格式小得多。
2.2 常用实体解析的字段对照表与读取要点
SMT轨迹里面最常用到的实体就是LINE、LWPOLYLINE、CIRCLE、ARC这四种,偶尔还会碰到SPLINE和INSERT。每一种实体的关键组码字段不同,我在项目里整理了一张速查表,开发的时候基本就靠这张表对着读:
| 实体类型 | 关键组码 | 说明 |
|---|---|---|
| LINE | 10/20/30 起点XYZ,11/21/31 终点XYZ | 最基础直线 |
| LWPOLYLINE | 90 顶点数,10/20 顶点XY,42 凸度 | 轻量多段线,顶点坐标是二维 |
| CIRCLE | 10/20/30 圆心XYZ,40 半径 | 圆心+半径 |
| ARC | 10/20/30 圆心XYZ,40 半径,50 起始角,51 终止角 | 角度单位是度 |
| SPLINE | 10/20/30 控制点,70 拟合次数,40 节点值 | 样条曲线,轨迹曲面较复杂 |
| INSERT | 2 块名,10/20/30 插入点,41/42/43 缩放比例 | 块引用,需要展开图元 |
这里有三个最容易踩的细节。第一,ARC和CIRCLE的角度单位是度,不是弧度,而且角度0度是X轴正方向,逆时针为正向,这里不换算清楚到了G代码里圆弧方向就全反了。第二,LWPOLYLINE的顶点是二维的,只有10/20两组坐标,没有30组码代表Z值,要默认补一个Z=0。第三,42组码是凸度值,表示相邻两个顶点之间插着的是一段弧线而不是直线,很多初学者没见过这个字段就直接忽略了,Polylines画出来的“圆弧”就会变成一条直线连接两个端点。
凸度的计算公式是凸度=2*tan(圆心角/4),圆心角正负代表了圆弧方向。拿到凸度之后,先计算圆心角,再算出这段圆弧的起点和终点以及圆心,然后走后面讲的圆弧离散化流程,才能把带弧段的多段线准确还原成轨迹。
2.3 图层过滤与实体筛选的实用策略
DXF文件里所有实体都有一个图层名,图纸上不同功能的轨迹通常画在不同图层上。点胶路径放一层,Mark点放一层,外形边框放一层,文字标注放一层。如果导入程序不区分图层,一股脑全读进来,生成的G代码里就会混入一堆无关路径,轻则浪费时间空跑,重则撞针报废工件。
我的做法是程序启动后先读一遍DXF文件,把所有图层的名称统计出来,在界面上列成一个复选框列表让操作员勾选。默认只勾选“胶路层”或者“轨迹层”,其他层默认不选。这一步看起来很基础,其实非常影响用户体验,因为大部分操作员并不关心DXF内部结构,他们只想知道“我这板要跑哪几层”。
图层过滤的另一个隐藏价值是可以规避一些奇怪的图纸习惯。有些工程师会把东西画在Defpoints层或者标注层里,这些层默认不打印,所以在图纸上看不见,但实体数据实实在在存在于DXF文件里。不做过滤的话,你在程序预览里会发现莫名其妙多了一段轨迹,排查半天才发现源头在隐藏图层里。
3. G代码生成与轨迹处理的三大难点
3.1 坐标归一化:把图纸坐标对齐到加工原点
DXF图纸里的坐标是设计师的世界坐标,很多情况下和加工坐标系的零点并不重合。比如图纸原点在左下角,但机器的工件原点在板中心;再比如多拼板的图纸包含整张拼板的坐标,实际加工时可能只需要其中一个小板。不处理坐标对齐,生成的G代码一到控制器里就是一头雾水。
我在程序里做了一个手动拾取原点的功能:在图形预览区加载DXF世界坐标,操作员用鼠标点一下图纸上某个位置作为工件原点,程序自动计算这个点相对世界坐标的偏移量,后续所有坐标转换的时候统一加上这个偏移。这个操作简单直观,比让操作员在配置界面里输入一对未知的坐标要友好太多。
如果图纸和机床坐标方向不一致,比如图纸的Y轴朝上、机床的Y轴朝下,还需要加一个镜像或者旋转选项。正常情况下DXF的坐标系是右手系,和大多数运动控制器的XY平台坐标方向是一致的,所以最常用的就是平移变换,旋转和镜像只在特殊装夹方式下才启用。这块做灵活点,能应付的现场情况就多很多。
3.2 圆弧离散:按公差计算步长而不是拍脑袋
控制器执行G代码时,G02/G03圆弧指令虽然理论上能用,但实际调机时很多设备对于小半径圆弧、三维姿态联动的情况处理得并不好,容易出现速度不均或者圆弧精度偏差。所以我选择了把所有圆弧离散成小线段,输出为一连串G01直线插补指令。代价是G代码体积变大,但换来的是运动轨迹的可控性和稳定性。
离散精度是用弓高误差来控制的,也就是离散折线逼近圆弧时偏离真圆弧的最大距离。对于一个半径为R的圆弧,允许的弓高误差为tolerance,那么每一段的圆心角可以通过公式推导:已知圆心角θ的一半,弓高=R*(1-cos(θ/2)),反过来就是θ = 2*arccos(1 - tolerance/R)。有了分段圆心角,用总圆心角除以它取整加一,就能算出一段圆弧要切成几段线。
举个实际的例子,R=5mm的圆弧,允许0.02mm的弓高误差,算出来每步圆心角约0.1799度,90度圆弧大约需要501段线。R=100mm的圆弧,同样公差下算出来每步圆心角约0.0404度,90度圆弧需要2229段。可见公差给得越小,线段数量增长越夸张。所以我在界面里加了公差输入框,默认0.05mm,特殊情况再往下调,而不是程序内部写死,给现场留调整空间。
离散完的每个插值点不仅要包括XY坐标,还要在最终生成G代码时配合Z轴的抬刀和下刀动作。点胶轨迹的处理逻辑是:快速移动到起点正上方,下刀到工作高度,然后沿着点序列做直线切削移动,走完一段之后抬刀到安全高度,再移动到下一段轨迹起点。这样能在保证效率的同时避免胶嘴划伤已完成的轨迹。
3.3 轨迹排序:避免设备在板上来回“跳棋”
所有实体解析出来之后,它们的顺序是DXF文件里的存储顺序,而这个顺序对加工来说通常是最差顺序。比如说一个板子左上角有一条线、右下角有一条弧、中间还有一个圆,如果按文件顺序直接输出,设备就会在板面上来回穿梭,空跑路径比实际加工路径还长。更麻烦的是,点胶中如果路径间断太远,胶线会被拉扯断裂。
排序算法我用的是最朴素的最近邻贪心:从第一个点开始,每次都找离当前点最近的下一个未访问点,直到全部访问完。这个算法简单可靠,对于轨迹数量在几千段以内的场景,计算时间可以忽略不计,排序效果也足够让设备从按部就班地从一个区域走到下一个区域。
不过贪心排序有个前提,不能让不同图层甚至不同工位的轨迹混在一起排。我的做法是先按图层分组,组内再按最近邻排序,组与组之间最后拼接。否则一个板子上同时有两个点胶区域,排完序之后两个区域之间的点会互相穿插,反而更乱。分层级排序之后,轨迹的连贯性会明显提升,实际跑起来也更顺。
3.4 G代码模板与指令输出规范
G代码生成器最核心的是两个部分:文件头尾和轨迹指令。文件头我固定输出一组通用设置:
G90 ; 绝对坐标 G21 ; 公制单位 G54 ; 工件坐标系 M3 S24000 ; 主轴或胶阀开启,转速/参数可配置轨迹部分区分快速移动和切削移动。快速移动用G00,点胶或焊接用G01,并且下刀和提刀都要有明确的Z值。每组轨迹结束后抬刀到安全高度,避免移动过程中刮擦板面。文件结束输出M5和M30,表示停机和程序结束。
输出精度方面,我统一保留3位小数,坐标单位是毫米。实际上大多数控制器对这个精度已经足够,再多的小数位不仅文件体积变大,对电机执行也没有实际意义。数值格式我做了字符串格式化,防止浮点数计算出类似3.99999999这种见鬼的表述,这类细节在G代码文件里很扎眼,而且不同控制器的解释能力不同,有时候真的会引起误读。
4. C#核心模块实战:解析器与代码生成器怎么写
4.1 工程结构与数据模型设计
工程项目我分了四个模块,分层清晰,后续维护和功能扩展都方便。Model层放实体模型,Parser层负责DXF文件读取,Processor层负责排序、离散化、坐标变换,Generator层负责输出G代码。UI层只是把这几条串起来,不做任何解析逻辑。
数据模型我尽量让实体类保存原始的DXF字段,避免在读取阶段就丢失信息。比如Arc类里既有StartAngle、EndAngle也有Center和Radius,跟DXF的ARC实体字段一一对应。后续做离散化时再根据这些字段计算插值点。
设计时有一个原则:Parser只产生中立的数据结构,不直接产出G代码。这样如果哪天现场要改成输出某品牌的专属指令格式,只需要替换Generator层,解析器完全复用,不用推倒重来。
4.2 DxfParser解析器的关键代码
解析器的核心是一个按行读取的状态机。读取DXF文件时,每次读取两行:第一行是组码,第二行是值。当组码是0时,根据值判断当前遇到了什么类型的实体,并把之前暂存的实体数据封存起来,开始读取新实体。
public class DxfParser { public DxfDocument Parse(string filePath) { var doc = new DxfDocument(); bool inEntities = false; string currentEntityType = null; var currentData = new Dictionary<string, double>(); using (var reader = new StreamReader(filePath, Encoding.UTF8)) { string groupCodeLine; while ((groupCodeLine = reader.ReadLine()) != null) { string groupCode = groupCodeLine.Trim(); string value = reader.ReadLine()?.Trim() ?? ""; int code = int.Parse(groupCode); if (code == 0) { if (value == "SECTION") { string sectionCode = reader.ReadLine().Trim(); string sectionName = reader.ReadLine().Trim(); inEntities = sectionName == "ENTITIES"; continue; } if (inEntities && value == "ENDSEC") { inEntities = false; continue; } if (inEntities) { if (currentEntityType != null) ParseEntity(doc, currentEntityType, currentData); currentEntityType = value; currentData.Clear(); } continue; } if (!inEntities || currentEntityType == null) continue; if (currentData.ContainsKey(groupCode)) currentData[groupCode] = double.Parse(value, CultureInfo.InvariantCulture); else currentData.Add(groupCode, double.Parse(value, CultureInfo.InvariantCulture)); } } return doc; } }注意上面这段代码把所有组码的值都当成double存了,但图层名是通过组码8获取的字符串,所以还要单独维护一个字典存字符串字段。另外,坐标值里面有负号、小数点的各种变体情况,建议一律用CultureInfo.InvariantCulture去解析,否则不同系统的区域设置会造成小数点解析错误。这个问题在中文Windows和英文Windows之间切换环境时最容易出现,老程序跑得好好的换了台电脑就崩了,十有八九是这个原因。
实体创建完之后,根据类型读取对应的字段。比如LINE实体读取10/20/30作为起点,读取11/21/31作为终点。ARC实体根据50/51取得角度时,要先判断起始角度和终止角度是否跨过0度,跨过时终止角小于起始角,需要加上360度再计算总扫过角度。
4.3 GCodeGenerator输出模块的写法
生成G代码的模块本身不难,难的是模板的可配置性。现场换设备时,下刀深度、安全高度、点胶速度这些都是不一样的,所以我在界面里放了几个参数输入框:安全高度、工作深度、快速移动速度、点胶移动速度、主轴转速。生成代码时把值填充进去,比每次改源码灵活得多。
public class GCodeGenerator { private readonly GCodeSettings _settings; public string GenerateFromTrajectory(List<List<TrajectoryPoint>> groups) { var sb = new StringBuilder(); sb.AppendLine("G90 G21"); sb.AppendLine("G54"); sb.AppendLine($"M3 S{_settings.SpindleSpeed}"); foreach (var group in groups) { if (group.Count < 2) continue; var start = group[0]; sb.AppendLine($"G00 X{start.X:F3} Y{start.Y:F3} Z{_settings.SafeZ:F3}"); sb.AppendLine($"G01 Z{_settings.WorkZ:F3} F{_settings.PlungeSpeed}"); for (int i = 1; i < group.Count; i++) { var p = group[i]; sb.AppendLine($"G01 X{p.X:F3} Y{p.Y:F3} F{_settings.WorkSpeed}"); } sb.AppendLine($"G00 Z{_settings.SafeZ:F3}"); } sb.AppendLine("M5"); sb.AppendLine("M30"); return sb.ToString(); } }块代码里可以看到轨迹被组织成groups,每组是一条连续的轨迹线。每一组开始前先快速移动到起点上方,下刀到工作深度,然后走完整条轨迹,最后抬刀。这个结构保证每条轨迹之间没有额外空走路径,既效率高又安全。
一个比较容易忽略的点是,如果组内只有两个点,需要在中间直接连接没问题;但有些解析出来的线可能是零长度的点、或者起点终点几乎重合,这种线段要在排序阶段过滤掉,否则控制器会得到一个长度小于一个脉冲当量的微小线段,执行时可能卡顿。
4.4 主流程串联:从选文件到出G代码
程序的主流程非常简单,一共五步:选择DXF文件、选择需要导出的图层、设置加工参数和公差、预览轨迹、生成G代码文件。每一步对应一个UI界面节点,数据流转清晰。
预览这一步很关键。我在界面上用GDI+画了一个简易的2D预览,把解析出的轨迹按世界坐标绘制出来,图层颜色和图元颜色保持一致。这样操作员能在生成G代码之前先看到将要跑的路径,万一选了错误的图层或者路径明显偏离,预览阶段就能发现问题,不用上机试跑才发现。我建议做这个工具的朋友千万别省掉预览功能,现场人员对它依赖非常大。
另外,生成后的G代码文件可以批量输出。多拼板的情况,程序支持加载一份包含多个小板坐标的图纸后,根据拼板阵列的偏移量循环生成多份G代码,或者直接在一份G代码内做坐标偏移重复。这个扩展是我后加的需求,但非常实用,因为实际产线上几乎没有一个产品只贴一块板。
5. 高频踩坑与排查实录
5.1 文件兼容性问题:编码、版本、块引用
DXF文件最容易出的问题就是编码。有些CAD软件保存DXF时默认用当前系统语言的代码页,比如中文环境可能存成ANSI简体中文,图层名如果是中文,用UTF-8直接读就会乱码,导致图层匹配不上、实体全被过滤掉。我的做法是读取文件时先尝试按UTF-8读取,如果发现乱码再回退到系统默认代码页,或者干脆留一个手动选择编码的选项,让操作员自己试。
另一个常见问题是INSERT块引用。图纸里设计师喜欢把某个重复出现的图形做成了一个块,然后在多处插入。很多DXF解析库都不会自动展开块,所以如果你的程序不处理INSERT实体,就会发现某些轨迹神奇地丢失了。处理方式也不复杂:从块定义表里找到对应的图元列表,根据插入点的位置和缩放比例把每个子实体变换到世界坐标再添加到轨迹列表里。注意缩放比例可能不是1,放大缩小的块在坐标变换时一定不能漏掉缩放系数。
版本兼容方面,我实测过DXF从R12到2018版本的文件,核心ENTITIES段的组码结构基本稳定。最需要注意的反而是2020年后某些软件导出的DXF里,SPLINE实体的大批量出现。如果你的轨迹里有样条线,建议要么离散成多段线再导入,要么在程序里写一个简单样条采样逻辑,否则直接忽略会导致轨迹缺口。
5.2 轨迹顺序错乱:为啥排序后还是乱跳
排序算法用的是最近邻贪心,大部分情况下效果很好,但有一种场景会翻车:整个轨迹是由两个距离很近的独立区域组成的,并且每个区域内部都有大量小线段。贪心算法可能会在两个区域之间跳来跳去,因为区域A的末尾距离区域B的起点比A内部的下一条线段起点更近,于是算法就跳到B去了,跑回来再跳到A,轨迹就成了一条来回折返的锯齿线。
解决这个问题不能只靠全局聚类,我的办法是处理前先把所有线段按起点距离进行连接,形成若干条连续的子轨迹链,然后再对这些子轨迹链做最近邻排序。相当于先整理出完整的线条,然后让设备只在“线条之间”做跳跃,而不是在“线段之间”跳跃。这样排序质量会明显改善,生成出来的G代码也更接近人工规划的节拍。
还有一个和排序间接相关的问题:方向。线段是有方向的,有些轨迹是点胶路径,方向不影响结果;但是如果有单方向性要求的轨迹,比如焊接时要从一侧走到另一侧,那么排序时就要注意每条线段的方向一致性。我在排序时增加了“是否允许反向连接”的选项,默认允许,个别工位按需求关闭,避免生成出来的轨迹方向是反的。
5.3 圆弧方向与安全高度:最容易写错的一对参数
ARC实体的角度是逆时针正值增长的,而G代码的G02顺时针与G03逆时针判定是和设备坐标系绑定的。由于我采用直线离散方案,实际生成G01直线段时不涉及G02/G03方向判定,这个坑直接绕过去了。如果你的代码里保留了圆弧插补输出功能,那务必确认清楚控制器里正方向的定义,否则强烈建议像我一样直接走离散折线,省心不少。
安全高度看起来是个小参数,实际影响很大。安全高度设置太低,刀具或胶嘴在快速移动时会刮过板面的元件;设置太高,来回抬刀浪费时间。经验值是安全高度至少比板面最高元件高出3-5mm,具体数值要根据产线的产品和程序确定,千万不要设成固定的10mm了事。我把这个参数放到了界面显眼位置,并且支持按“工件高度+安全余量”的方式自动计算,现场人员只用输入板面最高点高度即可。
下刀深度同样容易出错。点胶工艺中胶嘴一般要接触到PCB表面,所以工作Z通常是一个微小的负数或者严格贴近零的值;焊锡或锁螺丝又有不同。如果直接把图纸里的Z坐标拿来做下刀深度,很容易因为DXF图纸带了三维视角的高度值而出现Z轴失控下压的险情。我最后的策略是:图纸里的Z坐标只作为参考显示,实际执行深度完全由界面的“工作深度”参数决定,图纸Z值不直接参与G代码生成。
5.4 大文件卡顿与G代码体积失控
DXF文件解析本身很快,但排序和预览是两个性能瓶颈。上千条独立线段时,最近邻排序的两层循环大概几十万次比较,还能接受;几万条线段时就要几亿次比较,界面直接卡死。解决方案是读取实体时先按图层拆分成多个集合,每个集合内部排序,可以显著减少计算量。如果还嫌慢,可以对坐标做网格分块索引,先找最近网格再找最近点,排序速度能提升一个数量级。
G代码体积失控的典型原因是圆弧离散公差设置得太小。前面算过,如果把公差从0.05mm改成0.005mm,线段数量大概增加十倍,一个中等复杂板原本2000行的G代码会变成2万行,控制器的内存和传输都会受到影响。实际上0.02mm到0.05mm的弓高误差对于绝大多数点胶轨迹已经足够平滑,没必要追求0.001mm这种离谱精度。我在程序里加了防呆逻辑:公差小于0.01mm时弹出确认提示,防止操作员误设。
文件缓存方面,我的做法是程序解析完DXF后,把解析出的实体序列化到本地一个二进制缓存文件,第二次加载同一个图纸时直接读缓存,秒开预览。这也算一个小技巧,产线切换产品时不用每次都等大文件的解析速度。图纸更新后只要文件修改日期变了,缓存自动失效重解析,不需要额外维护。
6. 写在最后:几个后续可扩展的方向
程序做到现在这个程度,已经满足了基本产线使用,但我自己用下来的体会是,还能往这几个方向再拓展:一是对接视觉定位系统,用Mark点坐标自动校正工件原点,把“手动拾取原点”升级成“视觉自动定位”;二是输出格式抽象成更多控制器的专有指令,比如脉冲模式下的坐标格式或者某类点胶控制器的自定义格式;三是增加工艺参数管理,把每种产品的轨迹和对应参数保存为一个工程文件,下次切换产品一键载入。
还有一个非常实用的扩展,就是把生成G代码的日志和坐标系信息上报给MES或追溯系统,让每一块板子上实际运行的轨迹都能和批次绑定。这样如果发生品质问题,可以直接反查出某个时间点设备实际执行了哪一版轨迹,对质量追溯的帮助特别大。
最后说一点点私心体会:做这类工具最忌讳“看似全能实则难用”。现场操作员不是程序员,界面上参数越多他们越慌。现在我的界面只保留六个输入项——安全高度、工作深度、速度、延时、公差、图层选择——其他全部根据DXF文件自动推断。结果反而是现场几乎零培训就能上手,比自己一开始做的那种全参数开放版本成功得多。如果你们也在做类似工具,建议从一开始就把“减参数”当成一个核心目标来设计。