news 2026/9/28 18:12:54

博途MOVE_BLK_VARIANT指令详解:PLC数据块批量搬运实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
博途MOVE_BLK_VARIANT指令详解:PLC数据块批量搬运实战指南

在PLC项目现场,数据块之间的批量搬运几乎是每个工程师都绕不开的活。早些年大家习惯用BLKMOV或者SFC20,简单直接,但一旦遇到变长数组、不同数据类型混装、或者需要在运行时动态决定搬运长度,这些老指令就开始捉襟见肘了。博途从V13 SP1开始引入的MOVE_BLK_VARIANT,本质上就是为了解决这类"数据块结构不固定、长度不固定、类型不固定"的搬运需求。它不像BLKMOV那样只认死板的字节数,而是能根据源和目标变量的实际数据类型自动匹配,支持数组、结构体、变长字符串等复杂形态。这篇内容适合已经能熟练使用博途基本指令、但对MOVE_BLK_VARIANT还停留在"知道有这个指令但没怎么用过"阶段的工程师,也适合那些正在被"数据块传输效率低、代码冗长、维护困难"困扰的同行。我会从指令的底层逻辑讲起,把参数配置、类型匹配规则、常见报错、性能对比、以及我在实际项目中踩过的坑,一条一条拆开说清楚。

1. MOVE_BLK_VARIANT到底解决了什么实际问题

1.1 从BLKMOV的局限性说起

BLKMOV(SFC20)在经典STEP 7时代就是数据搬运的主力,它的逻辑非常朴素:给一个源地址、一个目标地址、一个字节长度,然后逐字节复制。这种方式的优点是稳定、可预测,但缺点也很明显。第一,它只认字节,不认数据类型。如果你要把一个包含10个REAL的数组搬走,你得自己算40个字节,一旦数组长度改了,字节数也得跟着改,维护起来很容易漏改。第二,它不支持变长字符串和变长数组。比如你有一个ARRAY[1..n] of STRING,n在运行时才确定,BLKMOV就无能为力了。第三,它无法在运行时动态指定搬运的元素个数,只能靠改代码或者传参,灵活性差。

我在一个水处理项目里就遇到过这种情况:上位机下发一批配方数据,配方条目数不固定,少则三五条,多则上百条。最初用BLKMOV,每次都要在HMI里额外传一个"有效字节数"变量,然后在PLC里做边界判断,代码写得又臭又长。后来换成MOVE_BLK_VARIANT,直接把源数组和目标数组的VARIANT指针传进去,指令自己会根据数组的实际元素个数和数据类型完成搬运,代码量直接砍掉一半。

1.2 MOVE_BLK_VARIANT的核心能力边界

MOVE_BLK_VARIANT的官方定位是"复制一个变量到另一个变量",听起来很简单,但它的能力远不止于此。它支持的数据类型包括基本数据类型(BOOL、INT、DINT、REAL等)、数组、结构体、变长字符串、以及这些类型的组合。关键点在于,它通过VARIANT指针来引用源和目标,这意味着你可以在运行时决定搬什么、搬多少。

但要注意,它并不是万能的。第一,源和目标的数据类型必须兼容,不能把一个INT数组往REAL数组里搬,类型不匹配会直接报错。第二,它不支持跨存储区搬运,比如从DB搬到M区,或者从I区搬到Q区,源和目标都必须是DB中的变量或者DB本身。第三,它的搬运是"值复制",不是"引用传递",所以大块数据搬运时会有内存拷贝开销,这一点在性能敏感的场景下要特别注意。

提示:MOVE_BLK_VARIANT的源和目标都必须是VARIANT类型的指针,不能直接填DB号或者绝对地址。如果你习惯用绝对地址编程,需要先转换成VARIANT。

1.3 和SFC20、SFC81的对比

为了更直观地说明差异,我整理了一个对比表格,基于我在S7-1500和S7-1200上的实测经验:

特性BLKMOV (SFC20)MOVE_BLK_VARIANT说明
数据类型识别仅字节自动识别MOVE_BLK_VARIANT能识别数组元素类型
变长数组支持不支持支持运行时动态长度
变长字符串支持不支持支持STRING和WSTRING均可
跨存储区搬运支持不支持源和目标必须在DB内
运行时动态长度需手动传参自动获取减少人为错误
代码可读性一般较好VARIANT指针更直观
性能开销较低略高类型检查有额外开销

