VB编写OPC UA客户端:从连接到订阅的完整实战指南

简介:这是面向工业自动化开发者的OPC UA客户端示例,基于Visual Basic(VB)编写,适合正在学习OPC UA通信协议,或需要在VB环境中快速实现数据采集与读写功能的工程师借鉴参考。压缩包大小15.26MB,共181个文件,以122个DLL类库为主,另含VB项目源码、XML配置、EXE可执行程序、PDB调试符号及少量工程缓存文件,其中TestOpcUaVb项目可直接打开调试。资源已有481人学习浏览。代码完整覆盖OPC UA客户端的核心交互流程,包括连接与安全认证、信息模型节点浏览、数据订阅通知、数值读写和异常处理等关键模块;配合配套的OPC Helper 2.02工具,可快速连接真实OPC服务器进行读写验证,理解UA客户端原理。通过研究项目源码,还能掌握VB环境下创建会话、构建订阅、订阅释放等SDK调用方式,为工业系统集成提供可复用的实践模板。

1. 这份VB写的OPC UA客户端,能让你少走三个月弯路

去年在车间调试一台老设备的联网采集,上位机是VB6改过来的,新设备却只提供OPC UA接口。网上搜了一圈,C#的示例倒是不少,VB的却少得可怜。翻遍几个工控群,最后找到这份用VB写的客户端工程,才把数据采回来。它不是完整的商业软件,只是一个能连接、能读点、能订阅的客户端骨架,代码风格直白,没有多余的封装,非常适合用来学习OPC UA客户端的开发思路。如果你也在做设备数据采集、MES对接、车间看板,又没有现成的C#同事帮你写底层,这份资源值得解压后对照着敲一遍。

2. 先看懂OPC UA客户端:通信模型、核心对象和VB选型

2.1 客户端面对的不再是DA的Item,而是Endpoint和Node

OPC UA的通信模型比传统OPC DA(那套COM/DCOM的冒号反斜杠)清晰得多。在客户端这边,你不再需要逐个“组态”变量,而是先和服务端建立一条安全通道,再通过节点ID直接操作数据。整个体系里,你只需要盯住几个核心对象:EndpointDescription描述的是服务端暴露的通信地址和安全策略;ApplicationConfiguration描述的是客户端自己的身份、证书和超时参数;Session是双方建立的一条逻辑会话,所有读写都发生在这条会话上;而Node是服务端地址空间里的数据节点,变量、参数、报警都抽象成Node,每个Node有一个唯一的NodeId。

我一开始忽略了Node和旧的Item的区别,总想把变量当成独立的东西去配置,后来发现OPC UA更方便:地址空间本身就是一棵树,你想读什么,先通过NodeId定位那个节点,再在会话上操作它就行。选型上,VB环境要用UA库,最常见的选择是直接用官方发布的Ua .NET Standard库,它在VB.NET里可以直接引用,证书验证、会话管理、订阅回调都封装好了,适合把精力放在业务逻辑上。如果你的项目还在用VB6,那就只能走OPC DA兼容层或者另外写COM封装,代价大得多,我建议新项目直接上VB.NET加这个库。

顺带说一个容易让人绕晕的点:OPC UA的地址空间是有层级结构的,根节点下面有ObjectsFolder,之后才是设备节点和数据变量。你用浏览工具看到的是一个树,而不是旧OPC DA里那种扁平的Group和Item列表。这个变化对代码的影响在于,你不用维护一个“项集合”,只要记住几个关键NodeId就能随时访问。真正费时间的反而是如何从服务端那棵大树上精准找到你要的变量,这个我会在联调章节里专门讲浏览代码。

2.2 用VB引用UA .NET库:项目设置里的三个决定项

先讲如何创建工程:新建一个VB项目,通过包管理器安装UaClient相关的包,然后在代码里引用命名空间。这里有三项设置会影响后面所有代码:

第一,目标框架。我一般会选.NET Framework 4.7.2或更高,兼容性好,服务端和工具集都不容易出幺蛾子。选到.NET Core也可以,但要注意某些第三方模拟器只支持.NET Framework,会导致联调时来回折腾。第二,平台目标。如果你的项目默认是AnyCPU,在连接本地服务端时没问题,但一旦用到证书和某些本地库,偶尔会报BadImageFormatException。常见做法是把“平台目标”固定为x64,尤其是Windows 10以后的系统,对应服务端多为64位进程。第三,应用配置里ApplicationName和ApplicationUri。服务端在证书验证阶段会查看客户端证书的应用URI,如果证书和配置不一致,会直接拒绝连接。学习阶段可以先关闭证书验证,但你要知道这个坑存在。

下面是一个最小配置的代码示例,放在Form的构造函数或Module启动段里就能用:

' 创建客户端ApplicationConfiguration,注意证书路径可以暂时留空 Dim config As New ApplicationConfiguration() config.ApplicationName = "VB OPC UA Client" config.ApplicationUri = "urn:vb:opcuaclient" config.ApplicationType = ApplicationType.Client ' 学习阶段先自动接受未信任证书,生产环境必须改为False config.SecurityConfiguration = New SecurityConfiguration() config.SecurityConfiguration.AutoAcceptUntrustedCertificates = True ' 输出日志到控制台,方便排查连接问题 config.SetTraceMask(Utils.TraceMasks.StackTrace)

这段配置里的ApplicationName要和你后面创建的证书匹配,否则服务端会回一个BadCertificateInvalid。ApplicationUri是全局唯一标识,建议用urn:开头的固定字符串,不要每次启动都随机生成,否则服务端会认为你在反复换身份。AutoAcceptUntrustedCertificates这个开关是学习时偷懒用的,放在车间现场必须关掉,不然后面证书问题会被人盯上。

还有一点容易忽略:Ua .NET Standard库的大部分API是异步的,VB里要用Async/Await或者把返回的Task直接阻塞等待。我见过不少同事直接写Session.Create(...).Result,结果在UI线程上死锁。后面代码示例里我用Async/Await,如果你习惯同步写法,要留意上下文。这里不展开线程问题,避坑章会专门说。

有了这些前置知识,你就能理解为什么很多示例代码里那些配置项看着像“玄学”了,其实每一项都有对应检查逻辑。接下来我按实际使用顺序,把连接、读点、订阅三个功能拆开讲。

3. 把客户端拆开:连接、读点、订阅三步走

3.1 建立连接:SelectEndpoint和Session.Create的配合

连接这一步做的事情比表面上多得多:先要通过服务URL拿到服务端暴露的Endpoint列表,再根据安全策略选一个能用的,最后带上客户端配置去创建Session。代码写起来是三步,但每步都对应着一次网络握手。下面是最小可用的连接方法:

Imports Opc.Ua Imports Opc.Ua.Client Private Async Function ConnectAsync(serverUrl As String) As Task(Of Session) ' 第一步:从服务端发现Endpoint,学习阶段先用不安全连接 Dim endpoint As EndpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, useSecurity:=False, discoverTimeout:=10000) ' 第二步:创建客户端配置对象,应用名需要唯一 Dim config As New ApplicationConfiguration() config.ApplicationName = "VBOpcClient" config.ApplicationUri = "urn:vb:opc:client" config.SecurityConfiguration = New SecurityConfiguration() config.SecurityConfiguration.AutoAcceptUntrustedCertificates = True ' 第三步:创建Session,超时设为60秒,匿名UserIdentity Dim session As Session = Await Session.Create(config, endpoint, True, "SessionName", 60000, New UserIdentity(), Nothing) Return session End Function

SelectEndpoint返回服务端的可用端点,useSecurity:=False的作用是跳过安全策略协商,先拿到地址。第二步的配置对象复用了之前的config,重点是ApplicationUri和AutoAccept。Session.Create看起来参数多,但前两个是配置和端点,第三个参数True表示需要创建一个Session实例,第四个是会话名称,最后两个分别是用户身份和订阅集合占位。如果你在UI线程调用,记得用Await而不是.Result,否则容易死锁。

