C#调用Proficy Historian API实战:从查询到写入的完整Demo
简介针对GE Digital旗下Proficy Historian工业数据历史记录系统二次开发的C#示例项目面向需要掌握Historian API调用方法的工业信息化工程师与上位机开发者。通过ClientAccessAPI库及样例代码可快速上手数据采集、历史查询、报警事件处理与数据写入等核心接口并结合WPF界面展示查询结果。资源为rar压缩包共18个文件以C#源码8个cs、界面布局2个xaml、项目配置sln/csproj/config及API文档说明txt为主整体仅18KB结构精简。压缩包内ClientAccessAPI目录包含与Historian通信所需的dll库及示例工程开发者可对照学习初始化连接、执行查询等关键流程进而扩展到数据可视化、批量导入导出等定制功能。已有558人学习下载适合具备C#基础、希望快速入门Proficy Historian API的开发者参考实践。1. 用 C# 调 Proficy Historian API这套 Demo 让数据追溯少走三周弯路做工业数据追溯项目时我接手的第一个任务是去摸清生产线上某批产品的工艺参数是否超标。数据源是 Proficy Historian业务方要求出报表、做趋势、给 MES 留接口。当时手上没有现成代码文档又厚又散整整折腾了三周才把数据跑通。后来我把这套 C# proficy Historian API demo 整理成一个可复用的示例包把连接、查询、写入、导出这四个最常用的环节全部拆开每段代码都能独立跑。适合两类人刚接触 Historian 的工艺工程师以及要给追溯或报表系统接历史数据的开发。读完你至少能搭出一个能查数据、能写数据、能落地的 Demo 骨架。2. 开发前先搞懂 Historian 的数据模型三个概念直接影响 API 调用方式2.1 Tag 不是数据库字段理解采样方式才知道怎么查Proficy Historian 的核心数据单元是 Tag标签相当于一个带时间轴的数据通道。每个 Tag 对应一个采集点比如某条产线的反应釜温度、泵出口压力、电机电流。但 Tag 和关系数据库里的字段有个本质区别它自带采样属性和存储策略。我在实际项目中常用的 Tag 类型有三种模拟量Float64、数字量Boolean/Int32、字符串String。查询时要注意字符串 Tag 在 API 里单独有一套取值逻辑不能和模拟量混在一个查询条件下。还有一类是计算 TagCalculated Tag它本身不存储原始数据而是基于其他 Tag 实时计算这种 Tag 在查询时往往有额外的参数要求。除此之外还需要区分“原始采样数据”和“归档压缩数据”。Historian 默认会对采集到的数据做压缩存储如果直接用采样查询去拉数据拿到的可能是被压缩过的值。API 里对应两种查询模式原始查询Raw和采样查询Sampled。查报警和事故追责时用原始查询做趋势分析时用采样查询两者返回的记录密度差别很大。2.2 时间戳和质量码是查询结果里的两个隐藏字段每条 Historian 记录都带四个字段TagName、TimeStamp、Value、Quality。其中 Quality 是很多人第一次接触 API 时忽略的字段。它的取值范围包括 Good、Bad、Uncertain 等。设备断电、信号超量程、采集器重启都会让质量码变成非 Good 值。我在做批次追溯时吃过这个亏查询出来一批温度数据画到图表上才发现中间有一段毛刺。原因是那几分钟设备校准数据质量是 Bad但查询结果里没有过滤。请记住任何跨时间段的查询第一次跑通后先看结果里有没有非 Good 的记录做一个数据清洗逻辑。时间戳的精度是微秒级API 返回的时间是 UTC 还是本地时间取决于服务器的时区设置。这个我在后面踩坑章节里细说这里先记住一点查询时传时间范围要确认用的是服务器时间还是本地时间否则查出来的数据可能整体偏移几个小时。2.3 API 选型.NET SDK、REST API、OLE DB 怎么选Proficy Historian 对外提供了三种主流访问方式选型直接决定 Demo 的代码结构。我把它们的差异整理如下访问方式适用场景主要特点性能表现.NET SDK桌面应用、后台服务、批量处理类型安全、功能全、支持订阅和异步最高适合大量历史数据REST APIWeb 应用、跨平台系统HTTP 协议、JSON 格式、轻量易调试中等受网络开销影响OLE DB ProviderExcel 分析、临时查询、报表SQL 语法、无代码即可用一般适合中小体量如果只是做报表分析OLE DB Provider 配 Excel 就够了如果做 Web 平台优先考虑 REST API但本 Demo 用 .NET SDK 是合理的因为要演示写入和批量操作。SDK 方式可以控制消息集、批量提交、事务回滚这些在 REST API 里实现起来要绕弯。2.4 环境准备先确认 SDK 和授权再写第一行代码开发前按这个清单准备环境确认 Historian 服务器版本和 SDK 组件已安装检查当前用户有数据读取权限确认服务器连接端口开放准备一个可以用于测试的 Tag。// 引用核心程序集不同版本 SDK 程序集名略有差异以安装目录为准 using GE.Proficy.Historian; using GE.Proficy.Historian.Data;程序集一般位于 Historian 安装目录下的 SDK 文件夹。引用后用对象浏览器检查 GServer 类的构造函数能避免后续编译时找不到类型的问题。注意开发机和 Historian 服务器之间的网络延迟不能太高否则连接超时现象会很频繁。3. C# Demo 核心实现连接、查询、写入、导出一串跑通3.1 连接服务器一个连接对象管理整个会话连接是 Historian API 的第一步。SDK 里的 GServer 对象代表一个服务器会话连接成功后后续所有操作都用这个对象。这里给出最常用的连接写法。// 创建连接对象 GServer server new GServer(); // 方式一集成身份验证适用于域环境 server.Connect(historian-server-host); // 方式二显式账号密码验证适用于跨域或非域环境 // server.Connect(historian-server-host, domain\\user, password);用集成身份验证的前提是当前 Windows 用户有 Historian 的数据读取权限。显式账号密码方式会在字符串里暴露密码我一般配合配置文件加密使用。连接是一个有状态的操作用完要调用 server.Disconnect() 释放资源。实际开发中我习惯用 using 块或 try-finally 包裹连接与断开逻辑防止连接泄漏。3.2 查询采样数据拉最近 24 小时整点趋势查询数据是 Demo 的核心功能。下面这段代码查询某个 Tag 在过去 24 小时内每隔 1 小时的采样值返回结果可以直接画趋势图。// 创建数据连接 GDataConnection connection server.CreateDataConnection(); // 创建查询对象 GQuery query connection.CreateQuery(); // 指定要查询的 Tag query.AddTagNames(new string[] { Reactor_Temperature }); // 设置查询类型为采样查询采样周期 1 小时 query.QueryType GQueryType.GSampledQuery; query.SamplingInterval TimeSpan.FromHours(1); // 设置时间范围当前时间往前 24 小时 DateTime endTime DateTime.Now; DateTime startTime endTime.AddHours(-24); query.TimeRange new GTimeRange(startTime, endTime); // 执行查询并获取结果 GRecords records query.Execute();这里说三个参数细节。AddTagNames 支持一次传多个 Tag批量查询性能远好于循环单查。GSampledQuery 是插值采样查询适合画趋势如果是查报警事件改用 GRawQuery 拿原始数据。TimeRange 传入的时间要注意时区问题后面避坑章会给出具体原因。查询返回的 GRecords 对象内部是一个二维结构外层是 Tag内层是该 Tag 的所有记录点。读取时先按 Tag 维度遍历再取时间戳和数值。// 遍历查询结果 foreach (GRecord record in records) { string tagName record.TagName; DateTime timestamp record.TimeStamp; double value Convert.ToDouble(record.Value); string quality record.Quality.ToString(); Console.WriteLine(${tagName}, {timestamp}, {value}, {quality}); }遍历时注意 record.Value 的类型处理模拟量 Tag 时直接转 double 即可。字符串 Tag 用 record.Value.ToString() 获取。每一条记录都要取 Quality 字段做判断非 Good 的数据在统计前要剔除。3.3 写入历史数据把计算出的工艺参数回写到 Historian有些场景需要把分析结果或人工输入的数据写回 Historian。SDK 的写入逻辑用消息集MessageSet实现好处是支持批量提交和事务控制。// 创建消息集对象 GMessageSet messageSet connection.CreateMessageSet(); // 向消息集中添加数据点 messageSet.AddDataValue(Reactor_Temperature, DateTime.Now.AddMinutes(-5), 98.6, Quality.Good); messageSet.AddDataValue(Reactor_Temperature, DateTime.Now.AddMinutes(-4), 99.1, Quality.Good); // 批量提交到服务器 connection.OutputData(messageSet);这里提个建议写入的时间戳不要使用 DateTime.Now因为 Historian 对历史数据写入有一定的时间窗口限制某些版本不允许写入当前时间之后的数据。我一般写入过去几分钟内的数据或者从业务系统里拿到确定的工艺时间。消息集的机制类似于数据库事务先把多个数据点攒在缓冲区最后一次性提交。这样做比逐条写入效率高一个量级。如果写入的 Tag 不存在OutputData 时会报错或写入失败需要在写入前先用查询接口探测 Tag 是否存在。3.4 导出 CSV把查询结果落盘给下游做分析查询拿到数据后最常见的需求是导出成 CSV 给业务或算法团队用。这里给出一个简洁的导出代码片段注意加上质量码字段。// 导出查询结果为 CSV 文件 using (StreamWriter writer new StreamWriter(historian_export.csv)) { writer.WriteLine(TagName,TimeStamp,Value,Quality); foreach (GRecord record in records) { writer.WriteLine(string.Join(,, record.TagName, record.TimeStamp.ToString(yyyy-MM-dd HH:mm:ss.fff), record.Value, record.Quality.ToString())); } }时间戳格式化要精确到毫秒CSV 打开时才不会出现时间重复。如果数据量超过几十万行StreamWriter 默认缓冲够用数据量特别大时可以考虑用 CSV 库异步写入。这个导出逻辑加上前面 3.2 的查询逻辑就是一个完整的“查数据—落文件”工作流。4. 避坑记录连接超时、数据错位、权限不足的五个真实问题4.1 连接超时服务器没开门代码卡到天荒地老现象调用 Connect 后长时间无响应最后弹出超时异常。原因最常见的是防火墙未放行 Historian 服务端口其次是服务器名解析失败。部分环境里 Historian 服务依赖 Windows 服务管理器服务停了也会表现为连接超时。解决先用 telnet 验证端口连通性确认服务器上的 Historian 服务处于运行状态。如果使用主机名连接先测试 DNS 解析是否正常。我习惯在连接代码里设置较短的超时时间快速失败比长时间挂起容易定位问题。4.2 查询结果为空标签存在但一条记录都取不到现象Tag 名称确认无误用测试工具能看到历史数据但 API 查询返回空集合。原因大概率是查询类型与存储方式不匹配。比如用插值采样查询去查一个没有配置采样策略的 Tag或者时间范围刚好落在数据归档周期之外。解决先用原始查询验证该 Tag 在指定时间范围内确有数据再切换到采样查询。原始查询的代码只改一个 QueryType 属性query.QueryType GQueryType.GRawQuery;如果原始查询有数据而采样查询没数据检查 Tag 属性的存储间隔和可接受采样区间确需插值时就以原始数据为准自己在内存里算不依赖 Historian 的插值逻辑。4.3 数据整体偏移一小时时区问题把趋势图拉歪了现象查询得到的记录时间戳比实际工艺时间晚或早一小时且整个数据集偏移量一致。原因这是时区没有对齐的经典症状。Historian 服务器可能配置的是 UTC 时间而应用端用的是本地时间。代码里传入的 DateTime.Now 带本地时区偏移被 SDK 在传输层做了转换。解决统一约定时间基准。我一般把查询和写入的时间戳全部转成 UTC 后在应用层处理最后展示时再转回本地时间。代码里写一个工具方法DateTime serverTime DateTime.UtcNow; query.TimeRange new GTimeRange(serverTime.AddHours(-24), serverTime);这样能绕开大部分因时区设置产生的偏移问题。如果问题还在去看 Historian 服务器的时间区域配置把服务器的时区选项改到和业务系统一致。4.4 写入成功但查不到消息集没有真正落盘现象调用 OutputData 没有报错但重新查询时写入的数据不存在。原因消息集的事务没有提交或者写入的数据点质量码被服务器拒绝。还有一种可能是写入时间戳超出服务器允许写入的最大时间窗口。解决确认写入数据点的 Quality 是 Good 值非 Good 数据会被服务器丢弃。其次检查消息集的提交模式确认调用的是输出并提交的接口而不是只写入缓冲区。最后验证时间戳不要在未来时间轴写入过去 5 分钟内的数据点最稳妥。4.5 权限不足有数据源但 API 拒绝访问现象提示类似“Access Denied”或“Permission denied”的错误但用客户端工具可以正常查数据。原因Historian 的权限模型是分组的用户可能有界面查看权限但没有 SDK 的数据访问权限。企业环境中域用户的权限继承经常出问题。解决联系管理员确认当前账号属于哪个权限组申请对应数据操作权限。如果使用服务账号运行程序确保服务账号在 Historian 中也有显式授权不能因为它是域管理员就默认放行。5. 进阶用法把 Demo 改造成生产级数据服务5.1 查询逻辑改成异步任务界面不再卡死前面 3.2 的查询是同步阻塞式调用。在 WPF 或 WinForms 里跑几十秒的查询界面会直接冻结。我一般把查询包在 Task.Run 里配合 async-await 模式返回结果集。TaskGRecords queryTask Task.Run(() { using (GDataConnection asyncConn server.CreateDataConnection()) { GQuery asyncQuery asyncConn.CreateQuery(); // 参数与同步查询一致 return asyncQuery.Execute(); } }); GRecords result await queryTask;注意 GDataConnection 和 GQuery 都不是线程安全的不能在多线程间共享。Task.Run 内部必须重新创建连接对象用局部变量操作。异步化之后还要控制并发数量避免同时发起几十个查询把 Historian 服务器拖垮。一般我用信号量把并发度限制在 4 到 8 之间。5.2 批量写入的分批策略一万条数据一次提交会失败消息集虽然支持批量提交但单次提交的数据量不是无上限的。写入大量数据时我有一个习惯每 500 条分一批提交批与批之间留 100 毫秒间隔。// 分批写入示例 int batchSize 500; for (int i 0; i allValues.Count; i batchSize) { GMessageSet batchMessageSet connection.CreateMessageSet(); var batch allValues.Skip(i).Take(batchSize); foreach (var value in batch) { batchMessageSet.AddDataValue(value.TagName, value.TimeStamp, value.Value, Quality.Good); } connection.OutputData(batchMessageSet); Thread.Sleep(100); }分批策略能避免两个问题一个是消息集过大导致的内存溢出另一个是写入失败时回滚范围过大。每批数据独立提交失败时可以精确知道哪一批出了问题。这个批大小要根据服务器性能调整我测试过 200 到 1000 之间均有可用空间500 是稳定性较好的中间值。5.3 用插值查询对齐整点数据趋势曲线更干净原始数据的采样时间是不均匀的画趋势图时锯齿严重。除了 3.2 里的 GSampledQuerySDK 还提供了更细粒度的插值控制。设置采样间隔为 5 分钟配合历史插值模式可以得到一条平滑对齐的趋势线。query.QueryType GQueryType.GSampledQuery; query.SamplingInterval TimeSpan.FromMinutes(5); query.SamplingMode GSamplingMode.GLinearInterpolation;线性插值模式适合连续变化的模拟量比如温度、压力。离散量或开关量不适合线性插值要用阶梯式保持模式。这个细节对趋势图质量影响很大。我保存这段经验是因为有一次做批次分析用原始数据画的趋势曲线参差不齐客户要求按整点对齐。改用线性插值采样后图表效果立竿见影。5.4 验证手段用官方工具交叉核对查询结果无论查询还是写入代码跑通第一遍后我习惯用 Historian 自带的浏览器工具核对一批数据打开趋势图控件拉同一时间段同一 Tag目视对比 API 查出的数值是否落点一致。这个方法笨但能快速暴露时区偏移和数据压缩的问题。从那以后我每次写完历史数据的查询逻辑都强制走一遍“先看质量码、再对时间戳、最后对比官方工具”这三个步骤才敢把代码交给业务用。历史数据这东西出错不容易发现但一旦错了后续所有分析都是白做。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

mini-coding-agent源码解析(六):有界子Agent委派,只读受限的delegate工具设计思路

mini-coding-agent源码解析(六):有界子Agent委派,只读受限的delegate工具设计思路

【免费下载链接】mini-coding-agent Minimal and readable coding agent harness implementation in Python to explain the core components of coding agents. 项目地址: https://gitcode.com/gh_mirrors/mi/mini-coding-agent 点击查看 免费下载 在 mini-coding…

2026/10/14 19:51:59 阅读更多 →
Java多线程文件压缩实战:线程池与CountDownLatch性能优化

Java多线程文件压缩实战:线程池与CountDownLatch性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 19:51:59 阅读更多 →
基于STM32的空气质量监测系统:传感器选型、采集与校准实战

基于STM32的空气质量监测系统:传感器选型、采集与校准实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 19:51:59 阅读更多 →

最新新闻

Claude账号防封实战指南:从环境搭建到使用节奏的全面解析

Claude账号防封实战指南:从环境搭建到使用节奏的全面解析

1. 从“防封”这个词说起:账号稳定性的真实含义很多人第一次看到“防封”这两个字,脑子里浮现的画面是某种神秘操作或者隐藏开关。但如果你真正在长期使用各类AI工具,尤其是像Claude这种对账号行为比较敏感的服务,就会明白所谓的“…

