news 2026/9/18 5:03:26

Modbus RTU现场调试避坑指南:从双主站冲突到字节序陷阱

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Modbus RTU现场调试避坑指南:从双主站冲突到字节序陷阱

做工业现场调试这些年,我见过最折磨人的场面不是设备完全不转,而是这种:USB转485刚插上电脑,Modbus Poll里地址1、功能码03、长度10,一次性把所有寄存器读得漂漂亮亮;等你拔了线,把参数原封不动填进PLC程序,一上电就开始超时,数据乱跳,整个柜子都像在跟你作对。Modbus RTU明明是个快四十年历史的老协议,结构简单到不能再简单,可现场就是有办法让它“调一次崩一次”。今天就把这些年踩过的坑摊开来说,写给搞PLC、单片机、上位机和仪表集成的兄弟看,十分钟过一遍,至少让你在现场少掉几根头发。

1. 故障定位:先分清是“两个主站打架”还是“从站不理人”

1.1 最经典的“电脑一插就崩”

先说一个我在现场碰到不下十次的场景:工程师把笔记本通过USB转485接到RS485总线上,打开Modbus Poll,扫描一圈,所有从站数据整整齐齐。然后拔下电脑,把同样的参数填进PLC程序,点击运行,第一轮轮询就超时,设备动作全部乱套。这时候大部分人的第一反应是“PLC程序写错了”,于是开始研究程序,改来改去,越改越崩。

真正的元凶其实是:你的电脑和PLC同时挂在了同一条485总线上。Modbus RTU是严格的主从问答式协议,半双工总线在同一时刻只允许一个主站发起请求。电脑开着Modbus Poll在轮询,PLC也在轮询,两个主站的请求帧在总线上碰撞,从站收到的全是乱帧,根本没法正确应答。而且这种故障的表现非常随机,有时候能通几帧,有时候全部超时,看起来就像从站设备在发疯。

判断方法很简单:把PLC切到停止模式,或者把电脑从总线上彻底断开,再看设备是否恢复正常。如果恢复,那就是典型的双主站冲突。永久解决方案不是让现场人员每次调试都拔线,而是在总线上加一个带切换开关的485分配器,或者干脆用笔记本+PLC串口调试口做分时连接。

1.2 用Modbus Poll + Modbus Slave 做A/B测试

现场排查最怕“凭空猜”,我习惯把系统拆成两半来做A/B测试。这里要用到两个最常用的调试工具:Modbus Poll(主站模拟)和Modbus Slave(从站模拟)。

A/B测试的套路是这样的:

  • A测:PLC程序 vs Modbus Slave。在电脑上跑Modbus Slave,把它模拟成你要通信的那个从站设备,然后让PLC按程序去轮询这个虚拟从站。如果PLC能正常读写Modbus Slave里的寄存器,说明你的PLC主站程序和参数配置基本没问题,问题出在真实从站设备或者物理线路上。
  • B测:Modbus Poll vs 真实从站。用Modbus Poll去读现场那台真实设备,如果Poll能正确读到数据,说明设备侧也没大问题,那问题大概率是PLC和设备的“连接参数”不一致,比如波特率、校验位、从站地址、寄存器映射这些。如果Poll也读不对,那问题基本可以锁定在从站配置或物理层。

这套方法的价值在于,Modbus Poll和Modbus Slave是按协议标准实现的,它们扫不出来问题,至少说明协议层和线路层是通的。剩下的就只是参数匹配问题,范围一下子缩小了一大半。

1.3 别在调试工具版本上浪费时间

网上搜“modbus poll密钥”“modbus slave密钥”的人特别多,我的建议是:调试场景下,官方试用版已经够用。Modbus Poll的完整功能在试用期里基本都能体验,现场用也只是偶尔开一下,真需要长期使用就买一份正版,别把时间耗在找注册码上。工具稳定比版本号重要得多,你真正要花时间研究的,是寄存器地址、字节序、轮询周期这些实实在在的东西。

还有一个细节:用Modbus Slave做模拟从站时,记得把从站地址设成和真实设备一致,否则PLC程序里写的是地址5,虚拟从站却是地址1,无论如何都通信不上,白白折腾半小时。