参数说明:serverUrl必须写完整,例如opc.tcp://192.168.1.10:4840或带路径的opc.tcp://192.168.1.10:4840/servicename。有些服务端省略路径也能连,但正规写法是加上服务名。discoverTimeout是毫秒,太短在大型服务器上会失败,我习惯设10秒。60000是sessionTimeout,指服务端认为会话有效的毫秒数,超过这个时间没有交互,服务端会回收会话,客户端需要重连。

不建议一上来就用不安全连接,就算服务端允许匿名,你也至少把SecurityPolicy设成Basic256Sha256。学习阶段用不安全连接没问题,但接到生产服务器时要改回安全模式,否则数据明文传输,车间核心参数等于裸奔。

3.2 读节点:NodeId的格式和DataValue的结构

读一个节点的值,核心就两句话:把字符串转成NodeId,再通过Session读DataValue。代码里我尽量把逻辑抽成函数,方便复用:

' 读取单个节点的示例,nodeIdStr形如:"ns=2;s=Machine/Temperature" Public Function ReadNodeValue(session As Session, nodeIdStr As String) As DataValue Dim nodeId As NodeId = NodeId.Parse(nodeIdStr) Return session.ReadValue(nodeId) End Function

调用方式:

Dim dv As DataValue = ReadNodeValue(session, "ns=2;s=Machine/Temperature") Console.WriteLine("Value={0}, Timestamp={1}", dv.Value, dv.SourceTimestamp)

NodeId.Parse会自动识别ns=2;s=Tag1形式,其中ns是命名空间索引,s表示字符串标识。后面跟的是服务端地址空间里真正的标识符,而不是你在客户端自定义的名字。ReadValue返回DataValue,里面除了Value,还有SourceTimestamp和StatusCode。StatusCode等于Good才能用,否则读到的是错误码,不是实时值。

参数说明:如果返回BadNodeIdUnknown,说明NodeId在服务端不存在,或者命名空间索引不对,这个坑在避坑章还要展开。读数组或者结构体也没问题,DataValue.Value会是数组或对象,你可以直接强转成对应的类型。如果节点是方法,要用CallMethod,不能读。

3.3 订阅数据变化:MonitoredItem和Subscription的配合

轮询在很多场景下能用,但变量一多就浪费带宽。OPC UA的订阅机制是把变量挂到一个Subscription上,服务端按设定的周期推送变化,模型比轮询优雅得多。代码也不复杂:

' 订阅示例:把节点挂到订阅上,回调在数据变化时触发 Public Sub Subscribe(session As Session, nodeIdStr As String) Dim subscription As New Subscription() subscription.PublishingInterval = 500 ' 发布间隔,500ms subscription.LifetimeCount = 100 ' 生命周期计数,定时清理 subscription.MaxKeepAliveCount = 10 ' 心跳数量 Dim monitorItem As New MonitoredItem() monitorItem.StartNodeId = NodeId.Parse(nodeIdStr) monitorItem.SamplingInterval = -1 ' -1表示用服务端默认采样间隔 monitorItem.QueueSize = 1 AddHandler monitorItem.Notification, AddressOf OnDataChange subscription.AddItem(monitorItem) session.AddSubscription(subscription) subscription.Create() End Sub Private Sub OnDataChange(monitoredItem As MonitoredItem, args As MonitoredItemNotificationEventArgs) Dim value As DataValue = monitoredItem.LastValue Console.WriteLine("节点 {0} 新值: {1}", monitoredItem.StartNodeId, value.Value) End Sub

New Subscription创建一个订阅,AddItem把监控项加进去,最后必须调用subscription.Create(),订阅才会被服务端接受,这一步漏了是最常见的坑。回调OnDataChange里取monitoredItem.LastValue,是订阅发生后服务端推送的最新值。

参数说明:PublishingInterval是指服务端按这个时间间隔向客户端推送数据;SamplingInterval是指服务端对底层信号采样的间隔,设为-1时使用服务端默认值。这两者不是一回事,后者不能大于前者,否则客户端看到的数据会跳跃。QueueSize设为1只保留最新值,如果设成0则无限缓冲,回调会积压。LifetimeCount和MaxKeepAliveCount是订阅生命周期参数,和学习阶段关系不大,保持默认即可。

