
简介一套基于C#开发的无人值守地磅称重系统设计源码面向需要实现地磅自动化称重管理的中小企业、项目开发者及C#桌面应用学习者。系统围绕称重流程控制、用户界面交互、配置管理等核心环节展开提供了从界面层到业务层的完整工程实现可作为无人值守称重解决方案的参考工程也可直接用于二次开发或功能扩展。压缩包内共137个文件其中91个C#源代码文件承载主要业务逻辑与算法21个XAML文件定义界面布局8个config文件用于运行时参数配置还包含csproj项目文件、sln解决方案文件、settings设置文件及resx资源文件等整体压缩包大小约2.44MB。文件组织规范模块边界清晰能够体现工业级桌面应用的典型架构。当前已有414人学习该资源适合希望了解地磅无人值守系统设计思路、钻研C#与XAML协同开发方式的读者。通过分析源码可掌握称重流程的实现步骤、界面与逻辑的绑定方式、配置文件的使用技巧以及项目结构和资源管理方法为同类工业自动化项目提供有价值的代码参考。1. 无人值守地磅称重系统一套能落地的 C# 源码到底解决什么问题做过工业上位机的人都知道地磅称重这行看着简单不就是“车上去、读数、下来”吗真到现场全是坑司机和司磅员串通换卡、称重时整车压在传感器边缘、仪表数据被拦截篡改、两辆车同时上磅……所以无人值守地磅系统真正要解决的不是“自动读个数”而是在没人盯着的情况下保证数据可信、流程闭环、异常可追溯。这份 C# 设计源码覆盖了从车牌识别、红外对射防作弊、道闸联动到仪表数据采集、数据库落库的完整链路适合正在做称重系统集成、打算自研无人值守方案或者想找一套能二次开发的基座的工程师。对我来说它最值钱的地方不是代码量而是把工业现场那些“潜规则”写进了业务逻辑里。2. 系统架构与技术选型为什么是 C# 串口/网络仪表 SQL 数据库的铁三角2.1 先拆需求无人值守地磅的完整业务闭环是什么拿到这套源码我建议你先别急着看代码先对着现场把业务流程捋一遍。一套能真正跑起来的无人值守地磅系统至少包含八个环节车辆入场触发、车牌识别、称重状态判断上磅是否到位、仪表数据读取、重量合法性与防作弊校验、数据保存与照片留存、道闸抬杆放行、异常情况远程干预。任何一个环节断了都在给“无人”这两个字挖坑。整套源码的模块划分基本就是按这条业务链走的。入口是车辆检测触发地感线圈或红外对射中间是车牌识别与仪表数据采集出口是道闸控制和语音提示。数据层把每一笔称重记录连同抓拍照片、车牌号、时间戳、重量值、判定标志位一起写入数据库。这个设计思路很适合做过度方案——即使以后要接 ERP 或者物流系统只要把数据库层面的接口暴露出去就行业务层不用大改。2.2 为什么是 C# WinForms 而不是 Web 或 PLC 方案做无人值守地磅选型的核心矛盾有三个实时性、硬件交互能力、现场维护成本。PLC 方案足够稳但写复杂业务逻辑比如多车排队、黑白名单、分段计费非常痛苦而且报表和界面是硬伤。Web 方案在跨平台和设备接入上有优势但串口通信、动态链接库调用、摄像头抓拍、道闸继电器的直接控制这些底层操作在浏览器里绕一大圈反而更容易出问题。C# 在这个场景里的位置比较特殊用 WinForms 或者 WPF 做上位机可以直接调用 System.IO.Ports 操作串口也可以用 Socket 对接网络仪表调用第三方车牌识别的 DLL 更是主场P/Invoke 和 COM 互操作都很成熟数据库选 SQL Server 或 MySQL 都行只要能处理并发写入。最关键的是C# 的桌面程序可以做成单机部署现场一台工控机全搞定断网也不影响称重——这在很多厂区是硬需求地磅房经常在偏远角落网络不稳定是常态。2.3 模块划分和主流程源码的骨架先看清楚这套源码在结构上是典型的“一主三副”模式主程序负责流程调度三个核心类库分别管硬件通信、业务逻辑和数据访问。硬件层封装了地磅仪表和道闸控制业务层处理称重流程状态机数据层负责所有持久化操作。UI 层保持得很薄这是对的——无人值守系统界面本来就不需要复杂操作主要给管理员和远程监控用。主流程的核心是一个状态机。车辆触发到位信号后系统依次执行“车牌识别→重量读取→防作弊校验→写库→抬杆放行”任何一个环节失败都会回滚到等待状态并触发报警。这种设计的好处是现场出现任何非预期情况系统都不会卡死在中间步骤而是回到一个安全状态等待人工处理。源码里这部分逻辑值得仔细读它是整个系统可靠性的地基。// 无人值守称重主流程状态机精简示意 public enum WeighState { Waiting, // 等待车辆上磅 VehicleDetected, // 车辆到位地感/红外触发 PlateReaded, // 车牌识别完成 WeightReaded, // 仪表数据读取完成 Verifying, // 防作弊与合法性校验 Completed, // 称重完成 Error // 异常等待人工介入 }这段代码定义了称重流程的核心状态枚举。实际运行中状态由 Timer 事件或硬件中断驱动流转每进入一个状态都有独立的方法处理比如 VehicleDetected 状态会同时触发车牌识别和重量稳定判断。状态机的价值在于把不确定性隔离在单步范围内——每一步都有超时和异常分支不会让整个流程陷入死锁。3. 硬件通信与数据采集从串口读到仪表稳定值的完整实现3.1 仪表通信协议连续称重数据的处理逻辑地磅仪表是这套系统的“眼睛”数据读不准后面全是白搭。工业地磅仪表最常见的通信方式是 RS232 串口波特率一般是 9600 或 19200数据格式为 8 位数据位、1 位停止位、无校验。不同品牌的仪表协议略有差异但大多遵循一个共同逻辑仪表持续向外发送重量数据帧上位机需要做的只是监听串口、解析帧、过滤无效数据。源码里对仪表数据的处理有一个细节很值得学习它不是取单次读数而是连续采集多帧数据做稳定性判断。这对应地磅行业的“稳定值确认”需求——车辆刚上磅时重量是跳动的必须等数据在设定范围内连续波动才算稳定。直接取第一次读数就写入数据库在空车和重车切换时会出现几百公斤的误差。// 串口数据接收与稳定性判断 private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { string data serialPort.ReadExisting(); // 读取串口缓冲区全部数据 rawBuffer data; // 追加到原始数据缓存 // 按帧尾标志常见为 \r\n切出完整帧 while (rawBuffer.Contains(\r\n)) { int idx rawBuffer.IndexOf(\r\n); string frame rawBuffer.Substring(0, idx); rawBuffer rawBuffer.Substring(idx 2); if (TryParseWeightFrame(frame, out decimal weight)) { // 将有效重量值放入队列供稳定性判断使用 weightQueue.Enqueue(weight); if (weightQueue.Count 10) weightQueue.Dequeue(); // 当最近5帧最大值与最小值差值小于20kg认为重量稳定 if (weightQueue.Count 5) { decimal max weightQueue.Max(); decimal min weightQueue.Min(); if (max - min 20m) OnWeightStabilized(weight); } } } }这段代码展示了两个关键点。一是TryParseWeightFrame 方法专门负责把仪表帧转成数值不同品牌仪表只需要替换这一个方法的解析逻辑其他部分完全复用二是稳定性判断采用滑动窗口队列里始终保存最近 10 帧数据只有当最近 5 帧的最大最小差值小于 20kg 时才触发稳定事件。20kg 这个阈值对于地磅来说很合理——大多数汽车衡的分度值是 20kg 或 50kg你只需要根据现场仪表的实际分度值微调这个参数即可。3.2 车牌识别与红外对射如何避免“卡脖子式”作弊无人值守系统最容易出问题的环节其实是车辆管理而不是称重本身。常见的作弊手段包括换车牌、跟车过磅前车还没完全下磅后车就上磅、车辆不完全上磅只压到一部分传感器。针对这些场景源码里做了三层防护。第一层是车牌识别联动。车辆到位后触发相机抓拍车牌识别结果会关联到本次称重记录。如果识别失败或者置信度过低系统不会阻断流程但会在数据库里标记“待人工确认”管理员事后可以回溯照片。第二层是红外对射逻辑——在磅体前后各安装一组红外对射当车辆上磅后如果尾部或头部红外被遮挡说明车辆没有完全停在地磅有效称重区域内此时系统会语音提示司机调整位置。// 红外对射与称重联动判断 public bool CheckVehiclePosition() { // 假设前红外为Port1后红外为Port2未遮挡时输出高电平 bool frontBlocked digitalInput.ReadPort(1); // 前红外被遮挡 true bool rearBlocked digitalInput.ReadPort(2); // 后红外被遮挡 true if (frontBlocked || rearBlocked) { // 红外被遮挡说明车辆超出磅体有效范围禁止称重 PlayVoicePrompt(请调整车辆位置确保完全停在地磅上); return false; } return true; }这里的判断逻辑看上去简单但现场实际部署时需要注意红外对射的安装高度和角度。我见过一个项目红外安装位置偏高皮卡和半挂车都能正常遮挡但遇到低平板车就失效了——因为车体太低红外光束从车底穿过去了。这个问题在源码层面无解属于现场施工经验。我的建议是安装两组不同高度的红外对射一高一低交叉验证。3.3 道闸联动与语音提示硬件控制的闭环处理道闸控制在无人值守系统里属于“最后一公里”。称重完成后系统需要给道闸控制器发送开闸信号车辆驶离磅体后再自动落杆。如果道闸信号发送过早车辆还没完全通过落杆可能砸到车尾发送过晚又会影响通行效率。源码里对道闸的处理采用继电器输出控制通过串口或 TCP 向道闸控制器发送指令。这里有一个容易踩坑的细节继电器输出不能直接控制道闸的交流电机必须通过道闸控制器的外部接口——有些道闸是“干接点触发”有些是“电平触发”搞混了轻则道闸不动作重则烧毁控制板。拿到源码之后第一件事就是确认你要接的道闸型号和触发方式然后在硬件封装层做适配。语音提示模块也是一样源码里使用 Windows 自带的文本转语音服务来播报“请上磅”“称重成功”“请下磅”这些提示。实际现场噪音大建议外接功放和防潮喇叭不然司机根本听不到。语音提示和道闸动作之间最好加 3~5 秒的延时给司机一个反应时间这个延时参数可以在配置文件里调整。4. 防作弊与数据落库把称重数据变成不可抵赖的证据4.1 防作弊判重逻辑毛重、皮重与净重的校准策略地磅称重的业务逻辑围绕三个值展开毛重载货上磅、皮重空车下磅和净重毛重减皮重。最常见的业务流有两种一次称重只称毛重皮重从数据库读取车辆档案和二次称重上磅称毛重、下磅称皮重净重现场计算。这套源码两种都支持核心是一张车辆档案表和一条称重记录的标志位字段。无人值守系统的防作弊重点在“判重”环节。比如一辆车进厂时称毛重 50 吨出厂时空车皮重 15 吨净重就是 35 吨。但这里存在一个隐蔽的作弊方式车辆进厂时偷偷在货箱里藏水或沙袋出厂前找个地方倒掉这样出厂皮重就会凭空多出几百公斤净重被恶意压少。源码里对这个问题的处理方式是设置“合理皮重范围”——读车辆档案里的历史皮重值如果本次皮重和历史皮重偏差超过设定阈值比如 3%系统自动标记异常并转入人工复核流程。// 皮重异常检测 public bool CheckTareWeight(string plateNumber, decimal currentTare) { VehicleTareRecord record GetVehicleTareHistory(plateNumber); if (record null || record.HistoryCount 0) { // 没有历史皮重记录首次称重直接通过但保存记录 SaveTareHistory(plateNumber, currentTare); return true; } decimal avgTare record.AverageTare; decimal deviation Math.Abs(currentTare - avgTare) / avgTare; // 偏差超过3%时触发异常 if (deviation 0.03m) { LogAbnormalTare(plateNumber, avgTare, currentTare); PlayVoicePrompt(皮重异常请等待人工处理); return false; } return true; }这里的关键参数是 3% 的偏差阈值。实际项目里这个值不能拍脑袋定——你要先收集至少 20 条同一辆车的正常皮重记录做统计然后根据标准差来设定阈值。如果是拉煤、拉沙这种重载车辆轮胎粘泥导致的皮重波动可能达到 200kg 甚至更多3% 阈值对于 15 吨的车就是 450kg基本能容忍。但拉轻货的面包车总重才 3 吨3% 只有 90kg轮胎沾泥就触发误报需要适当放宽。4.2 数据存储设计称重记录表结构如何支持追溯数据表设计直接决定这套系统能不能用来做结算和追溯。源码里的称重记录表涵盖的字段比较完整称重编号、车牌号、称重时间、毛重、皮重、净重、货物名称、供应商/客户、判定结果、车牌照片路径、车辆全景照片路径、仪表读数原始值、操作模式自动/手动。照片路径的保存方式是这份源码里比较讲究的地方。它没有把图片二进制直接写入数据库而是把抓拍图片存到本地磁盘目录数据库只保存相对路径。这样做的好处很明显——数据库体积不会爆炸而且图片文件可以直接用 Windows 资源管理器查看和管理。坏处是如果磁盘文件被误删或者目录权限变化数据库里的记录会变成“死链”。-- 称重记录核心表结构精简 CREATE TABLE weigh_records ( id INT PRIMARY KEY IDENTITY(1,1), record_no VARCHAR(32) NOT NULL, -- 称重编号格式日期流水号 plate_number VARCHAR(20) NOT NULL, -- 车牌号 weigh_time DATETIME NOT NULL DEFAULT GETDATE(), gross_weight DECIMAL(10,3) NOT NULL, -- 毛重单位吨 tare_weight DECIMAL(10,3) NOT NULL, -- 皮重 net_weight DECIMAL(10,3) NOT NULL, -- 净重 goods_name VARCHAR(50) NULL, -- 货物名称 pass_status TINYINT NOT NULL DEFAULT 0, -- 0正常 1异常 2人工确认 plate_image_path VARCHAR(200) NULL, -- 车牌照片路径 scene_image_path VARCHAR(200) NULL, -- 全景照片路径 operator_type TINYINT NOT NULL DEFAULT 1, -- 1自动称重 2人工称重 remark VARCHAR(200) NULL );建议你在实际部署时给这个表加上索引尤其在 weigh_time 和 plate_number 两个字段上。数据量大了以后如果每天几千条称重记录全表扫描的查询会越来越慢。另外如果后续要对接财务系统最好把货物名称和供应商客户做成单独的维度表用外键关联而不是直接存文本不然统计报表时会非常痛苦。4.3 数据安全与备份断电、断网、数据库异常的三重防线工业现场的电源质量和网络环境都比较恶劣无人值守系统如果因为断电丢数据那比人工操作还不靠谱。源码里针对这个问题做了两个关键设计一是数据库操作的事务保护一批数据要么全部写入成功要么全部回滚二是本地缓存的降级方案——当数据库连接失败时称重数据先写到本地 XML 或 SQLite 文件网络恢复后自动补传。// 数据库写入降级策略 public bool SaveWeighRecord(WeighRecord record) { try { using (var conn new SqlConnection(dbConnectionString)) { conn.Open(); // 使用事务确保记录和照片路径同时写入 using (var trans conn.BeginTransaction()) { // 执行INSERT操作... trans.Commit(); } } return true; } catch (SqlException ex) { // 数据库不可用时写入本地缓存文件 LogLocalCache(record, ex.Message); return false; } }这里的事务处理保证了称重记录不会出现“写真了一半”的情况。本地缓存机制是在数据库连接异常时的安全网——称重业务不能被数据库故障阻断否则车辆会堵在磅台上。缓存文件的格式建议用 JSON 或 XML每条记录一个独立节点恢复传输时逐条读取并写入数据库成功一条删除一条。要注意的是这个逻辑必须在多线程环境下保证线程安全否则两个线程同时读写缓存文件会导致数据错乱。5. 项目落地避坑串口不稳定、称重漂移、数据库锁死等常见问题的排查5.1 串口数据乱码或间歇性丢失仪表读数忽高忽低现象称重过程中仪表读数偶尔出现明显错误值比如负值、超出量程的夸张数字或者串口数据时不时丢失几帧导致稳定判断超时。原因绝大多数情况下是串口参数设置与仪表实际参数不一致。很多仪表出厂默认是 7 位数据位或带校验位而上位机代码里默认写的是 8N18 数据位、无校验、1 停止位两者不匹配就会出现间歇性乱码。另外串口线过长超过 15 米、使用了劣质 USB 转串口线、或者现场有变频器产生的电磁干扰也会导致数据错误。解决先用串口调试助手连上仪表直接看仪表发出的原始帧确认协议参数。然后在代码里对每一帧增加校验——比如解析前检查帧长度和 CRC 校验校验不过的直接丢弃不要参与重量计算。最后如果现场干扰严重把 RS232 换成 RS485或者用光电隔离的串口转换器这是最省心的物理层解决方案。5.2 车辆完全上磅后系统仍提示“请调整位置”现象红外对射已经未被遮挡了车辆也完全停在地磅有效区域内但系统依然语音播报位置异常流程卡住。原因最常见的原因是红外对射的安装角度有偏差车辆某些部位比如后视镜、货箱边缘在行驶过程中触发了遮挡但停车后遮挡状态没有及时复位。还有一种情况是红外继电器的状态检测逻辑写反了——正常应该是未遮挡为低电平、遮挡为高电平但现场接线方式导致电平逻辑相反。解决先查看输入端口的状态值确认遮挡和未遮挡对应的电平是否和代码里的判断一致。然后在程序中增加约 2~3 秒的消抖延时红外状态连续保持 2 秒不变才算有效这样可以过滤掉车辆行驶过程中造成的瞬时遮挡。如果还是不行检查红外对射的发射和接收窗口是否被灰尘覆盖用干净抹布擦拭后重新校准。5.3 空车重量漂移称重精度越来越差现象地磅每天空秤校零后刚开始还很准过两三个小时空车重量就出现 ±50kg 甚至更大的漂移。原因一是传感器受温度影响产生温漂——特别是夏天太阳暴晒后传感器和秤体膨胀系数不一致导致零点漂移二是称重仪表本身的零点跟踪功能太敏感三是秤台底部有杂物卡住或者限位螺栓与秤体间隙太小热胀冷缩后产生机械摩擦。解决在程序里增加“每日定时校零”逻辑比如每天早上 6 点和下午 2 点自动校零一次避开温度变化最大的时段。同时检查机械部分——清理秤台底部杂物把限位螺栓的间隙调整到 3~5mm。如果漂移仍然存在检查传感器是否进水地磅传感器接线盒的防水非常关键很多现场漂移问题都是接线盒进水导致的。5.4 数据库写入越来越慢几天后系统频繁超时现象运行前两天一切正常之后称重记录写入速度逐渐变慢最终导致系统卡死或数据库连接超时。原因称重记录表的数据持续增长后没有维护索引也没有做分区或归档。更常见的问题是照片路径字段里积累了海量数据而数据库服务器和工控机共用一台设备磁盘 I/O 被大量图片文件的写入拖垮。解决给 weigh_time 和 plate_number 加复合索引定期每周把超过 90 天的称重记录归档到历史表主表只保留近期数据。图片文件按照“年/月/日”目录结构存储定期把旧照片转移到独立硬盘或 NAS 存储数据库里只保留路径。另外把数据库服务和上位机程序分到两台机器上如果现场条件不允许至少要给数据库单独一块 SSD。5.5 无人值守系统半夜异常停机第二天才发现现象系统在夜间无人值守时停止响应司机无法正常称重直到第二天早上管理员到现场才发现。原因 Windows 系统自动更新重启、工控机电源管理里设置了硬盘休眠、或者程序本身遇到未处理的异常导致进程退出。无人值守系统最怕的就是静默崩溃。解决给工控机设置电源计划——禁用全部休眠和自动重启选项关闭 Windows Update 的自动安装。在程序里添加全局异常捕获未处理的异常先写入日志文件再退出同时通过短信猫或微信推送报警给值班人员。最稳妥的方案是加一个看门狗程序每 30 秒检查一次主程序进程是否存活如果挂了就自动拉起。这套源码本身没有看门狗但你可以用 Windows 计划任务或者一个简单的批处理循环实现同样的效果。6. 进阶技巧用模拟器把整条称重链路在开发机上完整跑通收到源码后不要急着接真实硬件调试先搭一套模拟环境把流程跑通这样后面到现场只调硬件参数就行。我的习惯是先做一个仪表模拟器和一个道闸模拟器用虚拟串口工具把数据接进来。第一步安装虚拟串口软件比如 Virtual Serial Port Driver 之类的工具创建一对互联的虚拟串口 COM3 和 COM4。把你的仪表模拟程序连接到 COM3把上位机程序的串口参数改成连接 COM4这样模拟器发的数据就能被上位机当作真实仪表数据接收。// 仪表模拟器按协议格式持续发送随机称重数据 private void Timer_Tick(object sender, EventArgs e) { Random rand new Random(); decimal fakeWeight 10m (decimal)(rand.NextDouble() * 40m); // 模拟10~50吨重量 string frame string.Format(ST,GS,{0:F3},kg\r\n, fakeWeight); serialPort.Write(frame); // 写入虚拟串口COM3 }这个模拟器的关键是把帧格式做真实。不同品牌的仪表帧格式差异很大你需要在串口调试助手里真实抓一次仪器的输出然后把帧格式原样写在模拟器里。模拟数据不用太精确关键是让上位机的解析代码能够“认为”这是合法数据。通过这种方式你可以在办公室里把车牌识别后的流程全部走通包括皮重异常检测、道闸联动、数据库写入这些环节。第二步用图片模拟车牌识别。真实环境里车牌识别是通过调用 DLL 或者 HTTP 接口实现的模拟时直接用一个随机生成器每次返回一个格式正确的模拟车牌号即可。这样可以测试数据库表结构和称重记录生成的完整逻辑。第三步验证数据落库。跑完一轮模拟称重后去数据库里查记录重点看毛重、皮重、净重三个字段的数值计算以及图片路径字段是不是生成了合理值。我一般会在模拟模式下跑 200 组随机数据然后写几个 SQL 查询检查有没有脏数据——比如净重为负、车牌为空、重量超过地磅最大量程。这些都是真实项目中容易踩到的坑。从那以后我每次接手一套新的地磅项目都强制自己先花半天时间把模拟环境搭好跑一遍全流程再碰真实硬件。这套源码本身就是个很好的参照物你把模拟器接上去跑通了业务逻辑再上现场能省掉一大半的调试焦虑。硬件通信出问题可以用串口助手定位业务逻辑出问题就要靠模拟器快速复现。希望这个流程对你也有用少走几趟现场比什么技巧都实在。本文还有配套的精品资源点击获取