2. 数据格式三座山:字节序、字序、地址偏移

2.1 现象:读回来全是天文数字

通信通了,但从站返回的数据完全看不懂:明明应该是68,读回来却是10404;明明应该是1.5的浮点数,读回来是一个巨大且陌生的值。这种问题在Modbus RTU调试里出现频率极高,尤其常见于PLC和第三方仪表、变频器、伺服驱动器对接的场景。

根源在于:Modbus RTU协议只规定了字节在总线上按大端顺序发送(高字节在前),但它没有规定一个“字”内部在设备内存里怎么存放,更没规定多个寄存器组成32位数据时哪个寄存器在前。这就导致不同厂商的设备有完全不同的数据排列习惯。西门子和汇川不太一样,三菱又有自己的风格,各种仪表传感器更是各搞各的。

网上搜“汇川PLC用Modbus RTU高低位转换”的人很多,说明这不是个别现象。实际上不光是汇川,任何PLC接第三方设备都可能遇到。你光看协议文档看不出问题,必须用工具确认源设备的“脾胃”。

2.2 用Poll一锤定音,别靠猜

数据格式的定位其实很简单,就是用Modbus Poll这种调试工具把原始寄存器值读出来,然后人工判断。

操作路径是:在Modbus Poll里配置好连接和读取定义,用03功能码读一段连续寄存器。比如你和设备约定的是两个寄存器存放一个32位数据,读回来两个寄存器的原始值是0x1234和0x5678。你期望的32位值可能是0x12345678,也可能是0x56781234,还可能是0x34127856这类字节再做了一次交换的版本。在Poll的读取定义界面里,切换Byte Order和Word Order的几种组合,哪一组显示出来的数值符合你设备手册上的量纲和量程,就选哪一组。

这里有一个非常实用的技巧:往从站写入一个已知值,再读回来反推格式。比如用06功能码把0x3FC0写入寄存器N,把0x0000写入寄存器N+1。0x3FC00000在IEEE 754标准里恰好是浮点数1.5。然后你用不同的字序组合读这两个寄存器,哪个组合显示成1.5,哪个就是设备的实际排列。这个“写已知值反推格式”的办法,我用了很多年,比对着手册猜快得多。

2.3 PLC侧高低位转换的通用做法

确认了设备的字序之后,就要在PLC程序里做转换。以汇川PLC为例,MODBUS库函数通常有数据排列相关的处理选项,或者你可以在程序里用字节交换指令把MW的高低位换过来。关键不是背指令,而是先搞清楚“源设备的排列”和“PLC目标变量的排列”差在哪一步。

见过太多人一上来就套用别人的转换子程序,结果源设备是A排列,PLC是B排列,中间明明只需要交换一次,他却交换了两次,数据又变回错的。我的习惯是:所有仪表和变频器的数据转换逻辑,统一写在一个功能块里,用输入参数区分设备类型,不要每次在现场临时拼指令。这样下次接同型号设备,直接调用,连验证时间都省了。

2.4 32位浮点数的四重陷阱

单个16位寄存器的问题还算好处理,32位浮点数的坑就更多了。一个REAL占两个连续寄存器,除了字序(哪个寄存器在高位),还有字节序(高字节在前还是低字节在前),组合起来有四种情况。实际现场最常见的两种:一种是低地址寄存器存高位字(例如西门子PLC常见),另一种是低地址寄存器存低位字(很多国产仪表、变频器常见)。

这里还要小心一种特殊情况:部分设备的32位参数并不是存放在两个完全相邻的寄存器里,中间可能隔了其他数据。比如手册说“浮点值存放在寄存器3001和3003”,那你就不能简单地把3001和3002拼起来,必须按3001和3003去组合。读错了寄存器,格式再怎么换都对不上。所以在写PLC程序之前,先花两分钟把寄存器映射表看清楚,能省掉后面一整天的抓狂。

2.5 地址偏移:那个永远差“1”的陷阱

Modbus协议报文里的起始地址是从0x0000开始的,但设备手册、触摸屏组态、PLC指令块里用的地址体系五花八门。最典型的就是“40001”这种PLC地址,以及设备手册里直接用“1、2、3”这种连续编号。

