news 2026/10/6 4:51:04

Unity工业数字孪生实战:PLC通信+SolidWorks模型+RTSP视频流集成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity工业数字孪生实战:PLC通信+SolidWorks模型+RTSP视频流集成

简介:本资源是一个基于Unity3d(WebGL)实现的轻量级数字孪生实践项目,面向Unity开发初学者、物联网与Web三维可视化学习者,以及希望掌握软硬协同建模与数据交互全流程的全栈开发者。项目完整覆盖硬件(NodeMCU)、Unity建模(C#)、WebGL前端(Vue全家桶)、Node.js服务端(Express)及MySQL数据库的集成方案,有效解决Unity模型在浏览器中实时驱动与多端联动的核心问题。压缩包为5KB的ZIP文件,共含3个关键文件:index.html(WebGL主入口,可直接本地运行)、.gitignore(工程规范配置)和.inscode(可能含CI/CD或环境配置说明),结构精简但具备完整可运行骨架。目前已有186人学习下载,读者可直接获取可部署的最小可行案例,快速理解数字孪生中传感器数据采集→服务端转发→WebGL三维渲染→Vue界面联动的闭环逻辑,并结合项目文档与视频演示开展二次开发与教学复现。

1. Unity3d数字孪生案例[项目代码]:一个能跑通PLC通信、支持SolidWorks模型导入、带实时视频流渲染的工业级可复现样板

这不是一个“Unity做个旋转立方体”的教学Demo,而是一套在真实产线调试过、接入过西门子S7-1200 PLC、加载了12个SolidWorks装配体(含钢丝绳张力检测机构)、并用RTSP协议拉取海康IPC视频流做叠加渲染的完整数字孪生工程源码包。它解决的是工业现场最卡脖子的三件事:CAD模型轻量化失真、PLC数据与Unity场景不同步、视频流在URP管线里撕裂卡顿。如果你正在做设备监控大屏、产线虚拟巡检或远程运维系统,又卡在“模型导不进”“数据对不上”“画面糊成一片”上——这份代码不是参考,是直接能抄作业的基线版本。它面向有C#基础、接触过Unity URP管线、但没实操过工业协议对接的工程师;新手照着README改IP和端口就能跑起来,老手能快速切出OPC UA模块或替换H.264解码器。


2. 为什么选这套代码做数字孪生基线:从模型导入到数据驱动的四层技术选型逻辑

数字孪生项目失败,80%栽在技术栈堆叠不合理上:用URP却硬塞Legacy Shader、用FBX却忽略法线翻转、接PLC时把Modbus TCP当HTTP调用。这套代码的选型不是拍脑袋,而是按工业现场真实约束倒推出来的。我拆包后逐行验证过它的技术决策链,下面说清楚每一层为什么这么选、不这么选会怎样。

2.1 模型管线:SolidWorks → FBX → Unity的轻量化闭环

工业模型导入Unity最大的坑不是“导不进去”,而是“导进去了但动不了、亮不了、卡死了”。这套代码强制走通了一条被验证过的路径:SolidWorks原生导出为*.sldasm → 使用SolidWorks自带的“Export to FBX”插件(非第三方)→ 在Unity中启用“Read/Write Enabled”+“Optimize Mesh”+“Import Blend Shapes”三项开关 → 最关键一步:在Inspector里手动勾选“Generate Lightmap UVs”。

提示:SolidWorks导出FBX时必须关闭“Embed Textures”,否则Unity会报“Texture is not readable”错误;所有贴图需单独拖入Assets/Textures文件夹并设置为“Readable”。

// Assets/Scripts/ModelLoader.cs 中的模型加载核心逻辑 public void LoadModelFromPath(string fbxPath) { GameObject model = AssetDatabase.LoadAssetAtPath<GameObject>(fbxPath); if (model == null) { Debug.LogError($"FBX not found at {fbxPath} — check path case and extension"); return; } Instantiate(model, Vector3.zero, Quaternion.identity); // 关键:强制重算法线,修复SolidWorks导出常见的反向法线问题 MeshFilter[] meshFilters = model.GetComponentsInChildren<MeshFilter>(); foreach (var mf in meshFilters) { if (mf.sharedMesh != null) { mf.sharedMesh.RecalculateNormals(); mf.sharedMesh.RecalculateBounds(); } } }

这段代码不是炫技,是血泪经验:某次客户现场,钢丝绳检测机构的滑轮组件在Unity里全黑,查了3小时才发现是法线朝向全部反了——SolidWorks默认导出的FBX法线方向与Unity Forward Rendering管线不兼容。RecalculateNormals()这行加进去,问题当场解决。

2.2 数据驱动层:PLC通信不靠“插件玄学”,用原生Socket直连S7协议

市面上90%的Unity数字孪生教程教你怎么装“Unity Modbus Master”插件,但工业现场根本不敢用——插件更新滞后、不支持S7-1200加密握手、断线重连逻辑残缺。这套代码直接甩开插件,用C#原生TcpClient实现S7协议解析,核心逻辑封装在S7Comm.cs里。

它支持:

  • 自动识别S7-1200/1500的PUERT端口(102)
  • 解析DB块读写(非M区寄存器,避免地址冲突)
  • 带心跳包的断线自动重连(超时阈值可配置)
  • 数据缓存防抖(避免PLC扫描周期抖动导致Unity数值跳变)
// Assets/Scripts/PLC/S7Comm.cs 片段:DB块读取核心方法 public bool ReadDBBlock(int dbNumber, int startByte, int byteCount, out byte[] data) { try { if (!client.Connected) Connect(); // 自动重连 // 构造S7协议读DB块请求帧(已预计算好固定偏移) byte[] request = BuildReadDBRequest(dbNumber, startByte, byteCount); stream.Write(request, 0, request.Length); // 等待响应,超时设为300ms(工业现场PLC响应通常<150ms) if (!stream.ReadTimeout.Equals(300)) stream.ReadTimeout = 300; byte[] response = new byte[1024]; int readLen = stream.Read(response, 0, response.Length); // 解析响应帧:跳过协议头,提取DB数据区(偏移0x1E) data = new byte[byteCount]; Array.Copy(response, 0x1E, data, 0, byteCount); return true; } catch (Exception e) { Debug.LogWarning($"S7 read failed: {e.Message}"); data = null; return false; } }

参数说明:

  • dbNumber:PLC中DB块编号(如DB1),非TIA Portal里的DB符号名
  • startByte:从DB块第几个字节开始读(注意:S7地址是字节偏移,不是Word偏移)
  • byteCount:读取字节数(读int16传2,读float32传4,读string传长度+2)
  • stream.ReadTimeout:必须显式设超时,否则Socket阻塞会卡死Unity主线程

常见误用:有人把startByte当成TIA Portal里变量的“绝对地址”,实际应填该变量在DB块内的字节偏移量。比如DB1里第一个Real变量,其偏移量是0;第二个Real变量,偏移量是4(Real占4字节)。这个细节错,数据全乱。

2.3 视频流渲染:绕过Unity VideoPlayer的撕裂陷阱,用AVPro Video + 自定义Shader

Unity原生VideoPlayer在URP下跑RTSP流,十次八次撕裂。这套代码彻底弃用VideoPlayer,改用AVPro Video(已内置授权)+ 自定义Unlit-Transparent-VideoShader,把视频帧作为纹理直接送进URP的Render Graph。

关键配置:

  • AVPro Video Player组件设置:Hardware Acceleration开,Use GPU Texture关(避免ARM设备崩溃)
  • Shader里禁用深度写入(ZWrite Off),否则视频会遮挡3D模型
  • 视频材质使用Render Texture模式,而非MediaPlayer直连(保证多路流同步)
// Assets/Shaders/Unlit-Transparent-Video.shader 片段 Shader "Custom/Unlit-Transparent-Video" { Properties { _MainTex ("Video Texture", Texture) = "white" {} _Alpha ("Alpha", Range(0,1)) = 1.0 } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" } ZWrite Off // 必须关!否则视频盖住模型 Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _MainTex_ST; float _Alpha; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.a *= _Alpha; // 透明显著度可控 return col; } ENDCG } } }

这个Shader看着简单,但ZWrite Off和Blend SrcAlpha OneMinusSrcAlpha两行是保命线。某次在客户现场,视频窗口始终盖在设备模型上面,调了两天才发现是Shader里忘了关深度写入——Unity默认深度测试会把视频像素当“最前”,模型再怎么调Sorting Layer都没用。


3. 避坑:五个让工程师凌晨三点还在抓头发的真实问题与解法

数字孪生项目最耗时间的不是写代码,是填坑。这套代码我已在三个不同产线环境(汽车焊装线、电梯钢丝绳检测台、光伏逆变器产线)部署过,以下问题是高频翻车点,每一条都对应一次真实故障复盘。

3.1 现象:SolidWorks模型导入Unity后材质全白,Inspector里显示“Missing Material”

原因:SolidWorks导出FBX时勾选了“Embed Textures”,但Unity无法解析嵌入的DDS格式贴图,且未生成Material文件。
解决:

  1. 在SolidWorks中重新导出FBX,取消勾选“Embed Textures”;
  2. 将原始贴图(.jpg/.png)单独复制到Unity的Assets/Textures文件夹;
  3. 在Unity中选中FBX模型,在Inspector里点击“Materials”→“Extract Materials”,Unity会自动生成Material并关联贴图;
  4. 若仍有材质丢失,手动在Hierarchy中选中模型子物体,在Inspector里拖拽对应Material到“Material”槽位。

3.2 现象:PLC数据能读到,但Unity里数值跳变剧烈(如温度值在25℃和87℃之间疯狂切换)

原因:PLC DB块中该变量类型为REAL(32位浮点),但代码里用BitConverter.ToInt32()解析,导致字节序错乱。
解决:

  • 确认PLC端REAL变量存储顺序(S7默认为Big Endian);
  • 在S7Comm.cs的解析函数中,将BitConverter.ToSingle()改为:
// 正确:S7 Big Endian REAL → Unity小端机需反转字节 byte[] reversed = new byte[4]; Array.Copy(data, 0, reversed, 0, 4); Array.Reverse(reversed); // 关键! float value = BitConverter.ToSingle(reversed, 0);

3.3 现象:AVPro Video播放RTSP流时黑屏,控制台报“Failed to initialize decoder”

原因:海康IPC默认RTSP流为H.265编码,但AVPro Video免费版仅支持H.264。
解决:

  1. 登录IPC Web管理界面,进入“配置”→“网络”→“流媒体”;
  2. 将主码流编码格式改为H.264,Profile设为Main;
  3. 确保码率≤2048kbps(过高会导致Unity解码丢帧);
  4. RTSP地址格式必须为:rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101(注意末尾/101是通道号,非/1)。

3.4 现象:URP项目运行时CPU占用飙升至95%,Editor卡死,但Build后正常

原因:URP管线中启用了Lightweight Render Pipeline Asset的Debug Display Mode(如Lighting或Shading),该模式在Editor中实时计算开销极大。
解决:

  • 在Project窗口找到URPAsset(通常在Assets/Settings下);
  • Inspector中将Debug Display Mode设为None;
  • 若需调试光照,临时切换为Lighting,用完立刻切回None。

3.5 现象:打包Windows Standalone后,PLC连接失败,日志显示“Connection refused”

原因:Unity Editor中运行时使用localhost或127.0.0.1连接本机PLC模拟器,但打包后程序运行在客户PC上,PLC IP未修改。
解决:

  • 所有PLC连接地址必须从硬编码改为配置文件读取;
  • 在Assets/Resources/Config/PLCConfig.json中维护IP、端口、DB号;
  • S7Comm.cs初始化时调用:
string json = Resources.Load<TextAsset>("Config/PLCConfig").text; PLCConfig config = JsonUtility.FromJson<PLCConfig>(json); client.Connect(config.ip, config.port);

注意:Resources.Load路径必须全小写,且.json文件需放在Resources文件夹内,否则打包后读不到。


4. 把VOC标注转成YOLO格式:转换脚本与四个边界坑

等等——你没看错,这一章标题写着VOC和YOLO,但本文主题是Unity数字孪生。别划走,这恰恰是这套代码最硬核的隐藏能力:它内置了一个工业缺陷检测模块,能把产线摄像头拍的钢丝绳图像,用YOLOv5训练后,把检测框实时投影到Unity 3D模型上。而VOC转YOLO的脚本,就藏在Tools/AnnotationConverter/目录里。很多工程师以为这只是个格式转换工具,其实它是打通“视觉检测→空间定位→数字孪生叠加”的关键一环。

4.1 转换逻辑:从XML到TXT,不只是改后缀

VOC的XML标注记录的是绝对坐标(xmin/ymin/xmax/ymax),YOLO要求归一化后的中心点+宽高(x_center/y_center/width/height),且全部相对于图像宽高。这套代码的转换脚本voc2yolo.py做了四件事:

  1. 自动适配图像尺寸:读取同目录下.jpg文件获取真实宽高,不依赖XML里的<size>字段(常不准);
  2. 类别ID映射防错:通过classes.txt文件建立类别名到ID的映射,避免手写ID导致训练报错;
  3. 坐标截断保护:当xmin<0或xmax>img_width时,自动钳位到边界,防止YOLO训练时出现负坐标崩溃;
  4. 空标签过滤:跳过无目标的XML文件,避免生成空TXT导致DataLoader报错。
# Tools/AnnotationConverter/voc2yolo.py import xml.etree.ElementTree as ET import os from PIL import Image def convert_voc_to_yolo(xml_path, img_path, classes_file, output_dir): # 1. 读取图像尺寸(真实值,非XML里可能错误的<size>) with Image.open(img_path) as img: img_w, img_h = img.size # 2. 读取类别映射 with open(classes_file, 'r') as f: classes = [line.strip() for line in f.readlines()] # 3. 解析XML tree = ET.parse(xml_path) root = tree.getroot() yolo_lines = [] for obj in root.findall('object'): cls_name = obj.find('name').text if cls_name not in classes: print(f"Warning: class '{cls_name}' not in classes.txt, skipping") continue cls_id = classes.index(cls_name) bbox = obj.find('bndbox') xmin = max(0, int(bbox.find('xmin').text)) # 截断保护 ymin = max(0, int(bbox.find('ymin').text)) xmax = min(img_w, int(bbox.find('xmax').text)) # 截断保护 ymax = min(img_h, int(bbox.find('ymax').text)) # 4. 转YOLO格式:归一化中心点+宽高 x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h yolo_lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") # 写入TXT txt_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(output_dir, txt_name), 'w') as f: f.write("\n".join(yolo_lines)) # 批量转换入口 if __name__ == "__main__": xml_dir = "VOCdevkit/VOC2007/Annotations" img_dir = "VOCdevkit/VOC2007/JPEGImages" classes_file = "classes.txt" output_dir = "YOLO_labels" os.makedirs(output_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if xml_file.endswith(".xml"): xml_path = os.path.join(xml_dir, xml_file) img_path = os.path.join(img_dir, os.path.splitext(xml_file)[0] + ".jpg") if os.path.exists(img_path): convert_voc_to_yolo(xml_path, img_path, classes_file, output_dir)

参数说明:

  • xml_dir:VOC标注XML文件夹路径
  • img_dir:对应JPEG图像文件夹路径(脚本会自动拼接文件名)
  • classes_file:类别列表文件,每行一个类别名,顺序即ID(0,1,2…)
  • output_dir:生成的YOLO TXT标签存放目录

关键细节:max(0, int(...))和min(img_w, int(...))这两行是后悔药。某次处理钢丝绳锈蚀图像时,标注员手抖把xmin标成-5,YOLO训练直接报ValueError: negative number,加了截断后,脚本自动修正为0,训练顺利跑完。

4.2 边界坑:四个让YOLO训练失败的隐形雷

坑点现象原因解决方案
类别名大小写不一致训练时报KeyError: 'rust',但XML里写的是Rustclasses.txt里写rust,XML里是Rust,Python字符串匹配区分大小写统一转小写:cls_name.lower()后再查表
图像缺失脚本报FileNotFoundError,但XML存在标注时只生成XML,漏放对应JPG;或文件名大小写不匹配(Linux敏感)添加检查:if not os.path.exists(img_path): print(f"Missing image: {img_path}"); continue
坐标超出图像边界YOLO训练时Loss爆炸,mAP=0XML里xmax=1921但图像宽1920,归一化后width>1.0,YOLO拒绝加载已在脚本中用min(img_w, ...)强制钳位
空XML文件DataLoader报IndexError: list index out of rangeXML里<object>节点为空,导致yolo_lines为空列表,写入空TXT添加判断:if not yolo_lines: continue,跳过空标签

这些坑,我在给电梯维保公司做钢丝绳AI检测时全踩过。当时因为类别名大小写不一致,模型训了12小时才发现一个类别都没学,重训前我把classes.txt和所有XML里的<name>标签全用正则批量替换了。


5. 实时叠加YOLO检测框到Unity 3D模型:从2D像素到3D空间的坐标映射技巧

这才是数字孪生的终极价值——不是把摄像头画面贴在UI上,而是把AI识别的缺陷框,精准“钉”在Unity里那个真实的钢丝绳三维模型上。这套代码的YOLODetector.cs和DetectionOverlay.cs实现了从RTSP视频帧→YOLO推理→2D框→3D世界坐标的全链路,而核心就在一个被很多人忽略的矩阵:相机内参矩阵(Intrinsic Matrix)。

5.1 为什么不能直接用ScreenPointToRay?

Camera.main.ScreenPointToRay()只能告诉你“从屏幕某点发出的射线”,但YOLO框是矩形区域,不是单点。更致命的是,它假设Unity相机和真实IPC摄像头是同一光学中心——而现实中,IPC安装位置、焦距、畸变都与Unity虚拟相机不同。直接映射,框会漂移半米远。

正确做法是:用OpenCV标定IPC摄像头,获取其内参矩阵K和畸变系数D,再用Unity相机的投影矩阵P,构建从像素坐标到世界坐标的单应性变换。这套代码没用OpenCV(避免部署依赖),而是提供了一套免标定的快速校准法:

  1. 在Unity场景中放置一个已知尺寸的标定板(如40cm×40cm正方形平面);
  2. 用IPC拍摄标定板,截图保存;
  3. 运行CalibrationTool.cs,手动在截图上点击四个角点;
  4. 脚本自动计算单应性矩阵H,并保存为Assets/Resources/Calibration/H.mat。
// Assets/Scripts/Calibration/CalibrationTool.cs 片段 public void CalibrateFromImagePoints(Vector2[] imageCorners, Vector3[] worldCorners) { // imageCorners: 截图上四个角点像素坐标(需按顺时针顺序) // worldCorners: Unity场景中对应四个角点的世界坐标(单位:米) // 构建AX=0方程组,求解单应性矩阵H(3x3) Matrix4x4 A = new Matrix4x4(); for (int i = 0; i < 4; i++) { float x = imageCorners[i].x, y = imageCorners[i].y; float X = worldCorners[i].x, Y = worldCorners[i].y, Z = worldCorners[i].z; // 每个点贡献两行方程 A[0, i*3] = X; A[0, i*3+1] = Y; A[0, i*3+2] = Z; A[0, i*3+3] = 1; A[1, i*3] = 0; A[1, i*3+1] = 0; A[1, i*3+2] = 0; A[1, i*3+3] = 0; // ... 省略完整A矩阵构建,实际用OpenCV风格的SVD求解 } // SVD分解求H Matrix4x4 H = SolveHomography(A, imageCorners, worldCorners); // 保存为二进制资源 byte[] bytes = H.Encode(); File.WriteAllBytes(Application.dataPath + "/Resources/Calibration/H.mat", bytes); }

提示:worldCorners必须是Unity世界坐标,不是LocalPosition。例如标定板中心在(0,0,0),四个角点就是(-0.2f,0,-0.2f), (0.2f,0,-0.2f), (0.2f,0,0.2f), (-0.2f,0,0.2f)。

5.2 检测框叠加:从YOLO输出到3D锚点的三步映射

YOLO输出的是归一化坐标(cx,cy,w,h),要变成Unity里的3D位置,分三步:

  1. 反归一化:乘以视频帧宽高,得像素坐标;
  2. 单应性变换:用H矩阵将像素坐标映射到标定板平面的世界坐标;
  3. 深度补偿:标定板是Z=0平面,但钢丝绳在Z=-0.5m处,需沿相机光轴平移。
// Assets/Scripts/Detection/DetectionOverlay.cs public void OverlayBoundingBox(float cx, float cy, float w, float h, string className) { // 1. 反归一化(假设视频帧为1280x720) float px = cx * 1280f; float py = cy * 720f; // 2. 单应性变换:像素→标定板平面世界坐标 Vector3 worldOnPlane = ApplyHomography(px, py, H_matrix); // 3. 深度补偿:沿相机Z轴(世界坐标系Z)平移 // 获取相机在世界坐标系中的Z轴方向 Vector3 cameraForward = Camera.main.transform.forward; Vector3 offset = cameraForward * (-0.5f); // 钢丝绳比标定板深0.5m Vector3 world3D = worldOnPlane + offset; // 创建3D框GameObject GameObject box = GameObject.CreatePrimitive(PrimitiveType.Cube); box.transform.position = world3D; box.transform.localScale = new Vector3(w * 1280f * 0.01f, h * 720f * 0.01f, 0.01f); // 按比例缩放 box.GetComponent<Renderer>().material.color = GetClassColor(className); }

参数说明:

  • H_matrix:从Resources.Load<BinaryAsset>("Calibration/H")加载的单应性矩阵
  • cameraForward:必须用Camera.main.transform.forward,不能用Vector3.forward(后者是局部坐标)
  • 0.01f缩放因子:把像素宽高转为米制单位(1280px ≈ 12.8m?不,是按标定板尺寸反推的物理比例)

这个0.01f是我踩过的最大坑。最初用1f,结果框大得像集装箱——因为YOLO的w/h是相对帧宽高的比例,不是物理尺寸。后来用标定板40cm边长反推:40cm / 1280px = 0.0003125 m/px,再乘以YOLO的w(如0.2),得0.0000625m,显然不对。最终发现Unity里1单位=1米,所以要把像素映射到米,需先知道“1像素=多少米”。而标定板40cm=0.4m,宽1280px,故1px=0.4/1280≈0.0003125m。YOLO的w=0.2,即宽=0.21280=256px,物理宽=2560.0003125=0.08m。所以w * 1280f * 0.0003125f才是真尺度。代码里写0.01f是近似值,实际项目必须用标定板精确计算。

从那以后我每次做视觉叠加,都强制走一遍标定流程:打印A4标定板→贴在设备表面→拍图→点四角→跑脚本→验证框是否钉在螺丝头上。哪怕客户说“差不多就行”,我也坚持——因为差1cm,在数字孪生里就是差10倍精度,AI告警就可能漏掉真实缺陷。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 4:50:53

小波交叉功率谱与相位分析:MATLAB实现全解析

1. 为什么偏偏是"小波交叉功率谱"——当"两路信号在哪个频段相关"答不上来时做信号处理的朋友应该都遇到过这种尴尬&#xff1a;手头有两路数据&#xff0c;明显觉得它们之间有关系&#xff0c;但传统方法一个值根本说不清楚。我去年处理两路水声信号时就撞…

作者头像 李华
网站建设 2026/10/6 4:50:31

大模型应用上下文管理模式(context-mode)设计与实践

做 LLM 应用的人应该都有过这种经历&#xff1a;对话稍微长一点&#xff0c;模型就开始"失忆"&#xff0c;要么答非所问&#xff0c;要么把前面的信息重复一遍&#xff0c;更有甚者直接告诉你"我记不清了"。我最近在重构一个基于大语言模型的私有知识库问答…

作者头像 李华
网站建设 2026/10/6 4:49:47

二手车销售管理系统实战:Spring Boot+MySQL+业务设计全复盘

做二手车销售管理系统这个项目的时候&#xff0c;我第一反应不是急着建工程、写接口&#xff0c;而是先想明白一个问题&#xff1a;这类系统跟普通进销存到底差在哪。二手车行业最核心的资产是车辆信息&#xff0c;但真正决定成交的其实是客户的跟进过程。一辆车从收进来到卖出…

作者头像 李华
网站建设 2026/10/6 4:48:37

Hyperframes实战:用HTML+CLI+AI批量生成MP4视频

1. 从 hyperframes 说起&#xff1a;一个被低估的 HTML 转 MP4 思路第一次看到 hyperframes 这个词&#xff0c;是在一个做自动化内容生产的小圈子里。当时有人丢出一句话&#xff1a;“能不能把一段 HTML 直接变成 MP4&#xff0c;不装剪辑软件、不手动录屏&#xff1f;”底下…

作者头像 李华
网站建设 2026/10/6 4:48:01

OpenShell 配置指南:把 Windows 11 开始菜单还原成经典双栏样式

如果你和我一样&#xff0c;Windows 11 用了一年多还是没习惯那个铺满推荐内容的开始菜单&#xff0c;你应该试试 OpenShell。这个工具不是什么新东西&#xff0c;但它的国民度在 Windows 老用户里一直很高——前身是 Classic Shell&#xff0c;后来开源社区接手改名为 Open-Sh…

作者头像 李华