倍速链、转盘线上,工装板(托盘)一圈圈地转,板上装的是什么、属于哪个工单、前面漏了哪道工序——靠人盯迟早出事。给工装板贴一张纸质流程卡,油污一糊就废;靠工位记忆型号,混线生产时必错。RFID 解决的正是"看不见的流动":载码体随板循环、读写头各站自动识别,上位机据此做型号校验、工序防错和路由分流。但 RFID 项目翻车的也多——频段选错、金属上读不到、相邻工位串读、写卡与落库不一致——这篇把这些坑一次讲透。
一、频段选型:工装板流转 90% 选 HF,别一上来就 UHF
- 工业读头有效距离通常 3~15cm,场界清晰,到位才读到、离开即断开,天然不串读
- 抗干扰好,金属工装板配抗金属载码体稳定可用
- 载码体便宜、无源免维护,读写头 RS485/以太网接入成熟
- 工装板/托盘逐站识别的首选
- 读距 1~10 米,支持多标签批量盘点(通道门、整箱出入库)
- 金属、液体环境调谐敏感,标签位置和贴法极讲究
- 远距离 = 双刃剑,容易读到隔壁工位、隔壁料架的标签
- 适合仓储/物流批量读,逐工位精确识别反而难做
| 对比项 | HF ISO 15693 | UHF EPC Gen2 |
|---|---|---|
| 典型用途 | 工装板工位识别、刀具/夹具管理 | 仓库通道门、栈板盘点、整车物流 |
| 识别边界 | 精确,与到位传感器配合 | 模糊,需功率/RSSI/定向天线收敛 |
| 金属环境 | 抗金属标签后稳定 | 必须专用抗金属标签并打样验证 |
| 单标签成本 | 几元~十几元 | 几毛~几元(但读写头贵) |
另有 LF 125kHz(EM 卡)只读、读距几厘米,老式低成本工装线还有存量,新项目不建议。原则:逐工位、要精确边界选 HF;要一次读一整托/一整箱选 UHF。拿不准就带着工装板和产品实物去读写头厂家现场打样,读距和稳定性以实测为准。
二、硬件构成与载码体数据结构
一套工位识别 = 载码体(嵌在工装板内的螺钉式/贴片式/PCB 式标签,金属板务必选抗金属款)+ 读写头(固定在工位下方/侧方,RS485、以太网或 IO-Link 接上位机/PLC)+ 安装支架与到位传感器。载码体芯片内部分两个关键区域:
| 区域 | 内容 | 用法 |
|---|---|---|
| UID / TID | 出厂全球唯一序列号,只读不可改(15693 为 8 字节) | 直接当工装板身份证,永远不用写 |
| 用户区 | 按块组织,每块 4 字节,容量依芯片(常见几十至上百字节) | 写板号/简码,块号与字节序按芯片手册对齐 |
三、读写头对接:统一抽象,隔离厂家协议
读写头接入常见三种:厂家 DLL/SDK(USB 或串口读写器)、Modbus RTU/TCP(读写头作从站,寄存器映射盘点结果)、工业总线(PROFINET/EtherNet/IP 直接进 PLC,上位机再从 PLC 取)。上位机侧定义统一接口,换厂家只换实现:
/// <summary>RFID读写头统一抽象:无论SDK还是Modbus,业务层只依赖此接口</summary> public interface IRfidReader : IDisposable { void Connect(string conn); // 盘点场内标签,返回TID列表(ISO15693 UID十六进制字符串) IReadOnlyList<string> Inventory(); // 读单块/写单块:block为块号,data固定4字节(具体以芯片手册为准) byte[] ReadBlock(string tid, int block); bool WriteBlock(string tid, int block, byte[] data); } // 串口命令型读写头的典型实现(各厂命令帧不同,此处演示帧结构骨架) public class SerialRfidReader : IRfidReader { private SerialPort _sp; public void Connect(string port) { _sp = new SerialPort(port, 115200, Parity.None, 8, StopBits.One) { ReadTimeout = 800, WriteTimeout = 800 }; _sp.Open(); } public IReadOnlyList<string> Inventory() { // 帧:帧头 + 命令字(盘点) + 长度 + 校验;返回帧里按标签数切出各TID byte[] resp = Transact(new byte[] { 0xAA, 0x01, 0x00, 0x00, 0xDD }); return ParseTids(resp) .Select(b => Convert.ToHexString(b)) .ToList(); } public bool WriteBlock(string tid, int block, byte[] data) { if (data.Length != 4) throw new ArgumentException("块长度必须为4字节"); byte[] resp = Transact(BuildWriteFrame(tid, block, data)); return resp[2] == 0x00; // 状态字0=写入ACK,但ACK后仍建议回读校验 } private byte[] Transact(byte[] frame) { /* 加校验-发送-读整帧-超时重发1次,略 */ } }
走 Modbus 的读写头更简单:盘点结果通常映射在保持寄存器里(TID 占若干连续寄存器、状态字 1 个),用 119 篇讲过的 Modbus 主站轮询即可,不必引厂家 SDK;总线直接进 PLC 的方案则让 PLC 把读到的 TID 搬进 DB,上位机通过 S7/OPC UA 读取。
四、工装板全生命周期:绑定→读校验→解绑→回流
上线绑定产品SN扫码,读载码体TID,库内建立 板↔SN↔工单↔型号 绑定
各站读校验每工位读TID查档案:型号对不对、工序顺序对不对、该不该走本分支
下线解绑成品下线,过站完结,解除板与SN绑定,产品档案保留
空板回流空工装板沿回板线回上线口,等待下一次绑定循环
闭环关键:空板回流到上线口时系统必须确认其处于"未绑定"状态——防止上一轮异常下线的残绑定把新产品挂到旧 SN 上。
public class PalletService { // ① 上线绑定:扫产品SN + 读板TID + 落库(标签只认TID,不写业务数据) public async Task BindAsync(string sn, string workOrder) { string tid = await ReadStableTidAsync(TimeSpan.FromSeconds(2)); var pallet = await _db.Pallets.FirstAsync(p => p.Tid == tid); if (pallet.State != PalletState.Free) throw new InvalidOperationException( $"工装板{tid}仍绑定旧产品{pallet.Sn},禁止上线(需异常解绑)"); var product = await _trace.GetBySnAsync(sn); pallet.Sn = sn; pallet.WorkOrder = workOrder; pallet.Model = product.Model; pallet.State = PalletState.Bound; pallet.BindAt = DateTime.Now; await _db.SaveChangesAsync(); } // ② 工位读校验:本工位只放行"档案允许经过本站"的板 public async Task<GateResult> VerifyAtStationAsync(string stationCode) { string tid = await ReadStableTidAsync(TimeSpan.FromSeconds(2)); var pallet = await _db.Pallets.FirstOrDefaultAsync(p => p.Tid == tid); if (pallet == null || pallet.State != PalletState.Bound) return GateResult.Ng("未绑定的工装板,停线"); var route = _routes[pallet.Model]; if (route.Model != pallet.Model) return GateResult.Ng("型号与工位路由不符,疑似混线"); if (!route.CanEnter(stationCode, pallet.PassedStations)) return GateResult.Ng("跳站/回流错序,禁止过站"); // 防错逻辑同121篇过站闸门 pallet.PassedStations.Add(stationCode); await _db.SaveChangesAsync(); return GateResult.Ok(pallet.Sn, pallet.Model); } // ③ 下线解绑:状态回Free,TID不变,载码体直接可循环 public async Task UnbindAsync(string tid) { var pallet = await _db.Pallets.FirstAsync(p => p.Tid == tid); pallet.State = PalletState.Free; pallet.Sn = null; pallet.PassedStations.Clear(); await _db.SaveChangesAsync(); } }
五、漏读、抖动与串读:读得稳比读得到更重要
RFID 现场三大不稳定:到位瞬间场还没建立导致首次盘点空、板移动中 TID 时有时无、相邻工位读写头互相读到。软件和安装两端同时收敛:
// 稳定读取去抖:连续N次盘点到同一个唯一TID才采信; // 场内出现2个以上TID直接判异常(工装板背靠背或串读) private async Task<string> ReadStableTidAsync(TimeSpan timeout) { var deadline = DateTime.Now + timeout; string last = null; int hits = 0; while (DateTime.Now < deadline) { var tids = _reader.Inventory(); if (tids.Count > 1) throw new Exception("场内多标签:检查工装板间距或读写头串读"); if (tids.Count == 1 && tids[0] == last) { if (++hits >= 3) return last; // 连续3次一致,采信 } else { last = tids.FirstOrDefault(); hits = 0; } await Task.Delay(60); } throw new TimeoutException("读标超时:标签损坏/位置偏移/读写头故障"); }
| 问题 | 安装/硬件手段 | 软件手段 |
|---|---|---|
| 首读漏读 | 读写头装在到位传感器后方,板停稳才处于场区中心 | 到位信号触发后延时 100~200ms 再盘点,失败短重试 |
| 读值抖动 | 标签安装位置全板统一,读写头距离按手册固定 | 连续多次同一 TID 去抖后采信 |
| 相邻工位串读 | 降低发射功率、加屏蔽/吸波材料、窄场天线、读写头间距拉开 | 到位传感器门控,非到位时段不盘点;多 TID 报警 |
| 金属工装板 | 必须抗金属标签(带隔离层/螺钉式),实物打样 | 建立标签档案,连续读不到的板自动报修 |
六、必须写用户区时:卡库一致性怎么保证
少数场景(板要离开本线上外协、跨产线读取)必须把简码写进载码体。写操作遵循"先落库、再写卡、写后回读校验、失败补偿":
public async Task WriteBoardCodeAsync(string tid, int block, byte[] code) { // 1) 库内先记"待写"状态——即便此刻断电,重启补偿任务也知道这张卡该写什么 await _db.InsertPendingWriteAsync(tid, block, code, WriteState.Pending); bool ack = await Task.Run(() => _reader.WriteBlock(tid, block, code)); if (!ack) { await MarkRetryAsync(tid); throw new Exception("写卡无ACK"); } // 2) 回读比对:ACK不等于真的写进去(场弱、标签寿命、干扰都可能假成功) var back = _reader.ReadBlock(tid, block); if (!back.SequenceEqual(code)) { await MarkRetryAsync(tid); throw new Exception("写后回读不一致"); } // 3) 卡、库两边都确认后才置完成 await _db.SetWriteStateAsync(tid, WriteState.Done); } // 后台补偿服务:周期扫描 Pending/Retry 记录重写并回读; // 超过重试次数告警并把该工装板锁定,禁止继续流转。
七、RFID 与条码激光码:不是替代,是分工
- 产品单品标识用条码/激光 DPM(113、124 篇):成本低、可视可抄、供应商和客户端都能读,随产品出厂;
- 工装载具循环标识用 RFID:可擦写复用、不怕油污遮挡、不需要对准视线,随板留厂循环;
- 两者在上线工位"相认"(扫产品 SN + 读板 TID 绑定),之后各工位既能读到板的档案,产品过站记录仍挂在单品 SN 下——这正是 121 篇追溯主链在输送线上的落地形态。
八、上线验收清单
- 读距实测:工装板以线速通过,各工位连续 200 次无漏读、无误读,背靠背两板不出现多 TID;
- 串读验证:相邻工位同时通电,板停在任一站时只有本站读写头读到,隔壁站零误触发;
- 绑定闭环:上线绑定后各站型号/工序校验准确,混放异型号板能被拦截停线;
- 回流检查:异常下线的板未解绑时重新上线,系统拒绝绑定并提示旧 SN,残绑定不会污染新产品;
- 写卡一致:若启用用户区写入,拔电/断网注入故障后补偿任务能补齐,回读 100% 一致;
- 运维兜底:坏板读不到时报警明确(区分"无板"与"有板读不到"),标签档案支持更换载码体后重新登记。
RFID 项目的成败七分在现场、三分在软件:频段和标签按实物打样、读写头场界靠安装收敛,上位机则守住稳定读取去抖、只认 TID、业务入库、卡库双写留痕、解绑闭环这五条。做到这些,每一块工装板走到哪个工位、载着什么产品、漏没漏工序,系统比线长还清楚——这就是"看不见的流动"被彻底管住的样子。