举个例子:某仪表手册说“1号寄存器是频率,对应Modbus地址40001”。你如果用Modbus Poll去读,起始地址要填0,而不是40001。新手直接把40001填进起始地址,从站会尝试访问地址0x9C41,越界了自然返回异常码02。PLC程序里如果用了类似40001的地址表示,还要确认你的指令库是否会自动减去40001这个偏移量,有些会,有些不会。

遇到地址对不上的情况,我的排查方法是:用Modbus Poll从地址0开始连续读几十个寄存器,把读回来的值逐一对照设备手册的寄存器映射表,找出错位规律。是整体差了1,还是从某个地址开始全部错乱,一目了然。

3. 通信参数和从站地址:差一个bit全完蛋

3.1 波特率、校验位、停止位的“隐性组合”

通信参数不匹配是现场翻车率最高的原因之一。常见情况是:从站设备出厂默认9600、偶校验、1停止位(8E1),而PLC或上位机里配的是9600、无校验、1停止位(8N1)。从站按照偶校验去校验帧,发现校验失败直接丢弃整个帧,主站发出去请求后就一直等响应,直到超时。

最阴间的组合是“8N2”和“8E1”恰好拥有相同的帧位宽(起始1位+数据8位+校验/停止2位,总共11位),有时候PLC配错了也能勉强通信,因为总线上看起来都是11bit一帧。但实际上奇偶校验逻辑完全不同,抗干扰能力也天差地别。这种“能通但不稳定”的状态,现场非常难排查。

我的建议很简单:把每一台设备的波特率、校验位、停止位打出来,三方核对(主站配置、从站面板、设备手册),不一致就先改一致。尤其注意有些仪表面板上波特率单位是kbps,比如9600显示为9.6,别看成96。

3.2 从站地址:0号地址和“广播地狱”

Modbus协议里,从站地址0是广播地址,所有从站都会接收,但从站不会对广播帧做任何应答。这个机制在正常使用时没问题,但现场调试时容易出大事。

有次我在现场看到的现象是:PLC一发写命令,总线上所有变频器屏幕都在闪,参数集体乱跳。查到最后,是之前有人调试时把某台变频器的从站地址给改成了0。PLC在向地址1发送正常请求时,地址0的设备理论上不应答,但向地址0发送广播写命令时,所有设备都执行了。这种“广播地狱”的后果极其严重,轻则参数被误写,重则设备动作错乱。

另外要注意地址偏移:有些设备的实际从站地址从0开始编号,而Modbus协议里地址1才对应第一个有效从站。如果PLC里给从站配了地址1,而设备实际是“0号”,那PLC实际上是在和广播地址通信,所有设备都收到了消息,但没人正确应答。排查方法是用Modbus Poll的扫描功能,把总线上的设备一台一台单独接上去扫,确认真实地址范围。绝对不要在多台设备同时在线时扫描,出现两个相同地址的设备会直接导致总线冲突。

3.3 功能码:03、04、06、16这四兄弟别搞混

Modbus功能码不多,但现场经常用错。03读保持寄存器,04读输入寄存器,很多设备把参数放在保持寄存器区,只能用03读;但测量值、实时监测值这类数据却放在输入寄存器区,只能用04读。如果你用03去读04的区域,从站会返回异常码01(非法功能码)。

写操作同样有讲究:06是写单个寄存器,16(十六进制0x10)是写多个连续寄存器。有些设备只支持06,有些只支持16,有些两个都支持但对地址范围有限制。最典型的错误是:你想把一个32位浮点数写入两个寄存器,却用了06功能码逐字写,结果只写了一半;或用了16功能码一次写两个寄存器,却把两个16位整数当成两个独立参数写了进去,数据逻辑全乱。

功能码含义常见用途
01读线圈开关量输出状态
02读离散输入开关量输入状态
03读保持寄存器读写型参数、设定值
04读输入寄存器只读型检测数据、测量值
05写单个线圈单点开关量控制
06写单个寄存器单个参数写入
16写多个寄存器批量参数、32位数据写入

3.4 一条总线上真挂32台变频器会怎样

网上有人问“一个西门子PLC与32个变频器Modbus通讯控制是否可行”,答案是可行,但要把代价算清楚。