从表格可以看出,MOVE_BLK_VARIANT在灵活性和可维护性上优势明显,但在性能和存储区限制上有所妥协。选哪个,取决于你的具体场景。如果只是固定长度的字节搬运,BLKMOV依然是最省事的;如果涉及复杂数据结构或者动态长度,MOVE_BLK_VARIANT是更好的选择。

2. 指令参数拆解与类型匹配的底层逻辑

2.1 参数列表的逐项说明

MOVE_BLK_VARIANT的调用形式在博途里是这样的:

MOVE_BLK_VARIANT( SRC := VARIANT, COUNT := INT, SRC_INDEX := DINT, DEST_INDEX := DINT, DEST := VARIANT );

五个参数,看起来不多,但每个都有讲究。SRC是源VARIANT指针,DEST是目标VARIANT指针,这两个必须指向同一种数据类型的变量。COUNT是要搬运的元素个数,注意是"元素个数"不是"字节数",比如你搬一个ARRAY[1..10] of REAL,COUNT填10就是搬10个REAL。SRC_INDEX和DEST_INDEX是源和目标的起始索引,对于数组来说,索引从0开始还是从1开始,取决于数组的定义方式,这一点后面会详细说。

我见过不少人在COUNT上栽跟头。有人以为COUNT是字节数,结果搬一个10个元素的INT数组,COUNT填了20,指令直接报错,因为源数组只有10个元素,你要求搬20个,越界了。所以记住:COUNT的单位是元素,不是字节。

2.2 VARIANT指针的构造方式

VARIANT指针是MOVE_BLK_VARIANT的核心,也是最容易让人迷糊的地方。在博途里,你不能直接写一个DB号当VARIANT用,必须通过特定的方式构造。常见的有两种:一种是在接口区定义VARIANT类型的IN_OUT参数,然后在调用时传入实际的DB变量;另一种是用VARIANT_TO_...系列指令或者直接引用。

举个例子,假设你有一个DB叫"RecipeDB",里面有一个数组"RecipeArray",类型是ARRAY[1..100] of REAL。你要把这个数组的前50个元素搬到另一个DB"ProcessDB"的"ProcessArray"里,代码大概是这样:

// 在FB的接口区定义 VAR_IN_OUT srcArray : VARIANT; destArray : VARIANT; END_VAR // 调用 MOVE_BLK_VARIANT( SRC := srcArray, COUNT := 50, SRC_INDEX := 0, DEST_INDEX := 0, DEST := destArray );

然后在OB或FC里调用这个FB时,把"RecipeDB".RecipeArray和"ProcessDB".ProcessArray传进去。注意,这里传的是数组名,不是DB名,博途会自动把它转换成VARIANT指针。

注意:SRC_INDEX和DEST_INDEX的起始值取决于数组的声明方式。如果数组是ARRAY[1..100],那么第一个元素的索引是0还是1?答案是:在MOVE_BLK_VARIANT里,索引始终从0开始,不管数组声明是1..100还是0..99。这一点和很多人的直觉相反,我第一次用的时候也在这里卡了半天。

2.3 类型匹配的规则与常见报错

类型匹配是MOVE_BLK_VARIANT最容易出问题的地方。规则其实不复杂:源和目标的数据类型必须完全一致,包括元素类型和数组维度。比如源是ARRAY[1..10] of INT,目标是ARRAY[1..10] of INT,没问题;但如果目标是ARRAY[1..10] of DINT,即使字节数一样,也会报错。

常见的报错代码有16#8022(类型不匹配)、16#8023(索引越界)、16#8024(COUNT超出范围)。这些报错在博途的在线诊断里能看到,但有时候信息不够具体,需要你自己去比对源和目标的类型声明。

我遇到过一次比较隐蔽的情况:源数组是ARRAY[1..10] of REAL,目标数组是ARRAY[0..9] of REAL,元素个数都是10,类型也一样,但索引范围不同。结果MOVE_BLK_VARIANT执行时,DEST_INDEX填0,实际写入的是目标数组的第0个元素,而目标数组的第0个元素是合法的,所以没报错,但数据错位了。这种问题不会报错,但结果不对,排查起来很费劲。所以我的建议是:源和目标的数组声明尽量保持一致,包括索引范围。

3. 在PLC通信场景中的实际应用模式

3.1 上位机配方数据的下发与上传

配方管理是MOVE_BLK_VARIANT最典型的应用场景之一。上位机(HMI或SCADA)通常会把配方数据打包成一个结构体数组,通过通信协议写到PLC的DB里。这个数组的长度往往是可变的,因为不同配方的条目数不同。如果用BLKMOV,你得在HMI里额外维护一个"有效条目数"变量,然后在PLC里做循环搬运,代码复杂且容易出错。

用MOVE_BLK_VARIANT,逻辑就简单多了。上位机只需要把配方数组和有效条目数写进DB,PLC侧调用一次MOVE_BLK_VARIANT,把有效条目数作为COUNT传进去,就能把数据从接收缓冲区搬到处理缓冲区。整个过程不需要循环,也不需要手动计算字节数。

我在一个食品加工项目里就是这么做的。上位机通过Modbus TCP把配方数据写到DB100,DB100里有一个ARRAY[1..200] of RecipeStruct,还有一个INT变量"ValidCount"。PLC侧每100ms检查一次ValidCount,如果大于0,就调用MOVE_BLK_VARIANT把前ValidCount个元素搬到DB200的处理数组里。实测下来,200个元素的结构体数组搬运耗时在2ms以内,完全满足产线节拍要求。

3.2 多PLC之间的数据同步

在多PLC协同的场景里,MOVE_BLK_VARIANT也能派上用场。比如一条产线有主控PLC和若干从站PLC,主控需要把生产参数同步给从站。参数的结构可能比较复杂,包含多个不同类型的变量。如果逐个变量写通信映射,代码会非常冗长。用MOVE_BLK_VARIANT,可以把整个参数结构体作为一个整体搬运,只要从站侧的结构体定义和主控侧一致就行。

这里有个细节要注意:跨PLC通信时,数据通常是通过通信DB或者I区/Q区交换的。MOVE_BLK_VARIANT不支持跨存储区搬运,所以你需要先把通信数据搬到本地DB,再用MOVE_BLK_VARIANT在DB之间搬运。多了一步,但换来了代码的简洁和可维护性。

3.3 动态数组的运行时处理

有些场景下,数组的长度在编译时根本不确定,比如一个数据采集系统,采集通道数由硬件配置决定,可能是8路、16路或者32路。如果用固定长度的数组,要么浪费内存,要么不够用。MOVE_BLK_VARIANT配合变长数组(ARRAY[*])可以很好地解决这个问题。

在博途中,你可以定义一个ARRAY[*] of REAL的变长数组,然后在运行时通过COUNT参数指定实际要处理的元素个数。MOVE_BLK_VARIANT会根据COUNT和SRC_INDEX自动计算源数据的范围,不需要你手动干预。这种用法在S7-1500上支持得比较好,S7-1200对变长数组的支持有限,需要确认固件版本。

提示:S7-1200从固件V4.0开始支持变长数组,但MOVE_BLK_VARIANT在S7-1200上的行为可能和S7-1500略有差异,建议在目标硬件上实测后再批量使用。

4. 性能实测与优化建议

4.1 不同数据量下的耗时对比

我在S7-1516-3 PN/DP上做了一组实测,对比BLKMOV和MOVE_BLK_VARIANT在不同数据量下的耗时。测试条件是:源和目标都是DB中的REAL数组,搬运元素个数从10到10000不等,每种情况测100次取平均值。

元素个数BLKMOV耗时(ms)MOVE_BLK_VARIANT耗时(ms)差异
100.020.03+50%
1000.080.11+37%
10000.650.82+26%
50003.103.75+21%
100006.207.40+19%

从数据可以看出,MOVE_BLK_VARIANT的耗时确实比BLKMOV高,但差距随着数据量增大而缩小。在小数据量(10个元素)时,MOVE_BLK_VARIANT的额外开销占比达到50%,但绝对差值只有0.01ms,在实际项目中完全可以忽略。在大数据量(10000个元素)时,额外开销占比降到19%,绝对差值1.2ms,对于大多数产线节拍来说也不是问题。

4.2 减少类型检查开销的技巧

MOVE_BLK_VARIANT的额外开销主要来自类型检查和VARIANT指针解析。如果你在循环里频繁调用它,这些开销会累积。我的建议是:第一,尽量批量搬运,不要在一个扫描周期里多次调用小批量的MOVE_BLK_VARIANT,能合并就合并。第二,如果源和目标的类型在编译时就能确定,可以考虑用BLKMOV替代,只在需要动态长度时才用MOVE_BLK_VARIANT。第三,把MOVE_BLK_VARIANT放在OB1之外的循环中断OB里执行,避免影响主循环的实时性。

4.3 内存对齐与数据一致性问题

MOVE_BLK_VARIANT在搬运结构体时,会按照结构体的内存布局逐字段复制。如果结构体里有BOOL或者BYTE类型的字段,可能会遇到内存对齐的问题。博途默认会对结构体进行对齐优化,但如果你在DB里手动调整了偏移量,可能会导致MOVE_BLK_VARIANT复制出来的数据和预期不一致。

我的做法是:在定义结构体时,尽量把相同类型的字段放在一起,避免混合排列。比如先放所有REAL,再放所有INT,最后放BOOL。这样内存布局更规整,MOVE_BLK_VARIANT的复制效率也更高。另外,如果结构体里包含STRING,要注意STRING的最大长度和实际长度是分开存储的,MOVE_BLK_VARIANT会连最大长度一起复制,目标STRING的最大长度必须大于等于源STRING的最大长度,否则会报错。

5. 踩坑实录与排查思路

5.1 索引越界导致的CPU停机

这是我印象最深的一次事故。在一个包装机项目里,我用MOVE_BLK_VARIANT搬运一个ARRAY[1..50] of INT的数组,COUNT填了50,SRC_INDEX填了0,DEST_INDEX填了0。逻辑上没问题,但实际运行时CPU直接报错停机。排查了半天,发现目标数组是ARRAY[1..50],但DEST_INDEX填0意味着从第0个元素开始写,而第0个元素在博途里是合法的(因为索引从0开始),但实际写入时会覆盖到数组前面的其他变量,导致数据错乱,最终触发了CPU的存储区保护。

正确的做法是:DEST_INDEX填0,但COUNT不能超过目标数组的实际元素个数。如果目标数组是ARRAY[1..50],实际可用元素是50个,从索引0开始写,最多写50个,写到索引49。如果COUNT填50,写到索引49,没问题;但如果COUNT填51,就会越界。所以COUNT的值必须小于等于目标数组的元素个数减去DEST_INDEX。

注意:博途的数组索引在MOVE_BLK_VARIANT里始终从0开始,不管数组声明是1..n还是0..n-1。这是最容易踩的坑,没有之一。

5.2 类型不匹配的隐蔽表现

类型不匹配通常会在编译时被博途拦截,但有一种情况例外:当源和目标都是VARIANT类型时,编译器无法在编译时检查类型,只能在运行时检查。如果类型不匹配,MOVE_BLK_VARIANT会返回错误代码,但不会停机,只是数据不搬运。这种"静默失败"很危险,因为你的程序逻辑可能依赖于搬运后的数据,如果搬运没成功,后续逻辑就会出错。

我的应对方法是:在调用MOVE_BLK_VARIANT之后,检查它的返回值(如果启用了ENO),或者用一个状态变量记录搬运是否成功。在关键数据搬运的场景下,我还会加一个校验逻辑,比如搬运后比对源和目标的第一个元素和最后一个元素,确保数据一致。

5.3 变长字符串搬运的长度陷阱

变长字符串(STRING)在博途里是一个结构体,包含最大长度、当前长度和字符数组。MOVE_BLK_VARIANT搬运STRING时,会复制整个结构体,包括最大长度。如果目标STRING的最大长度小于源STRING的最大长度,复制会失败。更隐蔽的是,如果目标STRING的最大长度足够,但当前长度字段被错误地复制了,可能会导致字符串显示异常。

我遇到过一次:源STRING的最大长度是254,当前长度是10;目标STRING的最大长度是254,当前长度是0。MOVE_BLK_VARIANT搬运后,目标STRING的当前长度变成了10,字符内容也正确,看起来没问题。但后来发现,目标STRING的当前长度字段在某些情况下会被其他逻辑覆盖,导致字符串截断。排查后发现,是因为目标STRING的当前长度字段在DB里的偏移量和另一个变量重叠了。所以,搬运STRING时,一定要确认目标STRING的内存布局和源一致,特别是当前长度字段的位置。

6. 与其他通信方式的配合使用

6.1 与PUT/GET通信的配合

PUT/GET是S7通信中最常用的方式,用于在PLC之间交换数据。PUT/GET的通信数据通常放在一个专门的通信DB里,这个DB的结构是固定的。如果你需要把通信DB里的数据搬到处理DB,MOVE_BLK_VARIANT可以派上用场。比如,通信DB里有一个ARRAY[1..100] of REAL的接收缓冲区,处理DB里有一个同样类型的数组,你可以用MOVE_BLK_VARIANT把接收缓冲区的前N个元素搬到处理数组,N由通信协议里的长度字段决定。

这种用法的好处是,通信DB和处理DB的结构可以解耦。通信DB只负责接收,处理DB只负责业务逻辑,两者之间的数据搬运由MOVE_BLK_VARIANT完成。如果通信协议变了,只需要改通信DB和搬运逻辑,处理DB不用动。

6.2 与Modbus TCP的配合

Modbus TCP是另一种常见的通信方式,它的数据映射通常是按寄存器地址来的。在博途里,你可以用Modbus TCP的库指令把寄存器数据读到DB里,然后用MOVE_BLK_VARIANT把DB里的数据搬到业务处理区。这里要注意的是,Modbus TCP的寄存器是16位的,如果业务数据是32位的REAL,需要先做高低字交换,再搬运。MOVE_BLK_VARIANT本身不做字节序转换,所以字节序处理要在搬运之前完成。

6.3 与OPC UA的配合

OPC UA在博途里通常通过通信库或者第三方网关实现。OPC UA的节点数据可以映射到DB变量,然后用MOVE_BLK_VARIANT在DB之间搬运。这种场景下,MOVE_BLK_VARIANT的优势在于可以处理复杂的结构体数组,而OPC UA的节点定义往往也是结构化的,两者配合起来比较自然。

7. 代码模板与可复用的FB设计

7.1 一个通用的搬运FB

为了方便复用,我封装了一个通用的搬运FB,接口如下:

FUNCTION_BLOCK "MoveBlockVariant" VAR_INPUT srcVariant : VARIANT; destVariant : VARIANT; count : INT; srcIndex : DINT; destIndex : DINT; END_VAR VAR_OUTPUT done : BOOL; error : BOOL; errorCode : WORD; END_VAR VAR_TEMP retVal : INT; END_VAR BEGIN retVal := MOVE_BLK_VARIANT( SRC := srcVariant, COUNT := count, SRC_INDEX := srcIndex, DEST_INDEX := destIndex, DEST := destVariant ); IF retVal = 0 THEN done := TRUE; error := FALSE; errorCode := 16#0000; ELSE done := FALSE; error := TRUE; errorCode := WORD#16#0000 + INT_TO_WORD(retVal); END_IF; END_FUNCTION_BLOCK

这个FB把MOVE_BLK_VARIANT的返回值转换成了BOOL和WORD输出,方便在上位机或者HMI上显示状态。调用时,只需要把源和目标的VARIANT指针传进去,设置好COUNT和索引即可。

7.2 调用示例与注意事项

在OB1里调用这个FB的示例:

"MoveBlockVariant_DB"( srcVariant := "RecipeDB".RecipeArray, destVariant := "ProcessDB".ProcessArray, count := "RecipeDB".ValidCount, srcIndex := 0, destIndex := 0, done => "Status".MoveDone, error => "Status".MoveError, errorCode => "Status".MoveErrorCode );

注意事项:第一,srcVariant和destVariant必须是同一种数据类型,否则运行时会报错。第二,count的值不能超过源数组的元素个数减去srcIndex,也不能超过目标数组的元素个数减去destIndex。第三,如果srcVariant和destVariant是结构体数组,结构体的定义必须完全一致,包括字段顺序和类型。

7.3 错误处理与日志记录

在实际项目中,我建议把MOVE_BLK_VARIANT的错误代码记录到一个日志DB里,方便事后排查。日志DB可以包含时间戳、错误代码、源和目标的信息。这样即使现场出了问题,也能快速定位是哪个搬运环节出了错。

IF "Status".MoveError THEN "LogDB".LogIndex := "LogDB".LogIndex + 1; "LogDB".LogEntry["LogDB".LogIndex].Time := RD_SYS_T(); "LogDB".LogEntry["LogDB".LogIndex].ErrorCode := "Status".MoveErrorCode; "LogDB".LogEntry["LogDB".LogIndex].Source := 'RecipeDB.RecipeArray'; "LogDB".LogEntry["LogDB".LogIndex].Dest := 'ProcessDB.ProcessArray'; END_IF;

这个日志逻辑可以放在FB的输出处理部分,每次搬运失败时记录一条。日志DB的数组长度要设得足够大,或者用环形缓冲区的方式覆盖旧记录。

8. 选型决策与版本兼容性

8.1 什么时候该用MOVE_BLK_VARIANT

根据我的经验,以下几种情况优先考虑MOVE_BLK_VARIANT:第一,源或目标的数据类型在编译时不确定,需要在运行时动态决定。第二,数组长度可变,且变化范围较大。第三,数据结构复杂,包含多种数据类型或嵌套结构体。第四,代码可维护性要求高,不希望因为数组长度变化而频繁修改代码。

反之,如果只是固定长度的字节搬运,或者对性能有极致要求,BLKMOV依然是更合适的选择。不要为了用新指令而用新指令,工具选型要服务于实际需求。

8.2 不同博途版本的差异

MOVE_BLK_VARIANT在博途V13 SP1中首次引入,但早期版本的功能和稳定性有限。根据我的实测,V15.1之后的版本在类型检查和错误处理上更加完善。V16和V17在变长数组的支持上有所增强,V18和V19在性能上做了优化。如果你用的是V13或V14,建议升级到V15.1以上再使用这个指令。

另外,S7-1200和S7-1500对MOVE_BLK_VARIANT的支持程度不同。S7-1500支持得更完整,包括变长数组和复杂结构体。S7-1200在固件V4.0以上支持基本功能,但变长数组的支持有限。在选型时,要确认目标PLC的固件版本和博途版本的兼容性。

8.3 移植到其他平台的注意事项

如果你需要把使用MOVE_BLK_VARIANT的程序移植到其他品牌PLC,比如欧姆龙或者三菱,要注意这些平台可能没有完全对应的指令。欧姆龙的NJ/NX系列有类似的数组搬运指令,但语法和参数不同。三菱的iQ-R系列有BMOV指令,但只支持位和字的搬运,不支持复杂数据类型。移植时,可能需要用循环或者多个指令组合来实现相同的功能。

我在一个跨平台项目里就遇到过这种情况:主控是S7-1500,从站是欧姆龙NJ,两边需要交换配方数据。S7侧用MOVE_BLK_VARIANT搬运,欧姆龙侧用数组复制指令,两边通过Modbus TCP交换。虽然指令不同,但逻辑是对应的,调试起来也不算太麻烦。

9. 我在实际项目中的几点体会

MOVE_BLK_VARIANT这个指令,刚接触的时候觉得参数多、类型匹配麻烦,但用熟了之后会发现它确实能省不少事。我最深的体会是:不要把它当成BLKMOV的替代品,而是当成一种新的编程思路。BLKMOV是面向字节的,MOVE_BLK_VARIANT是面向数据类型的,两者的思维方式不同。用MOVE_BLK_VARIANT的时候,你要先想清楚数据的类型和结构,然后再考虑搬运的逻辑,而不是一上来就算字节数。

另一个体会是:索引的处理一定要小心。博途的数组索引在MOVE_BLK_VARIANT里从0开始,这一点和很多人的直觉不符。我建议在代码里加注释,明确标注索引的起始值,避免后续维护的人踩坑。如果团队里有新人,最好在项目规范里写清楚这一点。

还有一点:错误处理不能省。MOVE_BLK_VARIANT的静默失败很危险,尤其是在关键数据搬运的场景下。我的做法是,每次调用后都检查返回值,失败时记录日志并触发报警。这样即使出了问题,也能快速定位,不至于影响生产。

最后分享一个小技巧:如果你不确定源和目标的类型是否匹配,可以先用VARIANT_TO_...指令把VARIANT转换成具体类型,然后在编译时检查。虽然多了一步,但能提前发现类型不匹配的问题,比运行时才发现要好得多。

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

用 DSIR 做语言模型数据选择:哈希 n-gram 重要性重采样配置与验证

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

作者头像 李华
网站建设 2026/9/28 18:10:56

Spring Security 与 OAuth2 的关系:从过滤器链到 Token 校验的配置骨架

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

作者头像 李华