激光打标工位最要命的两种事故:一是打错——上位机换了工单,打标机里还躺着上一个型号的固定文本,整批件刻着别人的码;二是打重——打标软件里的静态计数器和上位机发码各走各的,同一张码刻在两件产品上。更隐蔽的是打标"成功"了但二维码浅得读不出,到了包装站才发现,工件已经流转了三站。激光打标对接的核心不是"让机器出光",而是内容由上位机唯一发放、打标结果必须读码回验、设备只负责执行不负责编号。
一、什么时候必须上激光:贴标解决不了的四个场景
| 场景 | 贴标签(ZPL,113篇) | 激光直接打标 DPM |
|---|---|---|
| 金属/塑料件本体标识 | 标签易翘起、油污环境脱落 | 直接刻在零件上,与件同寿命 |
| 耐高温/后道热处理、喷涂 | 标签过炉报废 | 打标安排在热处理后或选用耐高温工艺,永久可读 |
| 防伪/防篡改 | 标签可撕可换 | 刻码无法无损移除,配合追溯防串货 |
| 小件/曲面/高速节拍 | 贴标机构对小曲面适应性差 | 振镜打标几百毫秒一件,二维码 3mm 也能刻 |
激光器按材质选:金属件主流光纤激光器(1064nm),有机塑料/木头/亚克力用 CO2(10.6μm),透明塑料/玻璃精细打白、食品包装冷打码用紫外(355nm,热影响区极小但贵)。选机器时把本厂实际材料样件寄给集成商打样并验读码等级,比看功率参数表靠谱得多。
二、系统构成与三种二次开发方式
一套激光打标设备 = 激光器 + 振镜扫描头 + 打标控制卡(插在工控机 PCIe 或以太网连接)+ 打标软件。国产打标卡九成以上是金橙子 EZCAD 生态,模板文件 .ezd 里画好固定文本、图形和命名的"可变文本对象"。上位机二次开发有三条路:
引用随卡 LMCardDll(x86/x64 必须与程序位数一致),P/Invoke 调初始化→加载 ezd→替换命名文本→打标。实时可控,能拿到完成/错误码,适合联机自动工位。
打标软件配置为监控某 txt 文件或串口,上位机只负责把 SN 写进文件。开发量最小但反馈弱,适合小厂半自动工位、不方便拿 SDK 的杂牌设备。
一体式激光机(基恩士/大族智能机等)自带命令协议,上位机走 TCP 或串口发文本与打标触发。不依赖板卡和驱动,部署最干净,但协议各厂家不通用。
// ===== 方式①:EZCAD2 打标卡 SDK 的 .NET 封装(32位典型声明)===== // 注意:dll 必须与进程位数匹配(LMCardDll.dll=x86 / LMCardDll_x64.dll=x64), // 且打标卡 SDK 通常只允许同机一个进程实例持有板卡。 public static class EzCadSdk { private const string Dll = "LMCardDll.dll"; [DllImport(Dll)] public static extern int LMCard_Initial(); [DllImport(Dll)] public static extern int LMCard_CreateMark(ref int card); [DllImport(Dll)] public static extern int LMCard_DownLoadEzdb(int card, [MarshalAs(UnmanagedType.LPStr)] string ezdFile); [DllImport(Dll)] public static extern int LMCard_Mark(int card); [DllImport(Dll)] public static extern int LMCard_StopMark(int card); // 替换模板里命名的可变文本对象(不同SDK版本函数名/参数以随卡手册为准) [DllImport(Dll)] public static extern int LMCard_SetTextByName(int card, [MarshalAs(UnmanagedType.LPStr)] string objName, [MarshalAs(UnmanagedType.LPStr)] string text); [DllImport(Dll)] public static extern int LMCard_GetProgress(int card, ref int progress); } public class LaserMarker : IDisposable { private int _card; public void Open(string templateEzd) { if (EzCadSdk.LMCard_Initial() != 0) throw new Exception("打标卡初始化失败:查驱动/加密狗/被其他进程占用"); EzCadSdk.LMCard_CreateMark(ref _card); int rc = EzCadSdk.LMCard_DownLoadEzdb(_card, templateEzd); if (rc != 0) throw new FileNotFoundException("模板加载失败", templateEzd); } public bool Mark(string sn) { if (EzCadSdk.LMCard_SetTextByName(_card, "sn", sn) != 0) return false; if (EzCadSdk.LMCard_Mark(_card) != 0) return false; // 同步返回,打标耗时约0.3~1s return true; } public void Dispose() => EzCadSdk.LMCard_StopMark(_card); }
三、打标闭环:到位→发码→打标→回验,五拍缺一不可
public class MarkService { public async Task<MarkResult> MarkOneAsync(string workOrder, CancellationToken ct) { // ② 发码服务发号:数据库号段预占,保证全局唯一,绝不使用打标机自带计数器 string sn = await _numberSvc.AllocateAsync(workOrder, ct); // ③ 替换模板内容并打标(在线程池执行,SDK是阻塞调用) bool fired = await Task.Run(() => _laser.Mark(sn), ct); if (!fired) return MarkResult.HardwareFail(sn); // ④ 读码回验:给固定读码器留出光时间,超时/不一致都判NG var read = await _reader.ReadOnceAsync(TimeSpan.FromSeconds(2), ct); if (read.Timeout) return await FailAsync(sn, "读码超时:码过浅或读码器位置偏移"); if (read.Text != sn) return await FailAsync(sn, "回验不一致:打标内容异常"); if (read.Grade < Grade.B) return await FailAsync(sn, "读码等级低于B:标记质量退化"); // ⑤ 成功:SN建档(与121篇首站激活同一动作),通知PLC放行 await _trace.ActivateSnAsync(sn, workOrder, ct); _plc.Write("DB8.DBX0.1", true); return MarkResult.Ok(sn); } private async Task<MarkResult> FailAsync(string sn, string reason) { await _numberSvc.MarkFailedAsync(sn, reason); // 该SN置"打标失败",不释放给第二件货重用 _plc.Write("DB8.DBX0.2", true); // NG剔除信号 return MarkResult.Ng(sn, reason); } }
四、方式②补充:目录监听怎么写才不丢单
// 打标软件监控某文件,上位机写文件触发打标。 // 必须用"临时文件+原子替换",否则打标软件可能读到写了一半的内容 private void PushSnByFile(string sn) { var dir = @"D:\Laser\watch"; var tmp = Path.Combine(dir, Guid.NewGuid().ToString("N") + ".tmp"); var target = Path.Combine(dir, "markdata.txt"); File.WriteAllText(tmp, sn + Environment.NewLine, Encoding.ASCII); File.Copy(tmp, target, overwrite: true); // Copy/Move 是原子的,监听方不会读到半截 File.Delete(tmp); } // 同时用 FileSystemWatcher 监听打标软件写出的完成回执文件; // OnCreated 里加 200ms 重试打开,文件可能尚未 flush 完句柄。
目录方式拿不到结构化结果,回验仍需独立的读码器通道;它只解决"把内容送进去"。节拍快、要结果绑定的正规自动线,直接选方式①或③。
// 方式③:智能一体机 TCP 命令(各机协议不同,以下为典型"连接-发文本-触发"骨架) public class IpLaserClient : IDisposable { private TcpClient _tcp = new TcpClient(); private NetworkStream _stream; public async Task ConnectAsync(string ip, int port, CancellationToken ct) { await _tcp.ConnectAsync(ip, port, ct); _stream = _tcp.GetStream(); // 多数一体机要求先发"远程模式"使能命令,防止面板本地操作与上位机抢触发 await SendAsync("REMOTE:ON\r\n", ct); } public async Task<bool> MarkAsync(string sn, CancellationToken ct) { await SendAsync($"TEXT:sn:{sn}\r\n", ct); // 更新机内文本对象 await SendAsync("MARK:START\r\n", ct); // 触发出光 string ack = await ReadLineAsync(TimeSpan.FromSeconds(3), ct); return ack.StartsWith("MARK:OK"); // 协议响应同样不能替代读码回验 } }
五、工艺参数与读码质量:软件管不了的部分要写进点检
| 参数/项目 | 影响 | 管控方式 |
|---|---|---|
| 功率/打标速度/频率/填充间距 | 决定刻线深浅与对比度,参数组合按材料打样定 | 模板参数随工艺版本保存,换型换模板,不允许工位私改 |
| 焦距 | 偏焦最常见,码直接变浅读不出 | 治具定位面统一高度,班前用首件读码等级点检 |
| 二维码尺寸/静区 | DPM 码建议 ≥4mm,四周留 ≥4 个模块静区 | 模板评审时检查,治具压块不能伸进静区 |
| 读码等级 | 按 ISO/IEC 15415(二维)分级 A~F | 关键工位 ≥B,回验结果里长期记录等级趋势,低于阈值提前清洁镜头/报修激光器 |
六、防重号与对账:设备计数永远只当旁证
- SN 唯一来源是上位机发码服务:打标机内静态计数器、条码软件自带流水号全部禁用,避免两套号源并发产生重码(号段预占与防重见 113、121 两篇);
- 打标失败的 SN 不复用:刻废的码号做"作废"留痕,下一件货发新号,防止"废件上的旧码"在系统里又被激活;
- 班末三方对账:发码服务发出数 = 打标成功建档数 + 打废数;打标卡内部计数只作旁证参考,差异必须当班查清(常见是人工模式偷打或急停中断);
- 急停恢复:打标中途急停后,当前 SN 状态未知,复位时该件强制走读码回验判定,读不到就按打废处理重发新号,绝不默认成功。
七、上线验收清单
- 内容唯一:连续打 100 件,SN 全部唯一且与发码记录逐条对平,打标机无自编号残留;
- 回验拦截:人为遮住打标区/调偏焦距制造废码,系统判 NG、剔除动作正确、PLC 不放行;
- 换型防错:切换工单后首件打标内容为新型号规则,旧模板不会被误用;模板名与工单版本绑定;
- 急停恢复:打标中拍急停,复位后该件走回验或作废重号,不产生"幽灵成功";
- 安全联锁:安全门打开时激光物理无法出光(硬线验证,不是看软件提示);
- 稳定性:连续运行一个班次无 SDK 崩溃/板卡掉线,读码等级记录无持续下滑,班末三方计数对平。
激光打标工位接好了,它就从一台"听人敲字的刻字机"变成追溯链上最可靠的起点:工单定型号、服务定号码、设备只执行、读码做裁判、PLC 管物理放行。每一件流出工位的产品,身上刻的码都经过了"发得对、刻得上、读得出、查得着"四重确认——后续条码追溯(121 篇)的所有价值,都从这一道闭环开始。