先做一道算术题。在9600波特率下,读每台变频器的3个保持寄存器:请求帧约8字节,响应帧约11字节,总共19字节。按常见的8E1格式,每字节11bit,一帧大概209bit,传输时间约22毫秒。加上帧间隔和从站内部响应时间,单台设备一轮需要的平均时间大约在30~40毫秒。32台串行轮询一圈,光正常通信就得1秒到1.3秒。

如果其中一台变频器从站掉线了,主站还要等响应超时。超时按200毫秒算,再加重试,整条总线的轮询周期会被拖到两三秒,操作员在触摸屏上按一下,设备半天才有反应,这就是“手风琴效应”的现场版。

所以在实际项目里,我一般这样处理:

  • 32台变频器不要挂一条总线,最少分两条总线,每条16台,或者用网关做分组;
  • 把波特率提到115200,轮询时间能缩短近10倍,但前提是布线质量和传输距离要过关;
  • 每台只读必要的运行参数,别把变频器的所有寄存器都拉一遍;
  • 重点做好从站掉线的容错处理,别让一台故障设备把整条总线拖死。

4. 时序、超时和CRC:看着没问题,跑起来就崩

4.1 3.5字符时间不是玄学,是协议规定

Modbus RTU的帧和帧之间,必须有至少3.5个字符时间的静默间隔,帧内字节间隔不能超过1.5个字符时间。这是协议写死的规则,目的就是让接收方能通过静默间隔来切分帧。

关键问题是“一个字符时间”到底是多少。它取决于波特率和帧格式:以常见的8E1为例,每个字符是起始1位+数据8位+校验1位+停止1位,共11位。9600波特率下,1个字符时间约1.146毫秒,3.5字符时间约4毫秒。如果是8N1(无校验),每字符10位,3.5字符时间约3.65毫秒。

很多人在程序里用固定的“帧间隔10毫秒”来判断一帧结束,这在9600波特率下勉强能用,但换到115200波特率下就有问题。10毫秒足以把下一帧的开头误判成当前帧的尾巴,处理逻辑就全乱了。尤其是做单片机或者嵌入式Modbus从站程序的朋友,帧超时时间必须根据波特率动态计算,或者直接用串口的空闲中断来实现。

4.2 响应超时别拍脑袋,轮询周期要留余量

从站收到请求到发出响应,中间需要处理时间。有的设备很快,十几毫秒;有的设备很慢,比如某些电能表和复杂的仪表,要50甚至100毫秒。如果你的主站响应超时设置太短,比如PLC默认的100毫秒,遇到慢速从站就会频繁超时。

超时之后主站通常会重试,重试又占用总线时间,导致后面排队的从站全部等待,造成网络拥堵。越堵越超时,越超时越重试,最后整条总线的通信效率急剧下降。

我一般这样设置:单台从站的响应超时设置在200~500毫秒,重试次数控制在1次或2次。轮询周期按“所有从站正常响应时间总和×1.5,再单独加上超时预算”来估算。如果某个从站总是偶尔超时,优先查线路和从站供电,而不是把重试次数拉到5次以上。重试是兜底策略,不是治疗方案。

4.3 CRC16的三个典型翻车现场

CRC校验错误是Modbus RTU通信失败的另一个高频原因,尤其是自己写单片机程序的时候。以下三个错误占了绝大多数:

第一个:初值用成了0x0000,而Modbus标准规定初值是0xFFFF。 第二个:多项式用成了0x8005,那是给XMODEM这类高位先行CRC用的。Modbus RTU的CRC是低位先行,对应多项式是0xA001。 第三个:算完结果不交换高低字节。Modbus规定发送时低字节在前、高字节在后,很多人按习惯先发高字节,结果接收方校验永远失败。

贴一段我验证过的C语言CRC16/MODBUS实现:

uint16_t crc16_modbus(uint8_t *buf, uint16_t len) { uint16_t crc = 0xFFFF; while (len--) { crc ^= *buf++; for (int i = 0; i < 8; i++) { if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001; else crc >>= 1; } } return crc; }

发送时注意顺序:先发送返回值的低字节,再发送高字节。这里给一个测试向量:发送帧01 03 00 00 00 0A,计算得到的CRC应为C5 CD,发送顺序是CD C5。如果你算出来不一样,说明代码里某个细节不对,拿这个向量一验便知。