在真实项目里,用订阅代替轮询能显著减少网络开销,尤其是变量多的时候。但要注意,如果服务端是那种只支持上报而不支持订阅的老版本,还是得退回到轮询。

4. 排查与避坑:连接、读点、订阅中的四个翻车点

4.1 连接失败:证书信任和URL格式是两大来源

现象:程序抛BadSecurityModeRejected,或者连接超时,日志停留在“Discover servers”阶段。

原因:80%是证书信任问题。服务端只信任它已知的客户端证书,而你本地还没有生成有效的应用证书,甚至证书压根没创建。剩下20%是URL格式错误,比如写成http://IP:Port,或者在端口后漏了服务名。OPC UA的标准地址是opc.tcp://开头,很多服务端在根目录也会有/UA/之类的路径,写错一个斜杠就找不到Endpoint。

解决:学习阶段把AutoAcceptUntrustedCertificates设为True,能直接跳过证书校验;生产环境则要生成客户端证书,并把它手动添加到服务端的信任列表中。URL的话,不要自己猜,用服务端提供的说明或管理工具导出的地址。我通常在代码里输出endpoint.EndpointUrl看看实际选中的是什么,能省不少时间。

4.2 读不到数据:NodeId的命名空间索引不对

现象:连接成功,但读取时返回BadNodeIdUnknown,或者读到的数值明显不是你要的变量。

原因:服务端的地址空间有多个命名空间,每个命名空间维护自己的节点集。ns=0是UA标准节点,业务变量一般在ns=2、ns=3甚至ns=8。你如果沿用别的项目的NodeId,或者自己在客户端里自定义了一个,自然找不到。另一个隐藏问题是标识符类型:有些变量用字符串标识,NodeId写法是ns=2;s=Tag1;有些用数字标识,写法是ns=2;i=1001;类型不对也会报节点不存在。

解决:用浏览地址空间的方法拿到真实NodeId。我一般会在对接前先打印一次服务端所有节点的NodeId和路径,再把这些值写进配置文件。不要手敲长字符串,很容易眼花。

4.3 订阅不触发:PublishingInterval和SamplingInterval的关系反了

现象:订阅创建成功,也没有异常,但回调一次都不执行,或者触发频率和设置完全不搭。

原因:这是典型的参数配合问题。订阅机制里,服务端按SamplingInterval采集信号,再按PublishingInterval打包发送给客户端。如果你把PublishingInterval设成10ms,而服务端采样间隔是500ms,客户端收到数据不会超过每秒2次。反过来,如果PublishingInterval设成5000ms,而采样间隔只有100ms,那100ms内多次变化会被合并成一次推送。还有一个常见失误:只调了AddSubscription而没有调subscription.Create(),服务端根本没收到订阅请求。

解决:把两个间隔先设成一致(比如都1000ms),跑通了再按性能需求调。同时确认在创建完订阅的那行代码后面没有抛异常,最好打印订阅的SequenceNumber来验证。

4.4 中文乱码:UA字符串编码与客户端默认编码不一致

现象:变量读到的是中文,但控制台或日志显示成“烫烫烫”或者一堆问号。

原因:OPC UA规范里字符串用UTF-8编码,而Windows下VB控制台的默认输出编码经常是GBK或ASCII。数据本身没有坏,是输出环节把它按错误的代码页解释了。如果写入时也乱码,那就更麻烦,说明你在写入前没有把字符串转成UTF-8字节。

解决:在读取端设置控制台编码为UTF-8,或者读取后用Encoding.UTF8.GetString处理字节数组。写入端则用System.Text.Encoding.UTF8.GetBytes把字符串转成符合UA规范的字节流。不要用Convert.ToString这种侥幸做法,你看到的现象会变成“有时候对,有时候错”,非常磨人。

5. 联调与改造:让客户端从“能跑”变成“能干”

5.1 先搭一个可用的模拟服务端,把客户端跑通

接入真实PLC之前,我强烈建议先在本地搭一个模拟服务端。常见做法是找一个带界面的UA模拟器,启动后它会自动建立一个地址空间,里面有一堆模拟变量,比如随机温度、转速、位移。你只需要把服务端地址填到客户端里,然后浏览地址空间,确认数据能读到,再在此基础上改业务逻辑。

浏览地址空间的代码是排查NodeId的重要工具,我通常把它写成一个独立方法:

' 浏览地址空间,打印前20个节点的NodeId和名称 Public Sub BrowseNodes(session As Session) Dim root As NodeId = ObjectIds.ObjectsFolder Dim nodesToBrowse As New BrowseDescriptionCollection() Dim nodeToBrowse As New BrowseDescription() nodeToBrowse.NodeId = root nodeToBrowse.Direction = BrowseDirection.Forward nodeToBrowse.NodeClassMask = UInt32.MaxValue nodeToBrowse.IncludeSubtypes = True nodeToBrowse.ResultMask = UInt32.MaxValue nodesToBrowse.Add(nodeToBrowse) Dim results As BrowseResultCollection = session.Browse(Nothing, nodesToBrowse, Nothing) For Each result As BrowseResult In results For Each ref As ReferenceDescription In result.References If ref.NodeId.IsNull() = False Then Console.WriteLine("{0} - {1}", ref.NodeId.ToString(), ref.DisplayName.Text) End If Next Next End Sub

root从ObjectsFolder开始,Direction用Forward表示向下遍历子节点,NodeClassMask设为最大值表示不过滤节点类型,IncludeSubtypes保证把继承的节点也带出来。结果里每个ReferenceDescription的NodeId就是后续读写的目标。注意有一些系统变量会非常多,打印前20个即可,不然控制台刷屏。

跑通之后,你会发现真正花时间的地方不是OPC UA本身,而是你的数据映射逻辑:从几十个NodeId里找出你要的那几个,然后安排合适的采集周期。模拟器正好能帮你把这个映射表定下来。

5.2 从“能跑”到“好用”:超时、重连和日志三个改造点

第一个改造点是超时。Session.Create里有个sessionTimeout参数,但那只决定服务端整理会话的周期,真正影响你用户体验的是每次读写的超时。你可以为每个读操作设置一个超时时间,也可以通过Session.Read的重载传入请求头。我一般会把超时统一配置到ApplicationConfiguration的RequestTimeout属性,避免每个方法都要传参数。

第二个改造点是重连。PLC重启、网络闪断,OPC UA会话都会断掉。如果你不做处理,程序会一直卡在等待状态。常见做法是监听Session的SessionReconnectComplete事件,重连成功后之前建立的订阅不会自动修复,你需要重新执行订阅创建逻辑:

' 监听会话重连完成事件 AddHandler session.SessionReconnectComplete, Sub(se As Session, e As EventArgs) RebuildSubscription() End Sub

这里用AddHandler和Lambda表达式,VB语法里注意在事件回调中捕获session变量时,闭包会引用同一个会话对象,重连后session内部会切换通道,所以直接用se重新获取即可,不要缓存旧的Subscription对象。RebuildSubscription方法里做的就是3.3那几行,把它抽出来复用。

第三个改造点是日志。Ua .NET Standard库本身的日志能告诉你每一层的握手状态,但默认不输出。你可以把SetTraceMask的组合设得细一点,比如只记录网络层和状态机变化,太多反而干扰。我自己会额外写一个简易日志类,把连接成功、订阅建立、每次数据变化的时间戳记录下来,排查时比翻库日志快得多。

还有一个小技巧:把NodeId集合移到配置文件或者数据库里,不要在代码里硬编码。这样模拟器换服务端时,只改配置文件就能连上,不用重新编译。我经历过一次因为NodeId写死在代码里,结果现场变量名和服务端对不上,改了一晚上。从那以后,我每次对接前都会强制走一遍浏览地址空间的流程,把所有NodeId存成JSON。希望这篇笔记帮到你。

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

相关新闻

EtherNet/IP 网络调试:设备扫描、CIP 连接与排障实战

EtherNet/IP 网络调试:设备扫描、CIP 连接与排障实战

简介:Ethernet/IP Tool V2.2.0 是一款面向自动化与工业网络工程师的 Ethernet/IP 协议调试测试工具,支持设备发现、实时数据交互、参数配置与通信故障排查,适用于设备集成、网络运维及教学培训等场景。压缩包共 20 个文件,以 exe …

2026/10/20 17:36:10 阅读更多 →
SQL Server JDBC无法打开数据库MSSQLSERVER排查与修复

SQL Server JDBC无法打开数据库MSSQLSERVER排查与修复

如果你在日志里看到这条 com.microsoft.sqlserver.jdbc.SQLServerException: 无法打开登录所请求的数据库 "MSSQLSERVER"。登录失败。 ,第一反应多半是"数据库连不上了,赶紧检查账号密码"。但按这个思路去排查,大概率会…

2026/10/20 17:36:10 阅读更多 →
Selenium四类高频难点完整方案:下拉框、文件上传、弹窗与多窗口切换

Selenium四类高频难点完整方案:下拉框、文件上传、弹窗与多窗口切换

做Web自动化测试,跑到电商、后台管理系统这类项目时,脚本最容易翻车的其实不是登录、不是数据断言,而是下拉框、文件上传、弹窗和多窗口切换这种看起来不起眼的交互操作。很多新人在这四个场景上栽了跟头,多半是因为官方文档只讲了…

2026/10/20 17:36:10 阅读更多 →

最新新闻

智能助手技能机制全链路拆解:存放、加载、更新与维护实战

智能助手技能机制全链路拆解:存放、加载、更新与维护实战

1. 从一次技能失效说起:为什么我要拆解这套机制去年冬天,我负责维护的一个智能协作助手突然在某个任务上"失忆"了。前一天还能准确调用的技能,第二天就像从没存在过一样,用户问它问题,它只会礼貌地回复"…

2026/10/20 18:22:01 阅读更多 →
Hello world!:从第一行代码到环境自检与冒烟测试

Hello world!:从第一行代码到环境自检与冒烟测试

每个程序员的电脑里,都住着一行 Hello world!。不管是写前端的、做嵌入式的,还是半路出家搞数据分析的,第一次敲下这行代码、按下运行键、盯着终端里的输出愣住两秒的时刻,基本可以算作职业生涯里的第一个"高光瞬间"。这…

2026/10/20 18:22:01 阅读更多 →
DeepSeek开源Agent框架插件化设计深度拆解:从注册到执行链路

DeepSeek开源Agent框架插件化设计深度拆解:从注册到执行链路

1. 拆之前先想清楚:为什么要看这个框架第一次看到 DeepSeek 开源 Agent 框架的消息,我其实没太当回事。市面上 Agent 框架已经多到数不过来,从早期的任务编排库到后来的多智能体协作平台,大部分都是把 LLM 调用、工具注册、对话循…

2026/10/20 18:22:01 阅读更多 →
插件化Agent框架源码拆解:工具、执行器、记忆与规划器四层设计

插件化Agent框架源码拆解:工具、执行器、记忆与规划器四层设计

1. 拆之前先想清楚:为什么要看这个 Agent 框架大模型圈子里做应用的人,最近一年绕不开一个词——Agent。但真正动手写过 Agent 的人都知道,从"能聊天"到"能干活"之间隔着一道巨大的鸿沟。你让模型调用一个工具&#xff0…

2026/10/20 18:22:01 阅读更多 →
AI中台数据资产智能清查指令与核验模板实战指南

AI中台数据资产智能清查指令与核验模板实战指南

1. 数据资产清查为什么需要一套“指令模板”做过企业级AI中台落地的人都有一个共同感受:模型训练、推理服务、知识库检索这些“显性能力”往往被反复讨论,但真正拖慢项目进度的,反而是最不起眼的一环——系统里到底有哪些数据资产、它们分布在…

2026/10/20 18:22:01 阅读更多 →
cgroups 核心模型与实操:从 v1 到 v2 的资源限制机制详解

cgroups 核心模型与实操:从 v1 到 v2 的资源限制机制详解

这段时间在帮人排查一套测试环境,机器一早就卡住,SSH 敲半天都进不去。登上去一看,好几个容器在抢 CPU,内存也快被打穿。想定位到底是哪个容器干的,光靠 namespace 根本看不出来——namespace 只解决“容器能看到什么”…

2026/10/20 18:21:00 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →