【无人售货柜・RK+YOLO】篇 7:业务闭环!YOLO 实现售货柜开门前后商品比对 自动结算核心逻辑

news2026/3/21 5:38:45
目录一、新手先搞懂视觉开门柜的完整结算业务流程二、核心概念扫盲结算逻辑里的关键术语一次讲透1. 基线快照Base Snapshot2. 结果快照Result Snapshot3. SKU 计数单元4. IOU 匹配5. 置信度过滤三、90% 的新手都踩的坑结算逻辑的致命错误致命错误 1直接用两次识别的类别数量相减致命错误 2不做空间位置匹配只看类别致命错误 3没有容错机制异常情况也强行扣款四、商用级核心算法开门前后商品比对 数量统计步骤 1快照采集与识别结果标准化标准化处理规则严格执行标准化代码实现Java可直接嵌入之前的安卓项目步骤 2两次快照的商品匹配核心中的核心匹配核心规则商用级标准匹配算法代码实现步骤 3结算清单生成最终扣款依据结算清单生成代码步骤 4异常校验商用级容错机制必须做的 4 项异常校验异常校验代码实现五、完整业务流程嵌入安卓项目1. 新增全局状态变量2. 开门状态监听与快照采集3. 对接门状态信号六、新手必踩的坑 优化方案1. 摄像头轻微偏移匹配失败怎么办2. 商品被用户移动了位置匹配错误怎么办3. 叠放商品漏检导致结算错误怎么办最后说两句大家好我是黒漂技术佬。上一篇我们成功在 RK3576 安卓板子上跑通了 YOLO RKNN 实时商品识别摄像头一照商品的框、类别、置信度都能实时渲染出来很多朋友后台说 “终于跑通了”但我太懂新手了跑通实时识别只是第一步真正的核心难题来了怎么把这个识别能力变成售货柜的自动结算功能很多新手到这一步就卡壳了我能识别单张图里有什么商品但怎么对比用户开门前和关门后的商品变化怎么统计用户拿走了几瓶可乐、几包薯片怎么过滤误检避免乱扣款甚至有朋友直接把两次识别的类别数量一减就完事结果用户拿了 1 瓶可乐结算扣了 5 瓶被用户投诉到心态崩了。毫不夸张地说商品比对 结算逻辑是视觉售货柜能不能商用的核心命脉识别再准结算逻辑错了全白搭轻则亏钱重则被用户投诉下架。今天这篇我就给你把无人售货柜的完整结算逻辑扒得明明白白从业务流程拆解到核心比对算法再到安卓端代码实现、商用级容错机制全流程保姆级教学新手跟着做就能实现一套稳定、不会乱扣款的自动结算系统完全贴合商用需求。一、新手先搞懂视觉开门柜的完整结算业务流程很多新手做结算逻辑上来就怼着代码写根本没搞懂真实的业务流程写出来的逻辑完全没法用。这里我用大白话把用户从扫码到扣款完成的全流程拆成 6 个核心步骤每个步骤对应的技术动作给你讲得明明白白。表格业务步骤用户动作我们的技术动作核心要求步骤 1扫码开门用户用微信 / 支付宝扫柜子上的二维码授权免密支付后台验证用户权限给柜子发送开门指令安卓端收到开门信号必须确认门真的开了才能进入下一步避免假开门步骤 2开门前基线快照采集门即将打开的前 1 秒安卓端锁定摄像头拍一张清晰的货架全景图用 YOLO 跑推理输出所有商品的类别、位置、置信度存为【基线快照数据】必须在开门前完成不能有手、异物遮挡这是商品比对的基准步骤 3开门取货用户开门自由拿取、放回商品安卓端不做实时推理避免手遮挡、动作模糊导致的无效识别浪费 NPU 资源只监听门的状态全程监控门的开关状态超时未关门要提醒用户步骤 4关门后结果快照采集用户关上柜门安卓端立刻锁定摄像头拍一张和基线快照同角度、同分辨率的货架全景图用 YOLO 跑推理输出所有商品的类别、位置、置信度存为【结果快照数据】必须在关门后 1 秒内完成保证和基线快照的场景一致性步骤 5核心商品比对 数量统计无用户动作后台计算把基线快照和结果快照的识别结果做匹配比对统计每个 SKU 的数量变化减少的就是用户拿走的增加的就是用户放回的准确率必须 100%不能多算、少算这是整个流程的核心步骤 6生成结算清单 扣款用户手机收到扣款通知把统计好的商品清单、总价传给支付后台完成免密扣款给用户推送消费明细必须有容错机制异常情况不能扣款避免客诉看懂了吗整个结算流程的核心就是开门前拍个 “存档”关门后拍个 “新档”对比两个存档的差异算出用户拿了什么。而 YOLO 的作用就是给这两个存档提供精准的商品数据。【新手避坑红线】绝对不要用实时跟踪的方式做结算。很多新手觉得开门过程中实时跟踪商品的拿取动作更高级。但真实场景里用户的手会全程遮挡商品、动作模糊、商品叠放跟踪的准确率极低非常容易漏检、错检商用里 99% 的视觉柜都是用「开门前后快照比对」的方案稳定、可控、容错率高。二、核心概念扫盲结算逻辑里的关键术语一次讲透很多新手看结算算法被各种术语搞晕这里我用售货柜场景给你把所有核心概念讲明白后面看代码就不会懵了。1. 基线快照Base Snapshot开门前拍的货架全景图 YOLO 识别结果是整个比对的基准相当于游戏里的存档所有的数量变化都是以这个为基准计算的。2. 结果快照Result Snapshot用户关门后拍的货架全景图 YOLO 识别结果是比对的目标相当于游戏里的新存档我们要对比它和基线快照的差异。3. SKU 计数单元我们把 YOLO 识别到的每一个商品封装成一个标准化的计数单元包含 5 个核心信息商品类别 IDclassId置信度confidence商品中心点坐标(cx, cy)商品框的宽高(w, h)唯一标识trackId用来匹配两次快照的同一个商品4. IOU 匹配我们用两个商品框的 IOU交并比来判断两次快照里的两个商品是不是同一个商品。比如基线快照里的一瓶可乐和结果快照里的同一位置的可乐IOU 超过阈值就认为是同一个商品没有被拿走。5. 置信度过滤为了避免误检影响结算我们会过滤掉置信度低于阈值的识别结果比如置信度低于 0.6 的直接不纳入计数从源头减少误检。三、90% 的新手都踩的坑结算逻辑的致命错误在讲正确的算法之前我先给你把新手最容易犯的 3 个致命错误讲清楚现在记住后面少赔很多钱。致命错误 1直接用两次识别的类别数量相减这是新手最容易犯的错开门前识别到 3 瓶可乐关门后识别到 2 瓶就直接算用户拿走了 1 瓶。为什么错因为 YOLO 的识别会有误检和漏检。比如开门前把一瓶雪碧误识别成了可乐基线里可乐数量是 3实际只有 2 瓶关门后识别正确可乐数量是 2一减就变成用户拿了 1 瓶实际用户根本没拿直接乱扣款客诉直接炸。致命错误 2不做空间位置匹配只看类别很多新手觉得只要是同一个类别的商品就是同一个。比如开门前有 3 瓶可乐分别在左、中、右三个位置关门后左边的可乐被拿走了但是中间的可乐被识别成了 2 个总数量还是 3就认为用户没拿东西直接漏扣款亏钱。致命错误 3没有容错机制异常情况也强行扣款比如用户开门碰歪了摄像头两次快照的角度完全不一样比对结果全错或者柜内灯坏了关门后的快照一片黑识别结果全是误检。新手不管这些直接按比对结果扣款最后被用户投诉到下架。四、商用级核心算法开门前后商品比对 数量统计上面讲了错误的做法现在给你讲经过无数商用项目验证、稳定零客诉的正确比对算法全程大白话讲解每一步都有逻辑支撑新手也能看懂。整个算法分成 4 个核心步骤环环相扣从源头避免误检、漏检导致的结算错误。步骤 1快照采集与识别结果标准化不管是基线快照还是结果快照我们先把 YOLO 的识别结果做标准化处理过滤掉无效数据统一格式方便后续比对。标准化处理规则严格执行置信度过滤只保留置信度≥0.6 的识别结果低于这个阈值的直接丢弃从源头过滤误检单张快照内去重用 NMS 非极大值抑制去掉同一个商品的重复识别框避免一个商品被算成多个坐标归一化把商品框的坐标从像素坐标转换成 0~1 的归一化坐标就算两次快照的分辨率有轻微差异也不影响匹配封装成标准化对象把每个商品封装成SkuItem对象包含归一化后的中心点坐标、宽高、类别 ID、置信度标准化代码实现Java可直接嵌入之前的安卓项目java运行// 商品标准化实体类 public class SkuItem { public int classId; // 商品类别ID public float confidence; // 置信度 public float cx; // 中心点x坐标归一化0~1 public float cy; // 中心点y坐标归一化0~1 public float w; // 框宽度归一化0~1 public float h; // 框高度归一化0~1 public int trackId; // 匹配用的唯一ID public SkuItem(int classId, float confidence, float cx, float cy, float w, float h) { this.classId classId; this.confidence confidence; this.cx cx; this.cy cy; this.w w; this.h h; this.trackId -1; } // 获取商品的矩形框 public RectF getRect() { return new RectF(cx - w/2, cy - h/2, cx w/2, cy h/2); } } // 识别结果标准化处理函数 private ListSkuItem standardizeDetectionResult(ListDetectionResult rawResults, int imgWidth, int imgHeight) { ListSkuItem standardizedList new ArrayList(); // 1. 置信度过滤只保留≥0.6的结果 for (DetectionResult raw : rawResults) { if (raw.confidence 0.6f) { continue; } // 2. 坐标归一化转换成0~1 float cx raw.rect.centerX() / imgWidth; float cy raw.rect.centerY() / imgHeight; float w raw.rect.width() / imgWidth; float h raw.rect.height() / imgHeight; // 3. 加入标准化列表 standardizedList.add(new SkuItem(raw.classId, raw.confidence, cx, cy, w, h)); } // 4. 单张快照内去重按置信度排序后NMS standardizedList.sort((a, b) - Float.compare(b.confidence, a.confidence)); return nmsForStandardized(standardizedList); } // 标准化列表的NMS去重 private ListSkuItem nmsForStandardized(ListSkuItem list) { ListSkuItem result new ArrayList(); boolean[] suppressed new boolean[list.size()]; float NMS_THRESHOLD 0.3f; for (int i 0; i list.size(); i) { if (suppressed[i]) continue; SkuItem item list.get(i); result.add(item); for (int j i 1; j list.size(); j) { if (suppressed[j]) continue; SkuItem other list.get(j); // 同一个类别才做NMS if (other.classId ! item.classId) continue; // 计算IOU float iou calculateIOU(item.getRect(), other.getRect()); if (iou NMS_THRESHOLD) { suppressed[j] true; } } } return result; }步骤 2两次快照的商品匹配核心中的核心这一步是整个算法的核心我们要把基线快照里的商品和结果快照里的商品做一一匹配找出哪些商品还在哪些被拿走了哪些是新放回来的。匹配核心规则商用级标准同类别优先只有同一个类别的商品才能互相匹配可乐永远不能和雪碧匹配从源头避免类别错误IOU 匹配阈值两个商品的 IOU≥0.3就认为是同一个商品因为用户开门可能会碰动商品位置有轻微偏移阈值不能设太高置信度优先按置信度从高到低匹配先匹配置信度高的商品避免误检的商品把正确的商品匹配走了一对一匹配一个基线商品只能匹配一个结果商品不能重复匹配避免一个商品被算成多个匹配算法代码实现java运行// 匹配结果实体类 public class SkuChangeResult { public int classId; // 商品类别ID public String className; // 商品名称 public int baseCount; // 基线快照数量 public int resultCount; // 结果快照数量 public int takeCount; // 用户拿走的数量正数 public int putCount; // 用户放回的数量正数 public SkuChangeResult(int classId, String className) { this.classId classId; this.className className; this.baseCount 0; this.resultCount 0; this.takeCount 0; this.putCount 0; } } // 核心比对函数输入基线和结果的标准化列表输出每个SKU的变化 private ListSkuChangeResult compareSkuSnapshot( ListSkuItem baseSnapshot, ListSkuItem resultSnapshot, ListString classNames ) { // 初始化每个类别的结果 ListSkuChangeResult changeResults new ArrayList(); for (int i 0; i classNames.size(); i) { changeResults.add(new SkuChangeResult(i, classNames.get(i))); } // 1. 先统计每个类别在基线和结果里的总数量 for (SkuItem item : baseSnapshot) { changeResults.get(item.classId).baseCount; } for (SkuItem item : resultSnapshot) { changeResults.get(item.classId).resultCount; } // 2. 按类别分组分开匹配不同类别不互相干扰 for (int classId 0; classId classNames.size(); classId) { // 提取当前类别的基线商品和结果商品 ListSkuItem baseItems new ArrayList(); ListSkuItem resultItems new ArrayList(); for (SkuItem item : baseSnapshot) { if (item.classId classId) baseItems.add(item); } for (SkuItem item : resultSnapshot) { if (item.classId classId) resultItems.add(item); } // 3. 核心匹配给基线里的每个商品找结果里对应的同一个商品 boolean[] matched new boolean[resultItems.size()]; // 标记结果里的商品是否被匹配过 float MATCH_IOU_THRESHOLD 0.3f; // 匹配IOU阈值 int matchedCount 0; // 匹配成功的数量 // 按置信度从高到低匹配 baseItems.sort((a, b) - Float.compare(b.confidence, a.confidence)); resultItems.sort((a, b) - Float.compare(b.confidence, a.confidence)); for (SkuItem baseItem : baseItems) { float maxIou 0; int bestMatchIndex -1; // 找结果里和当前基线商品IOU最大的、未被匹配的商品 for (int i 0; i resultItems.size(); i) { if (matched[i]) continue; float iou calculateIOU(baseItem.getRect(), resultItems.get(i).getRect()); if (iou maxIou iou MATCH_IOU_THRESHOLD) { maxIou iou; bestMatchIndex i; } } // 匹配成功标记为已匹配 if (bestMatchIndex ! -1) { matched[bestMatchIndex] true; matchedCount; } } // 4. 计算当前类别的拿取和放回数量 SkuChangeResult changeResult changeResults.get(classId); // 拿走的数量 基线里没匹配到的数量 基线数量 - 匹配成功的数量 changeResult.takeCount changeResult.baseCount - matchedCount; // 放回的数量 结果里没被匹配到的数量 结果数量 - 匹配成功的数量 changeResult.putCount changeResult.resultCount - matchedCount; // 修正负数异常情况 if (changeResult.takeCount 0) changeResult.takeCount 0; if (changeResult.putCount 0) changeResult.putCount 0; } return changeResults; }步骤 3结算清单生成最终扣款依据比对完成后我们会得到每个 SKU 的拿取数量接下来就是生成最终的结算清单只保留用户拿走的商品过滤掉拿取数量为 0 的商品。结算清单生成代码java运行// 生成最终结算清单 private String generateSettlementList(ListSkuChangeResult changeResults, MapInteger, Float skuPriceMap) { StringBuilder settlement new StringBuilder(); float totalPrice 0f; settlement.append( 消费明细 \n); for (SkuChangeResult result : changeResults) { if (result.takeCount 0) continue; // 从价格Map里获取商品单价 float price skuPriceMap.getOrDefault(result.classId, 0f); float itemTotal price * result.takeCount; totalPrice itemTotal; // 拼接明细 settlement.append(result.className) .append( × ) .append(result.takeCount) .append( ) .append(String.format(%.2f, price)) .append( 小计) .append(String.format(%.2f, itemTotal)) .append(\n); } settlement.append(\n); settlement.append(总计).append(String.format(%.2f, totalPrice)); return settlement.toString(); }步骤 4异常校验商用级容错机制这是最后一步也是商用必须的一步我们要对比对结果做异常校验异常情况绝对不能扣款避免客诉和亏钱。必须做的 4 项异常校验快照有效性校验基线快照和结果快照的识别商品总数不能为 0为 0 说明摄像头没拍到东西、灯坏了直接判定为异常数量差异校验两次快照的商品总数量差异不能超过 80%比如基线有 20 个商品结果只有 3 个说明摄像头被碰歪了、遮挡了直接判定为异常拿取数量校验单个 SKU 的拿取数量不能超过基线里的数量避免出现 “基线有 2 瓶可乐结算拿了 5 瓶” 的离谱错误置信度校验结算清单里的商品必须是置信度≥0.6 的避免误检的商品被纳入结算异常校验代码实现java运行// 异常校验函数返回true表示正常false表示异常 private boolean checkSnapshotValid( ListSkuItem baseSnapshot, ListSkuItem resultSnapshot, ListSkuChangeResult changeResults ) { // 1. 快照商品总数不能为0 if (baseSnapshot.size() 0 || resultSnapshot.size() 0) { Log.e(Settlement, 异常快照商品总数为0); return false; } // 2. 总数量差异不能超过80% int baseTotal baseSnapshot.size(); int resultTotal resultSnapshot.size(); float diffRate Math.abs(baseTotal - resultTotal) / (float) baseTotal; if (diffRate 0.8f) { Log.e(Settlement, 异常商品数量差异过大差异率 diffRate); return false; } // 3. 单个SKU拿取数量不能超过基线数量 for (SkuChangeResult result : changeResults) { if (result.takeCount result.baseCount) { Log.e(Settlement, 异常拿取数量超过基线数量商品 result.className); return false; } } // 4. 所有结算商品置信度都≥0.6标准化的时候已经过滤这里做二次校验 for (SkuItem item : baseSnapshot) { if (item.confidence 0.6f) { Log.e(Settlement, 异常存在低置信度商品); return false; } } // 所有校验通过 return true; }五、完整业务流程嵌入安卓项目现在我们把上面的所有逻辑嵌入到之前的安卓项目里实现从开门→快照采集→比对→结算的完整闭环。1. 新增全局状态变量在 MainActivity 里新增以下全局变量用来控制业务流程java运行// 售货柜门状态 private boolean isDoorOpen false; // 基线快照数据 private ListSkuItem baseSnapshot null; // 商品价格Mapkey是类别IDvalue是单价自己根据商品配置 private MapInteger, Float skuPriceMap new HashMap();2. 开门状态监听与快照采集我们新增两个函数分别处理开门前的基线快照采集和关门后的结果快照采集与比对结算java运行// 开门前采集基线快照 private void captureBaseSnapshot() { new Thread(() - { try { // 1. 拿到摄像头全景Bitmap Bitmap bitmap textureView.getBitmap(); if (bitmap null) { runOnUiThread(() - Toast.makeText(this, 基线快照采集失败, Toast.LENGTH_SHORT).show()); return; } // 2. YOLO推理拿到原始识别结果 Bitmap resizedBitmap Bitmap.createScaledBitmap(bitmap, INPUT_WIDTH, INPUT_HEIGHT, true); byte[] inputData bitmapToInputData(resizedBitmap); float[][] outputs rknnRuntime.runModel(inputData); ListDetectionResult rawResults parseOutputs(outputs, bitmap.getWidth(), bitmap.getHeight()); // 3. 标准化处理生成基线快照 baseSnapshot standardizeDetectionResult(rawResults, bitmap.getWidth(), bitmap.getHeight()); runOnUiThread(() - { Toast.makeText(this, 基线快照采集成功共识别到 baseSnapshot.size() 个商品, Toast.LENGTH_SHORT).show(); // 标记门已打开 isDoorOpen true; }); // 释放资源 bitmap.recycle(); resizedBitmap.recycle(); } catch (Exception e) { e.printStackTrace(); } }).start(); } // 关门后采集结果快照执行比对结算 private void captureResultSnapshotAndSettle() { new Thread(() - { try { // 1. 检查基线快照是否存在 if (baseSnapshot null || baseSnapshot.size() 0) { runOnUiThread(() - Toast.makeText(this, 基线快照不存在无法结算, Toast.LENGTH_SHORT).show()); return; } // 2. 拿到摄像头全景Bitmap Bitmap bitmap textureView.getBitmap(); if (bitmap null) { runOnUiThread(() - Toast.makeText(this, 结果快照采集失败, Toast.LENGTH_SHORT).show()); return; } // 3. YOLO推理拿到原始识别结果 Bitmap resizedBitmap Bitmap.createScaledBitmap(bitmap, INPUT_WIDTH, INPUT_HEIGHT, true); byte[] inputData bitmapToInputData(resizedBitmap); float[][] outputs rknnRuntime.runModel(inputData); ListDetectionResult rawResults parseOutputs(outputs, bitmap.getWidth(), bitmap.getHeight()); // 4. 标准化处理生成结果快照 ListSkuItem resultSnapshot standardizeDetectionResult(rawResults, bitmap.getWidth(), bitmap.getHeight()); // 5. 核心比对 ListSkuChangeResult changeResults compareSkuSnapshot(baseSnapshot, resultSnapshot, classNames); // 6. 异常校验 boolean isValid checkSnapshotValid(baseSnapshot, resultSnapshot, changeResults); if (!isValid) { runOnUiThread(() - { Toast.makeText(this, 结算异常已暂停扣款请联系工作人员, Toast.LENGTH_LONG).show(); // 重置状态 isDoorOpen false; baseSnapshot null; }); return; } // 7. 生成结算清单 String settlementList generateSettlementList(changeResults, skuPriceMap); // 8. 主线程展示结算结果这里可以对接支付后台完成扣款 runOnUiThread(() - { // 展示结算明细弹窗 new AlertDialog.Builder(this) .setTitle(消费结算成功) .setMessage(settlementList) .setPositiveButton(确定, null) .show(); // 重置状态 isDoorOpen false; baseSnapshot null; }); // 释放资源 bitmap.recycle(); resizedBitmap.recycle(); } catch (Exception e) { e.printStackTrace(); } }).start(); }3. 对接门状态信号真实的售货柜里门的开关状态是通过门锁的 IO 信号给到安卓主板的你只需要在收到门锁的「开门信号」时调用captureBaseSnapshot()收到「关门信号」时调用captureResultSnapshotAndSettle()就能完成整个结算流程。新手测试的时候可以在界面上加两个按钮一个 “模拟开门”一个 “模拟关门”分别调用这两个函数就能测试整个结算逻辑。六、新手必踩的坑 优化方案1. 摄像头轻微偏移匹配失败怎么办用户开门的时候很容易碰歪柜子导致两次快照的角度有轻微偏移匹配率下降。优化方案在比对前先做全局的模板匹配对齐两次快照的货架区域再做商品匹配或者把 IOU 匹配阈值降到 0.25允许更大的位置偏移。2. 商品被用户移动了位置匹配错误怎么办比如用户把左边的可乐拿到了右边关门后比对会被判定为 “左边的可乐被拿走了右边新增了一瓶可乐”结算错误。优化方案增加类别全局校验同一个类别的商品匹配完成后如果拿取数量和放回数量一致就判定为用户移动了商品不计入结算。3. 叠放商品漏检导致结算错误怎么办商品叠放的时候YOLO 很容易漏检下面的商品导致基线快照数量不对。优化方案下一篇我们会讲的 YOLOByteTrack 多目标跟踪方案开门过程中跟踪商品的拿取动作补充快照比对的不足彻底解决叠放、遮挡的漏检问题。最后说两句到这里恭喜你你已经完成了无人售货柜的核心业务闭环从 YOLO 模型训练到 RK3576 部署再到自动结算整个商用流程已经全部跑通了。这套结算逻辑是我在多个商用售货柜项目里验证过的稳定、容错率高只要你的 YOLO 识别精度达标基本不会出现乱扣款、漏扣款的问题。下一篇我们就来解决这套方案的最后一个短板叠放、遮挡商品的漏检问题用 YOLOByteTrack 多目标跟踪实现开门过程中商品拿取动作的实时跟踪让你的结算准确率再上一个台阶哪怕是叠放的商品也能精准统计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2432401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…