4.4 单片机接收:别再用delay判帧了

很多朋友写单片机上的Modbus RTU从站或主站程序,喜欢“每收到一个字节,延时等待下一个字节,超时则判定一帧结束”。这个思路本身没毛病,但实现方式要谨慎。

如果在串口中断里做延时等待,会卡住其他中断;如果在主循环里靠轮询判断,又容易丢字节。更稳的做法是:

  1. 串口每收到一字节进中断,把字节放入环形缓冲区,同时记录当前时间;
  2. 用定时器或者系统tick实现3.5字符时间的帧超时判断:收到第一个字节后启动定时,每收到一个新字节就重置定时,定时溢出说明一帧接收完成,进入帧处理逻辑;
  3. 如果MCU支持串口空闲中断(比如STM32的IDLE中断),可以用“DMA+空闲中断”的方案,一帧结束自动触发中断,CPU占用极低。

还要注意一个边界情况:如果收到的请求帧只有地址字节,后续没数据,要等满3.5字符时间后按错误帧丢弃。千万别把下一帧的开头当成这一帧的功能码或数据,否则整个状态机就乱了。这个细节写程序时十个人里有九个会忽略。

5. 硬件底子:线、电阻和地

5.1 时好时坏,先查终端电阻和接线

协议和软件都查完了,还是“时好时坏”,那就要怀疑物理层了。典型的症状是:设备面对面放能通,拉开30米就随机掉线;上午调试全好,下午莫名其妙超时。这种绝大多数是终端电阻和接线的问题。

RS485标准要求在总线的最远两端各接一个120欧姆终端电阻,用来吸收信号在末端的反射。只有两台设备且距离很近时,不接终端电阻也能凑合通;但距离一长、节点一多,没接终端电阻,信号就会反射,波形畸变,出现“第一帧能通、第二帧超时”这种诡异现象。

现场判断方法非常直接:断电后用万用表量A-B之间的电阻。正常情况下应该是约60欧姆,因为两个120欧姆电阻并联。如果量到120欧姆,说明只有一端有终端电阻;如果量出来是几千欧姆甚至开路,大概率两端都没接或者接线本身有问题。

5.2 偏置电阻:空闲电平别待在临界区

RS485总线空闲时,所有收发器都处于高阻状态,A-B之间的电压依靠偏置电阻来维持。标准规定空闲时A相对B的电平至少要有200毫伏以上,才能稳定地表示逻辑1。

很多工业级设备模块内部已经带了偏置电阻,但便宜的USB转485模块不一定有。如果你接上终端电阻后,量A-B间电压只有几十毫伏,那总线就处于“浮空”状态,稍微有点电磁干扰就会被从站误认为起始位,然后收到一堆乱码。

解决办法是在主站端加偏置电阻:A线上拉到+5V,B线下拉到GND,典型阻值用560欧姆或者470欧姆。加完之后再量一下空闲电压,确保A-B大于200毫伏。注意偏置电阻和终端电阻要搭配计算功耗,不要为了追求高电压把小电阻值用得过低,发热会受不了。

5.3 屏蔽层、共地、远离变频器

RS485虽然是差分传输,抗共模干扰能力比单端好了很多,但不代表可以随便接线。我现场遇到过好几个“怎么调都不行”的案例,最后都出在物理层这些看似不起眼的地方:

  • A/B接反。A接成了B,B接成了A,一根线都不通或者随机乱码。别迷信线色,上电前先用万用表确认设备端A/B定义。
  • 屏蔽层两端都接地。很多工程师认为屏蔽层接地越多越好,但两端接地会形成地环路,屏蔽层反而变成天线。正确做法是单端接地,一般接在主站侧的大地上。
  • 设备之间不共地。部分从站设备的24V电源和485通信口不做隔离,多个设备之间会通过地线形成回路。有条件就用带隔离的RS485收发器,或者保证所有设备电源的参考地真正连在一起。
  • 与变频器动力线平行走线。变频器的输出侧是强干扰源,485线最好单独穿管,实在要并行,至少间隔20厘米以上。现场有种说法叫“电柜里两根线走同一个线槽,通讯必抽风”,虽然夸张,但真实有效。

6. 把这些经验串成一份现场排查顺序

6.1 单站点读再联调,别上来就扫全站

到了现场,第一件事不是打开程序一顿操作,而是做减法。我的固定流程是:只保留一台设备在总线上,确认A/B接线、供电、屏蔽都正确,用Modbus Poll单站读取,先手动Read Once,读四五个已知寄存器。确认数据格式、字节序、地址偏移都没问题之后,再配置PLC程序,让PLC单独轮询这一台设备,观察至少十分钟。

这台设备稳定了,再接入第二台、第三台,逐步累加。最后再做整网联调。这个流程看似慢,实际是快的。我见过太多人上来就32台一起扫,结果要么是线序错误,要么是站号重复,最后根本分不清是哪一台设备带崩了全网,排查时间成倍增加。

6.2 从站异常码是设备在说话

Modbus从站收到无法处理的请求时,会返回异常响应帧。异常响应帧的功能码最高位置1,同时数据域里携带一个异常码。现场调试时一定要学会看这个码,它比任何猜测都靠谱。

异常码含义常见触发原因
01非法功能码设备不支持该功能码,例如没有输入寄存器区却用04读
02非法数据地址起始地址或寄存器数量越界,地址偏移算错的经典表现
03非法数据值写入值超出允许范围,比如频率写入负值
04从站设备故障设备内部异常,需要查看设备面板或日志

用Modbus Poll发一条请求,如果从站回了异常码02,别再纠结协议,赶紧回头算地址偏移。异常码本身就已经把问题范围缩短到非常小了。

6.3 我现在的现场检查顺序,你可以直接抄

根据多年踩坑经验,我整理了一份固定顺序,每次现场调试基本照着走,极少翻车:

  1. 检查线路供电、A/B线序、屏蔽层单端接地;
  2. 确认每台从站地址唯一且在1~247范围内,绝不允许0号设备存在;
  3. 三方核对波特率、校验位、停止位;
  4. 用Modbus Poll单站读取原始寄存器,记录字节序和字序组合;
  5. 校准地址偏移,用手册寄存器映射表逐一比对;
  6. 配置PLC或上位机程序,统一封装数据转换逻辑;
  7. 设置响应超时200~500毫秒,重试次数1次;
  8. 按全部从站正常响应时间总和的1.5倍估算轮询周期并留足预算;
  9. 单站联调稳定后再并站,逐步扩展到全网;
  10. 联调完成后,把所有参数、字序、偏移量整理成文档归档。

说实话,Modbus RTU这套协议本身一点都不难,难的是现场从来不按书本来。上面这些坑我基本都踩过,有些还不止一次。真要说有什么“保命”习惯,就是把每次调通的参数和设备寄存器的字序、偏移量记录下来,同型号的设备下次直接套用。我见过太多老哥每次到现场都重新猜一遍字节序,那才是真的“调一次崩一次”。把这些东西记下来,剩下的就是时间问题。

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

Pilot Shell 架构全景拆解:规则、钩子、技能与MCP如何协同工作

Pilot Shell 架构全景拆解&#xff1a;规则、钩子、技能与MCP如何协同工作 【免费下载链接】pilot-shell Professional context and harness engineering for Claude Code and OpenAI Codex. Build production-grade software with spec-driven development, TDD, persistent m…

作者头像 李华
网站建设 2026/9/18 4:55:50

低功耗策略的收益与风险平衡:嵌入式设计实践指南

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

作者头像 李华
网站建设 2026/9/18 4:55:22

Python时间序列分析:ACF与PACF计算逻辑与ARIMA定阶实战

简介&#xff1a;Python实现时间序列自相关图&#xff08;ACF&#xff09;与偏自相关图&#xff08;PACF&#xff09;的PDF教程&#xff0c;面向数据分析、统计建模及金融经济领域从业者&#xff0c;帮助读者理解时间序列模式并通过Python工具完成可视化。教程从ACF和PACF的基本…

作者头像 李华
网站建设 2026/9/18 4:54:30

做 Cohere 文档摘要,TaoToken 只提供 Base URL

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

作者头像 李华