Java集成海康与芊熠摄像头,打造无人值守地磅称重系统

Java集成海康与芊熠摄像头,打造无人值守地磅称重系统 简介这是一套面向物流仓储、码头及工厂过磅管理场景的Java企业级称重系统源码适用于具备Java Web与桌面应用开发基础的中级以上开发者学习与二次开发。系统深度融合海康威视摄像头实现实时视频监控与过磅抓拍与芊熠摄像头完成高准确率车牌识别打通称重数据、图像证据与车辆身份信息解决传统过磅中人工登记易错、过程不可溯、数据难关联等核心痛点。资源包共483个文件含219个Java后端逻辑文件、26个FXML桌面界面文件、44个JavaScript前端交互脚本、39个XML配置与38个CSS样式文件以及33个DLL驱动库支撑硬件通信整体压缩后41.25MB前端采用Bootstrap、FullCalendar、DataTables等主流组件风格统一且可扩展性强。目前已有723人下载学习提供完整模块化结构如ewcrm-service、ewcrm-dao、ewcrm-fx等清晰分层、服务器端数据持久化方案及后台编辑查看能力开箱即可运行并快速对接实际业务场景。 做过地磅系统的人都知道这套东西看起来简单真正落地全是细节。我最初接这个项目时甲方就一句话你们帮我把车牌识别、称重记录、数据上传这些串起来以前都是人工填单子太慢了。听起来不算复杂结果从海康威视SDK的JNA封装、芊熠车牌识别相机的HTTP接口调试到仪表串口数据稳定读取再到光电隔离、道闸联动整个周期将近三个月才敢说稳定运行。这篇就把整个系统的设计思路和源码实现串起来讲一遍包括那些踩过的坑。这套系统本质上解决的是车辆驶上地磅后系统自动识别车牌、自动读取仪表重量、自动生成称重记录并把抓拍图片和过磅数据绑定存档全程不需要人工填单。核心是Java后端集成了海康威视摄像头做监控与图片抓拍用芊熠车牌识别相机做车牌号的自动识别最后落库并向第三方ERP/MES系统推送。适合正在做无人值守称重、智能物流、工厂进出厂管理的开发者参考尤其是第一次接触海康SDK或者芊熠相机HTTP协议的人。1. 为什么过磅系统要集成车牌摄像头传统地磅的痛点与改造思路1.1 传统过磅流程的三宗罪没有摄像头的传统地磅过磅流程基本是这样司机把车开上磅司磅员在电脑上手动选择空车还是重车输入车号等仪表数字稳定手抄毛重或皮重最后打印一张磅单。这个流程最大的问题不是慢而是错。第一车号登记靠问。司机报个牌号司磅员输进系统字输错了后面对账全乱。第二作弊空间大。车不完全上磅、压边、跳磅这些操作单靠人眼很难抓。第三纠纷说不清。货主说这车数据不对但系统里没有现场照片拿不出证据。所以后来很多地磅改造项目摄像头就成了标配。1.2 集成摄像头后一个完整的自动过磅流程长什么样我做的这套系统落地后的完整流程是这样的车辆驶入磅区地感线圈或红外对射触发后芊熠车牌识别相机抓拍车头识别出车牌号同时海康威视球机抓拍一张全景照片。系统判断当前是上磅动作开始等仪表重量稳定。稳定后把重量和车牌绑定生成一条过磅记录。如果车还在系统车辆档案里自动带出常用皮重直接算出净重不是内部车辆就走登记-上磅-下磅两次称重流程。最后数据推送ERP同时把磅单图片、抓拍照片一并归档。全程不需要司磅员碰键盘。对比一下传统流程单次过磅时间可以从两分钟左右压缩到二十到三十秒更重要的是数据可追溯图片和记录绑定出问题能倒查。1.3 海康威视和芊熠摄像头分别承担什么角色这个项目里我用的是两套摄像头协同不是二选一。海康威视负责的是看得见、留得下、查得到。它接入现场监控大屏实时预览对磅台全景和车尾等进行抓拍。我用的海康摄像头是IPC球机布防报警后可以联动抓拍。这部分主要依赖海康HCNetSDK通过JNA把C接口封装成Java能调用的方法。芊熠负责的是看得准。它是专门的车牌识别一体机直接输出结构化结果比如车牌号、车牌颜色、识别时间不用我们自己跑算法。和传统OpenCV识别方案比芊熠的优势是识别速度快、夜间红外补光效果好、误判率低而且对外暴露的是HTTP JSON接口Java对接非常方便。两套相机在逻辑上完全独立但业务上必须联动芊熠识别到车牌号海康抓拍到现场图后台按同一时间戳和磅单号关联存储。2. 系统整体架构与模块划分不止是称重拍照那么简单2.1 硬件层地磅仪表、道闸、红绿灯、摄像头的协同先列一下这套系统里涉及的硬件设备做后端开发的人常常忽略这一层结果到现场才发现接口对不上。地磅仪表我用的耀华XK3190-A9串口输出连续重量数据车牌识别相机芊熠T260系列通过网口输出HTTP JSON监控相机海康威视IPC通过RTSPHCNETSDK对接道闸控制器遥控开关量输入部分场景接入继电器控制红绿灯提示车辆可以上磅 / 称重完成可以下磅地感线圈或红外对射判断车辆是否完全上磅硬件层设备通过交换机接入同一个局域网。这个网段规划很重要我项目里就吃过亏海康相机默认IP是192.168.1.64芊熠默认是192.168.1.120如果不提前规划到现场装完才发现全冲突了。2.2 Java后端分层设计与关键依赖后端是Spring Boot MyBatis Plus架构Maven多模块项目。我按对接层、业务层、管理后台三层来拆weigh-api对外接口模块给ERP、MES推送称重数据weigh-core核心业务模块过磅流程、数据校验、重量稳定判断weigh-camera-hikvision海康SDK封装模块weigh-camera-qianyi芊熠HTTP协议对接模块weigh-serial串口仪表通讯模块weigh-admin管理后台车辆档案、记录查询、磅单打印这个过程我用了JNA调用海康SDK用了RXTX和jSerialComm做串口通讯数据库用的MySQL缓存用的Redis。为什么用JNA而不是海康官方Java SDK因为海康官方在较早时期对Java的支持是通过JNA demo实现的而且自己管理DLL动态库反而更灵活交叉编译、多平台部署都好控制。jSerialComm则是因为它比RXTX在Windows下更稳定串口热插拔不容易崩。2.3 数据库表设计称重记录、车辆档案、摄像头配置核心就是三张表车辆档案表、称重记录表、摄像头配置表。车辆档案表保存车牌号、车辆类型、常用皮重、所属单位、设备绑定关系。称重记录表是主表一次上磅生成一条记录字段包括流水号、车牌号、毛重、皮重、净重、称重时间、方向进/出、操作方式自动/人工、抓拍图片路径、识别图片路径、状态。摄像头配置表保存每一台相机的IP、端口、用户名、密码、相机类型海康/芊熠、安装位置入口/出口、是否启用。我当时设计称重记录表的时候特意加了weigh_status字段用来表示当前这条记录处于哪个阶段1上磅待识别、2已识别待稳定、3称重完成、4已推送。这个状态字段帮了大忙因为现场断网、推送失败时可以通过定时任务补推。索引上有一个坑要提醒车牌号、称重时间这两个字段必须建联合索引否则量大了以后查询磅单列表会非常慢。我见过一个项目称重记录表才二十万行查一天的数据要十几秒原因就是没索引。3. 海康威视摄像头接入从SDK初始化到车牌抓拍的完整链路3.1 海康SDK的初始化与登录海康摄像头接入的底层是HCNetSDK.dll通过JNA封装。初始化很简单HCNetSDK hcNetSdk Native.load(HCNetSDK, HCNetSDK.class); hcNetSdk.NET_DVR_Init();但是登录比较关键必须用NET_DVR_Login_V40而不是老的NET_DVR_Login。因为V40兼容性好返回的登录句柄支持后续报警布防和抓图。如果项目用的是老设备只有NET_DVR_Login能登录那也没办法我看现场情况来切换。登录成功后保存lUserID句柄后续所有操作都靠它。这里有个细节海康SDK每次初始化只能做一次多次调用NET_DVR_Init()会导致资源泄漏。项目启动时初始化一次关闭时调用NET_DVR_Cleanup()释放。还有一点海康不同型号的相机SDK版本要求不一样。我项目里同时有DS-2CD系列和DS-2DE系列球机就必须用比较新的SDK版本否则球机的一些云台功能调不了。发布的时候SDK的dll、配置文件HCNetSDKCom文件夹都要放到Java项目的执行目录下否则运行时会报加载失败。第一次现场部署时就是因为漏了这个程序启动报找不到hlog.dll查了半天才发现SDK组件目录没一起打包。3.2 实况预览与抓图实时预览在Java里最省事的方案是直接通过RTSP拉流用VLC或者JavaCV显示。但是抓图也就是拍照取证还是要走SDK。抓图有几种方式NET_DVR_CapturePicture、NET_DVR_CaptureJPEGPicture。我建议用NET_DVR_CaptureJPEGPicture因为它是直接编码成JPEG参数可控不需要单独处理BMP转JPEGNET_DVR_JPEGPARA jpegPara new NET_DVR_JPEGPARA(); jpegPara.wPicSize 0xff; // 图片大小按实际配置 jpegPara.wPicQuality 0xff; boolean result hcNetSdk.NET_DVR_CaptureJPEGPicture( lUserID, 1, jpegPara, picturePath.getBytes(), 1024 * 1024);注意抓图路径在Windows下要用GBK编码Linux下用UTF-8这个不同平台字节码差异很容易踩坑。我后来统一用ByteUtil.getStringBytes封装了一个方法去兼容。3.3 海康车牌识别的结果解析海康部分设备本身自带车牌识别功能通过报警布防实时回调车牌信息。通过NET_DVR_SetupAlarmChan_V41注册报警回调当车辆经过相机视域时SDK回调ALARM_VC_PLATE消息里面带有结构化车牌数据。回调函数里要解析的是NET_DVR_PLATE_INFO结构。这个结构里主要字段有sLicense车牌号字符串byColor车牌颜色0蓝色、1黄色、2白色、3黑色byType车牌类型struPlateRect车牌在画面中的位置矩形不过说实话海康的车牌识别结果我用得不多因为项目里的主识别设备是芊熠。海康这个回调更多是作为一个备胎方案。如果现场芊熠相机故障自动切换走海康识别。这个双保险机制后面详说。回调函数是海康SDK在它自己的线程里调用的注意不要在里面做耗时操作比如数据库写入、HTTP请求。一定要把数据拷贝出来扔到自己的线程池里处理。我第一次写的时候直接在回调里调了MyBatis插入结果SDK线程卡死报警消息全都不往后走了设备端都被打满了。3.4 常见坑SDK内存泄漏、回调阻塞、网络超时海康SDK接起来容易稳定运行难说说我实际踩过的坑。第一个是内存泄漏。海康SDK的NET_DVR_Init和NET_DVR_Cleanup不能在业务代码里频繁调用每次登录和登出也要保持间隔。我项目早期版本每次抓图前都重新登录设备结果跑了几天Java进程内存直接爆掉。解决方式是做一个设备连接管理器启动时建立连接长连接保活断线才重连。第二个是回调阻塞。海康报警回调线程是内核态线程如果阻塞了所有报警都会积压。实际做法是把回调里收到的数据对象浅拷贝出来丢到ExecutorService处理回调方法立即返回。第三个是网络超时。海康相机的RTSP流和SDK信令都在同一个网口如果现场网络环境差设备经常掉线。一定要做心跳检测和自动重连。我写了一个定时任务每隔10秒检查一次NET_DVR_GetDVRWorkState连续失败3次就重新登录。4. 芊熠摄像头接入为什么选择它 它的JSON接口实现4.1 芊熠车牌识别相机的工作原理芊熠车牌识别一体机本质上是一台带算法的小型嵌入式设备前端集成了摄像机、补光灯、识别算法。它与海康那种通用相机不一样不需要你在后端跑识别模型相机在本地就完成了车牌定位、字符分割、字符识别然后把结果通过网口推给你。相机工作方式有两种一种是主动推图模式相机检测到车辆进入识别区域后主动把识别结果POST到后台服务器另一种是请求响应模式后台按需向相机请求结果或发送图片给相机做识别。我项目里用的是第一种主动推图模式。相机内配置好后台接口地址每当有车通过芊熠相机就向后台发送一个HTTP POST请求请求体是JSON。4.2 HTTP/JSON接口调用细节芊熠相机主动上传的JSON格式大致是这个样子{ plateNo: 粤B12345, plateColor: blue, plateType: standard, time: 2025-03-15 14:23:05, image: http://192.168.1.120/capture/202503151423056789.jpg, imageBase64: , bright: 12, vehicleType: truck, schedule: 45 }字段不固定不同固件版本会差异。我踩过的坑是旧版相机返回的plateColor是1、2这种数字枚举新版相机直接返回blue、yellow字符串。所以解析的时候不能写死枚举映射我给这个字段写了两种解析策略。后端接收接口我用的Spring Boot的RestController。做了一层签名校验加IP白名单因为相机是固定IP的只允许局域网内那几台相机IP访问保证安全。PostMapping(/api/camera/qianyi/notify) public ResultString receivePlateNotify(RequestBody QianYiPlateNotify notify) { // 校验来源IP String clientIp request.getRemoteAddr(); if (!allowedCameraIps.contains(clientIp)) { return Result.fail(ip not allowed); } // 异步处理过磅业务 weighService.asyncProcessPlate(notify); return Result.success(ok); }有个细节相机的HTTP请求超时时间很短如果后台处理超过2秒相机会重传。所以接口里一定不能同步去处理完整过磅逻辑必须异步。我当时用Redis锁做了个去重防止相机重传导致同一辆车生成两条称重记录。4.3 识别结果关联的图片处理芊熠相机返回的消息里带了一个图片URL指向相机自身存储的抓拍图片。这里有个很实用的技巧不要直接把相机的URL存到数据库因为相机的存储空间有限过几天图片可能被覆盖掉。应当后台主动去相机拉取图片保存到本地文件服务器或对象存储再把路径写入称重记录表。图片下载可以直接用HttpClient:byte[] imageBytes httpClient.get(notify.getImage()); String localPath saveImageToLocal(plate_ notify.getPlateNo() _ timestamp .jpg, imageBytes);保存图片的同时我还会从海康相机抓一张全景图这就回到了前面说的双摄像头协同。车牌识别图负责证明车是谁海康全景图负责证明现场环境是什么样。两个图片在称重记录表里分别用plate_image_path和scene_image_path字段保存。5. 从识别到车牌到完成称重记录核心业务逻辑串起来5.1 过磅事件状态机整个过磅业务流程就是一台状态机。我一开始没画状态机直接if else写后面代码越来越乱重构时理了一下核心状态就四个WEIGHING车辆已上磅重量数据正在读取中PLATE_CONFIRMED车牌已识别等待重量稳定WEIGHT_STABLE重量稳定可以生成记录COMPLETED记录已生成并推送第三方系统在实际代码里我用枚举状态表驱动流转。每次芊熠回调、重量读表、重量稳定都会触发状态变更。流转顺序不一定完全相同比如外部车辆可能先生成毛重记录下磅后再生成皮重记录中间跨了很长时间。这个状态机设计有个好处就算某个环节出错了比如识别失败状态卡在WEIGHING后台定时任务可以扫出那些停留超过5分钟的状态记录触发人工介入流程。5.2 重量数据采集和稳定性判断重量数据来自仪表串口。耀华仪表的连续输出格式大概是这样的000123.4kg\r\n 000123.5kg\r\n用串口读取程序解析每帧数据。重量稳定不能只看一次读数我采用的方法是连续采集10次如果这10次的重量值最大和最小差值小于等于一个阈值比如10公斤就认为重量稳定。这个阈值可以配置毕竟不同地磅量程不一样。public boolean isStable(ListDouble weights, double threshold) { double max Collections.max(weights); double min Collections.min(weights); return (max - min) threshold; }串口这块是个重点。我最早用RXTX结果在Windows上总是随机丢帧后来换jSerialComm才好。另外串口读取必须保证线程安全一次只允许一个线程读取。我们项目的重量读取和状态判断放在同一个线程里采用生产者消费者模式串口读取线程作为生产者重量数据放到队列业务逻辑线程作为消费者。5.3 一次过磅的核心代码流程把前面所有模块串起来一次标准的过磅处理流程大致是public void processPlateNotify(QianYiPlateNotify notify) { // 1. 检查当前是否有进行中的称重记录 WeighRecord record weighRecordMapper.selectOngoingByPlate(notify.getPlateNo()); // 2. 如果没有进行中的记录创建一条新记录状态设为WEIGHING if (record null) { record createNewRecord(notify.getPlateNo(), Direction.IN); } // 3. 从串口读取当前重量 double currentWeight serialService.readCurrentWeight(); // 4. 判断重量是否稳定 if (weightStableChecker.isStable()) { // 5. 如果当前记录还没有毛重写入毛重否则写入皮重 if (record.getGrossWeight() null) { record.setGrossWeight(currentWeight); } else { record.setTareWeight(currentWeight); record.setNetWeight(calculateNetWeight(record)); record.setStatus(WeighStatus.COMPLETED); } // 6. 保存图片 saveImages(record, notify); // 7. 推送第三方系统 pushToExternalSystem(record); weighRecordMapper.updateById(record); } }这里有个业务细节需要说清楚毛重和皮重的区分依赖的是车辆方向。在我这个系统里车辆档案里保存了常用皮重如果车牌是内部常跑车辆第一次上磅就能直接带出皮重一次过磅完成外部车辆则必须两次过磅。如果是出厂方向那么第一次读到的是毛重出厂后再读到的反而是皮重。所以每次读取重量时不能只看当前值还要看记录的方向和已有重量字段。还有一个跨界问题怎么判断车辆是否完全上磅我最初只用重量值变化来判断但现场有人把车停在磅边让重量轻微变化系统就傻了。后来加了地感线圈信号只有当重量超过一个触发阈值比如500公斤且地感信号为真时才认为车辆上磅这套逻辑才算稳定。这个部分不在源码里但是在现场调试时必须带上。5.4 与第三方ERP/MES对接的接口设计称重数据最终要推送给工厂的ERP系统。这里涉及到接口协议选型。我见过太多项目称重系统做完对接ERP时因为接口字段不一致扯皮扯半天。所以设计时我就把推送接口独立出来做成可配置的多通道推送。我采用HTTP POST JSON 重签名的方案。每次推送称重记录时携带的字段至少包括record_no、plate_no、gross_weight、tare_weight、net_weight、weigh_time、direction。第三方系统如果出现短暂不可用就用本地消息表落库通过定时任务补推。等ERP恢复正常可以自动把积压的记录推完。6. 调试部署阶段的真实踩坑记录给后来的项目省几个月时间6.1 摄像头IP与网段冲突这是我在项目开工第一天踩的坑。设备到场海康相机默认IP是192.168.1.64芊熠相机默认IP是192.168.1.120仪表通过串口不过网。现场局域网网段恰好是192.168.1.x但网关是192.168.1.1整个局域网里已经有几十台设备。结果我把海康相机插上网线整个网络立刻瘫痪广播风暴。因为海康出厂设置开了DHCP而现场没有DHCP服务器它自动分配了一个和网关冲突的IP。解决方案所有摄像头第一次上电前必须用海康SADP工具或者芊熠配置工具先把IP改成规划好的静态地址并且禁止DHCP。顺序不能反。我在项目中专门写了一个工具类通过ARP扫描局域网内设备防止新设备IP撞车。6.2 仪表串口通讯不稳定时怎么办现场最头疼的是串口丢帧。我一开始以为串口波特率、校验位设置错了查了半天后来用串口监视器一看PC发命令到仪表没问题但仪表返回数据时总是断断续续。原因很简单称重仪表放在磅房里和电脑之间的串口线穿过强电电缆桥架电磁干扰严重。解决方法不复杂但很有效。第一换成带屏蔽层的RS232线外层屏蔽单端接地第二串口线尽量不走强电桥架实在要走穿金属管。第三软件上对于收到的每帧数据做格式校验不合法就丢弃重新等下一帧。这样处理过后丢帧率从10%降到了几乎为零。如果现场距离超过15米RS232就不行了建议改用RS485转USB抗干扰能力完全不是一个级别。不过要注意RS485是半双工的需要控制发送和接收方向。6.3 车牌识别误判和漏识别怎么兜底芊熠车牌识别再牛也有识别失败的时候。最常见的是泥浆遮挡车牌、强逆光、夜间车牌反光。我最开始直接依赖相机输出结果现场运行三天统计发现漏识别率大概在3%左右。这个比例对于无人值守系统来说太高了。兜底方案我做了一整套第一层芊熠识别结果回来后做正则校验车牌不符合标准格式就标记为待人工确认第二层同一辆车如果连续5次识别结果不一致系统自动触发海康SDK抓拍并导入人工审核队列第三层后台管理页面专门做一个无车牌登记入口手机或PC端手动补录车牌补录后自动和重量记录关联最终这套系统即使识别失败也不会导致过磅业务停摆最多就是多一个人工确认步骤。6.4 工地现场的防尘防雷与UPS断电保护最后这点是写给做硬件部署的人看的。地磅房现场环境远比机房里恶劣粉尘大、温差大、雷雨多。摄像头装在立杆上必须有防雷器否则一个雷击网口和电源口都可能烧掉。我项目里第一次雷雨季后四台相机烧了两台后来统一加了防雷器才解决。整个系统建议配一台小功率UPS容量不用太大能撑十分钟就行。因为突然断电很可能导致称重记录写到一半仪表那边没影响但数据库和图片文件可能损坏。我用的MySQL开了binlog断电恢复后可以做一致性修复但最好还是别断电。UPS还能给串口仪表供上电防止仪表本身突然掉电导致内部标定参数丢失。还有一点地磅房防尘也关键。工业现场的灰尘会进串口接口、摄像头尾线时间久了接触不良。所有室外接线接口要用防水胶带缠好再套热缩管。这个细节施工队不会替你想自己盯紧点。最后再分享一个我自己的习惯上线前无论如何要抽取连续三天的完整测试数据对比人工记录的磅单和系统自动记录的差异。不要小看这一步很多隐藏问题比如某个时间段芊熠相机补光后识别率下降、某台海康相机SDK连接不稳定都会在长时间运行测试里暴露出来。等真正投入生产后再发现往往会付出几倍成本去补救。本文还有配套的精品资源点击获取