2026/10/14 22:30:56 阅读更多 →
最简单的出入库系统怎么选?仓库管理软件选型避坑指南

最简单的出入库系统怎么选?仓库管理软件选型避坑指南

仓库管理软件哪个好用?这恐怕是我被问次数最多的问题,没有之一。每次听到这个问题,我都会先反问一句:你说的“好用”,到底是想要什么效果?聊下去之后,绝大多数人其实并不是想要一个功能多么齐全…

2026/10/14 22:30:56 阅读更多 →
Mac 源码编译 OpenClaw 实战:从依赖管理到避坑指南

Mac 源码编译 OpenClaw 实战:从依赖管理到避坑指南

简介:这份源码包面向需要在 Mac 上部署 OpenClaw 的开发者与运维人员,提供从环境准备到配置落地的完整参考。内容围绕 Homebrew、Node.js、Git 等基础依赖的确认,以及通过 brew 安装 OpenClaw CLI、配置本地大模型与网关端口、模型 ID 等关键…

2026/10/14 22:30:56 阅读更多 →
工业AI落地实践:从边缘计算到视觉质检与预测性维护

工业AI落地实践:从边缘计算到视觉质检与预测性维护

工业AI这四个字,这两年被喊得很响,但说实话,真正蹲过产线、在机台边吃过灰、把一个模型从实验室搬到车间里还让它稳定跑上三个月的人,并没有想象中那么多。我入这行快十年,前几年做的是传统机器视觉和PLC集成&#xff…

2026/10/14 22:30:56 阅读更多 →
DSP28335步进电机控制方案:ePWM脉冲输出与梯形加减速详解

DSP28335步进电机控制方案:ePWM脉冲输出与梯形加减速详解

简介:这是一套基于TI TMS320F28335 DSP与Xilinx XC3S500E FPGA协同控制步进电机的完整工程参考,适合电机控制学习者、嵌入式软硬件开发人员,以及开展课程设计或毕业设计的学生使用。整个压缩包共248个文件,大小约25.13MB&#xff…

2026/10/14 22:30:56 阅读更多 →
SQL注入本质:输入未过滤、拼接未隔离、权限未收敛

SQL注入本质:输入未过滤、拼接未隔离、权限未收敛

1. 这不是“黑进网站”的速成课,而是你真正该懂的SQL注入本质很多人看到“SQL注入”四个字,第一反应是黑客电影里飞速滚动的代码、被攻破的数据库、管理员惊恐的脸——这种刻板印象害了不少人。我带过不少刚入门的安全学习者,他们花大量时间背…

2026/10/14 22:29:55 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →