news 2026/9/20 14:52:53

MATLAB仿真中EbN0与SNR换算全解析:让BER曲线不再偏移

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB仿真中EbN0与SNR换算全解析:让BER曲线不再偏移

先交代一个我见过太多人翻车的场景:在MATLAB通信仿真里折腾了一整天BER曲线,明明调制方式、信道编码、解调判决都检查过好几遍,曲线却始终和理论值差着那么一口气。有时候是整体右偏1dB,有时候是斜率对但横坐标平移了3dB,还有时候更隐蔽——只有高信噪比的点对不上。十次里有八次,问题都出在EbN0和SNR的换算上。

我最早也在这个坑里爬了很久,后来把公式链路从头到尾推了一遍,又把MATLAB里BERTool、awgn这些工具的实际行为扒了底朝天,才算把这事彻底理顺。这篇就把完整的推导过程、换算公式、MATLAB实操模板,以及我踩过的坑一并整理出来。无论你是刚开始做通信链路仿真,还是已经写了很久但偶尔还犯迷糊,这篇都值得存下来。

1. 先把概念掰扯清楚:EbN0和SNR到底分别衡量什么

很多同学用错公式,不是因为数学不好,而是从一开始就没分清EbN0和SNR这两件事的本质区别。先花点时间把定义捋清楚。

1.1 从单位维度看:一个词根之差,差出十万八千里

EbN0里,Eb是每个信息比特携带的能量,单位是焦耳/比特(J/bit);N0是噪声功率谱密度,单位是瓦特/赫兹(W/Hz)。两者相除之后,单位对消,得到一个无量纲的比值,工程上习惯换算成dB来用。

SNR则是信号总功率和噪声总功率之比,功率的单位是瓦特(W)。这里就出现了一个关键差异:EbN0是“比特维度”的指标,SNR是“功率维度”的指标。功率信号经过一段时间才能累积成能量,所以从EbN0换算到SNR,必然要引入时间/速率的因素。具体来说,就是符号速率、调制阶数、编码率、甚至采样率这些参数。

你可以用运输来类比:EbN0好比每件包裹平均消耗的油量,SNR好比整辆卡车当前的百公里油耗。前者用来评价“运输效率”,后者用来评价“车辆当前工况”——不同卡车的包裹数量不一样,直接拿百公里油耗来评价运输效率显然不公平。

1.2 为什么通信系统更爱用EbN0而不是SNR

既然SNR更直观,为什么在MATLAB的BERTool里,横轴几乎永远都是EbN0?

因为EbN0天然“剥离”了调制方式和编码速率的影响,让你可以站在信息传输效率的角度比较不同方案的优劣。举个例子,BPSK和QPSK在相同EbN0下,理论误码率是几乎相同的(这里指没有编码、按信息比特对齐),但两者如果按SNR对齐,QPSK的SNR会比BPSK高3dB。这3dB纯粹来自每个QPSK符号携带了2个比特,并不意味着QPSK在“能量效率”上更差。

所以做通信系统设计时,首要指标是EbN0;而到了实际仿真中,AWGN信道添加噪声时,MATLAB的awgn函数要的是SNR。这就逼着你必须在两者之间做换算。

1.3 转换的本质:把“比特域指标”翻译成“波形域参数”

在仿真里,你生成的是波形(符号序列、采样序列),叠加的也是波形域的噪声,此时系统关心的是信号功率和噪声功率之比。因此,从EbN0推导SNR,本质上就是沿着“信息比特→编码比特→符号→实际采样波形”这条链路,一步步把“每比特能量”翻译成“每个采样点的功率关系”。

理解了这个翻译过程,后面公式里的每个因子就不会再是“背下来的死公式”,而是可以自己推导出来的活工具。这也是这篇文章和网上那些只丢个公式的教程最大的区别。

2. 从EbN0到SNR的完整推导链路

建议打开MATLAB,边看边算。这一章的每个因子都会用一个具体例子跑一遍数字,你跟着按计算器也行,在MATLAB命令行里敲也行。

2.1 第一个核心因子:调制阶数 k = log2(M)

先从前向开始。一个M阶调制符号承载 k = log2(M) 个比特,比如:

  • BPSK:M=2,k=1
  • QPSK:M=4,k=2
  • 16QAM:M=16,k=4
  • 64QAM:M=64,k=6

每个符号携带的总能量 Es(焦耳/符号)必然等于 k 个比特的能量之和。如果每个比特的能量是 Eb,那么:

Es = k × Eb

两边同时除以N0,并换算到dB:

Es/N0 (dB) = EbN0 (dB) + 10×log10(k)

这就是第一个校正因子。看数字更直观:QPSK的k=2,所以转换时要加3.01dB;16QAM的k=4,加6.02dB。

这个因子的物理直觉是:同样大小的Eb,符号级能量越高,符号越好检测,所以符号级SNR比比特级SNR高,多出来的部分正好是每符号承载的比特数。

2.2 第二个核心因子:编码率 r 进入的时机

接下来是信道编码。这里特别容易搞混,所以我先明确一个术语约定:

  • EbN0里的Eb,在绝大多数论文、书籍和BERTool的横轴里,指的是信息比特的能量。
  • 经过编码率为 r 的信道编码后,每1个信息比特会变成 1/r 个编码比特。

如果每个调制符号承载 k 个“编码比特”,那么每个符号里实际包含的有效信息比特数是 k×r。因此,以信息比特能量为参照的符号能量:

Es = k × r × Eb

于是:

Es/N0 (dB) = EbN0 (dB) + 10×log10(k) + 10×log10(r)

举例:卷积码r=1/2配QPSK,10×log10(2)+10×log10(0.5)=3.01-3.01=0dB,也就是说这种配置下Es/N0 = EbN0。注意,这里的r是编码速率,一定小于等于1,所以10×log10(r)是负值,会削弱符号级SNR。

我再强调一次:如果你横轴表示的是“编码比特”的EbN0,那就不该乘r;但绝大多数场景包括BERTool默认都是“信息比特”的EbN0,所以这个r必须乘。这一步错了,曲线会整体偏移10×log10(1/r) dB。

2.3 第三个核心因子:过采样倍数N——最容易被遗忘的坑

完成调制和编码后,你有了待发送的符号序列。但实际仿真里,往往不会直接每个符号只给1个采样点。脉冲成型(比如rcosdesign升余弦滤波器)、上采样、以及后续的匹配滤波接收,都会引入“过采样倍数N”(每个符号对应的采样点数)。

这个因子为什么会影响SNR?要从噪声功率谱密度N0的原始定义说起。N0的单位是W/Hz,表示噪声在频域上的能量密度;实际叠加到带宽为B的噪声,总功率才是 N0×B。当你以采样率 Fs 来处理离散序列时,近似认为占用的带宽就是Fs,那么离散域噪声总功率就是 N0×Fs。

再看信号功率。符号速率为 Rs,每个符号能量Es,那么信号平均功率是 Es×Rs(瓦特)。于是在这个离散采样序列上,实际的功率信噪比是:

SNR_sample = (Es×Rs) / (N0×Fs)

把 Fs = N×Rs 代入:

SNR_sample = Es/N0 ÷ N

dB形式就是:

SNR_sample (dB) = Es/N0 (dB) - 10×log10(N)

如果把前面两个因子也拼进来,就得到完整公式:

SNR_sample (dB) = EbN0 (dB) + 10×log10(k) + 10×log10(r) - 10×log10(N)

这个式子就是我在手写仿真里最常用的换算公式,没有之一。

验证一次数字,防止记错:QPSK(k=2)、编码率r=1/2、过采样4倍,目标EbN0=5dB,那么:

SNR_sample = 5 + 10×log10(2) + 10×log10(0.5) - 10×log10(4) = 5 + 3.01 - 3.01 - 6.02 = -1.02 dB

也就是说,如果直接把过采样后的信号丢给awgn(sig, -1.02, 'measured'),对应的性能就是EbN0=5dB下的理论性能。

2.4 一表汇总:不同场景下的换算公式

为了方便对照,我把常见场景整理成一张表。使用前提是“接收端有理想的匹配滤波/相关接收”。

仿真场景换算公式(线性域)换算公式(dB域)
无编码、无过采样SNR = Eb/N0 × kSNR_dB = EbN0_dB + 10log10(k)
有编码、无过采样SNR = Eb/N0 × k×rSNR_dB = EbN0_dB + 10log10(k) + 10log10(r)
无编码、过采样NSNR = Eb/N0 × k / NSNR_dB = EbN0_dB + 10log10(k) - 10log10(N)
有编码、过采样NSNR = Eb/N0 × k×r / NSNR_dB = EbN0_dB + 10log10(k) + 10log10(r) - 10log10(N)

这里SNR_sample都指“离散采样序列的信号总功率/噪声总功率”,也就是awgn函数实际使用的那种SNR。

3. MATLAB实践:AWGN函数和BERTool里到底填哪个值

公式推完,接下来回答一个实际问题:MATLAB的awgn函数、BERTool工具,它们在内部到底拿这个SNR做了什么?不搞清楚,你填进去的数字就可能是“自以为对的”。

3.1 awgn函数的SNR参数是“序列总功率比”

先看最常用的awgn函数。文档里的定义很直接:对于输入信号x,它把噪声功率设置为:

P_noise = P_signal / 10^(snr/10)

其中P_signal要么由用户给(用'measured'的话就是x本身的实测功率),要么由’signalpower’参数指定。也就是说,awgn里的snr就是“离散序列总功率比”,和上一章公式里的SNR_sample是同一个定义。

有一点值得注意:如果x是复数基带信号,awgn会自动把噪声功率分成两部分,实部和虚部各占P_noise/2,也就是产生复高斯白噪声;如果x是实数信号,就只产生实噪声。这一点处理好了没问题,但如果你自己写加噪代码而不是用awgn,就非常容易在实部/虚部的功率分配上出错——具体在第4章展开。

3.2 BERTool内部是怎么换算的

BERTool的界面友好,但它背后做的事情经常被忽略。在BERTool里选择调制方式、编码率、EbN0范围后,点“Run”,它其实是调用了内置的仿真脚本来跑蒙特卡洛。如果你点击BERTool的“Generate Script”按钮,就能看到它生成的Matlab脚本。

关键点在于:BERTool内部默认“符号速率=采样速率”,也就是每个符号只给1个采样点,不做脉冲成型,也没有过采样。因此它在内部把EbN0换算成SNR时,只用了前两个因子:

SNR_BERTool = EbN0_dB + 10×log10(k) + 10×log10(r)

这一步对应的正是上表第二行。所以,BERTool跑出来一条BER vs EbN0的曲线;你自己写了完整的成形滤波+过采样链路,却把Bertool的SNR等价公式直接套进去,自然差出10×log10(N) dB。

这就能解释为什么很多人在论坛上问:“BERTool里BPSK在BER=1e-5时EbN0约9.6dB,但我自己仿真里把SNR设成9.6dB,误码率明显不对。”因为BERTool的9.6dB是EbN0,而你自己仿真里如果没过采样、无编码、BPSK,SNR才等于EbN0;一旦你加了2倍上采样,SNR就得减去3dB。

3.3 手写链路里最稳妥的加噪模板

基于上面的分析,我给自己定了一个规矩:手写仿真一律不用“先算SNR再喂awgn”这种间接方式,而是直接按N0来加噪。这样每一步物理意义都清晰,出问题时也容易查。

复基带信号加噪模板如下:

% 参数:EbN0_dB 目标信息比特信噪比 % 符号参数 M = 4; % QPSK k = log2(M); r = 1/2; % 编码率 N = 4; % 过采样倍数 Fs = 1e6; % 采样率 Rs = Fs / N; % 符号速率 % 信号生成(示意) data = randi([0 1], numBits, 1); encData = convenc(data, trellis); % 编码 modSym = pskmod(encData, M, 0, 'gray'); % 调制 txSig = upsample(modSym, N); % 过采样 txSig = conv(txSig, rrcFilter, 'same'); % 脉冲成型 % 换算 N0 EbN0_lin = 10^(EbN0_dB / 10); Es = k * r * EbN0_lin; % 信息比特能量换算到符号能量 N0 = 1; % 归一化N0,Es是相对值 noiseVarPerDim = N0 * Fs / 2; % 复信号每个维度噪声方差 % 加噪 noise = sqrt(noiseVarPerDim) * (randn(size(txSig)) + 1j*randn(size(txSig))); rxSig = txSig + noise;

这模板里我把N0归一化为1,然后用N0×Fs/2作为实部/虚部的噪声方差。为什么是Fs/2?因为复噪声总功率是N0×Fs,实部和虚部各分一半。这里没有用awgn,但本质上和awgn是一致的。用这种方式,你不需要额外计算“SNR_sample”,只需保证Es的相对关系正确即可。

如果你还是习惯用awgn,那就要用第2章的公式算好SNR_sample,然后:

snr_dB = EbN0_dB + 10*log10(k*r/N); rxSig = awgn(txSig, snr_dB, 'measured');

两种方式等价,前者更偏物理实现,后者更简洁。我自己调试时优先用前者,因为可以直接观测噪声功率,排查起来少一层换算。

4. 最容易翻车的四类坑:逐个拆解和验证

公式推导清楚了,模板也有了,为什么实际仿真里还会翻车?下面这四类坑是我见过最多、也亲自踩过的,按踩坑频率排序。

4.1 复基带与实信号:噪声功率的3dB玄机

这个坑在BPSK仿真里特别常见。BPSK的符号映射通常是±1,当作复数基带信号,也就是实部有信号,虚部是0。加噪声时,如果按照实信号处理,只给实部加方差为σ²的高斯噪声;如果是复基带信号,正确的做法是实部和虚部各加方差为σ²/2的噪声,合成一个复噪声总功率σ²。

我见过有同学自己写加噪时这么写:

noise = sqrt(N0) * randn(size(sig)); % 实噪声,给复数信号加

这里的问题在于:对复数信号只加实噪声,相当于噪声只存在于一维,而正确的复噪声应该分布在两维。结果就是实际噪声功率只有一半,信噪比凭空涨了3dB——BER曲线会整体“偏左”3dB,看起来性能比理论还好,但你很清楚这不可能是真的。

正确写法有几种:

% 复基带信号,N0为单边噪声谱密度 noise = sqrt(N0/2) * (randn(size(sig)) + 1j*randn(size(sig)));

或者干脆用awgn:

rxSig = awgn(sig, snr, 'measured'); % 对复信号会自动两维分配

检测自己有没有踩这个坑的方法很简单:连续跑两个相同参数的BPSK仿真,一次把信道写成实高斯,一次写成复高斯,看BER曲线是不是正好差3dB。正常情况应以复高斯为准。

4.2 过采样和脉冲成型带来的带宽因子

第二个高频坑就是第2.3节讲过的过采样因子。很多同学写了rcosdesign做脉冲成型,然后滤波后直接:

rxSig = awgn(txSig, snr_dB, 'measured');

其中snr_dB还是没考虑过采样倍数的理论值。结果就是BER曲线整体偏移10log10(N) dB,N是上采样倍数。2倍上采样偏3dB,4倍偏6dB。

更隐蔽的是,有些人即使加了滚降系数α,也不知道带宽到底怎么算。实际上,加噪时只要按“采样率Fs对应噪声带宽”来理解,就不会错。因为离散仿真中,你以Fs采样时,能表示的最高频率分量就是±Fs/2,噪声总功率就是N0×Fs。你的滚降滤波器本身并不会改变你加噪时该用的总噪声功率,它只是改变了信号带宽和滤波后的噪声形状。

所以我会在仿真脚本里一直保留“N=上采样倍数”这个变量,核对SNR时心里始终绷着一根弦:凡是用awgn函数,就必须问自己一句“这个SNR是采样序列级的吗?跟我的Es/N0差了几个因子?”

4.3 编码率 r 放错位置:信息比特和编码比特的错位

编码率这个坑,我见过不止一次。有人做QPSK + 1/2卷积码,查表得到“编码后的符号级SNR应该比EbN0高3dB”,于是他给awgn填了EbN0+3dB。结果BER曲线比理论差很多。为什么?因为他把“编码比特”当成了“信息比特”。

再捋一遍:QPSK的k=2,携带2个编码比特,这2个编码比特只对应1个信息比特(r=1/2)。所以Es/N0相对于“信息比特EbN0”的增益是10log10(k×r)=10log10(1)=0dB,而不是3dB。漏乘r=1/2直接多出3dB的误差。

这个错位特别容易在“查表查一半”的时候发生。我的建议是:在做仿真前,明确写下你这张BER曲线的横轴EbN0到底是“什么比特的EbN0”。如果是信息比特,符号能量公式必须是Es=k×r×Eb;如果是编码比特,把r去掉也可以,但你这条曲线和BERTool、论文里的曲线大概率对不上,因为它们横轴几乎都是信息比特EbN0。

4.4 BERTool结果和手写代码对不上

第四个坑前面提到过,但值得单独摆出来:BERTool默认没有过采样,也不会做脉冲成型,它内部等价于“每符号1个采样点”。你在外部脚本里做了4倍过采样,看到BERTool里SNR那一栏显示的值,就直接拿过来用,那必偏。

正确做法是:要么在BERTool生成的脚本基础上,把过采样、滤波环节加进去,并同步更新SNR换算;要么把手写链路的“等效SNR”换算成BERTool口径再对比。我偏向后者,因为BERTool更适合做参考基准,手写链路则要贴近真实系统。

还有一个需要注意的细节:BERTool里的“EbN0”默认是包含编码增益的“信息比特EbN0”。如果你拿一个无编码的BPSK手写链路去和BERTool里“BPSK”比较,两者对齐没问题;但如果你拿“编码QPSK”和BERTool里“QPSK”比,必须确认编码率设置一致——否则横轴看着是EbN0,实际却差了一个编码率因子。

5. 验证与自查:怎么判断你的换算到底对不对

很多同学改完公式,改完代码,依然心里没底。这里分享一套我常用的自查方法,按顺序跑一遍,基本能把换算是否正确钉死。

5.1 第一步:用理论BER曲线反向锁定换算关系

以最简单的情况开始:无编码、无过采样、BPSK/QPSK。BPSK在AWGN下的理论误码率为:

BER = 0.5 × erfc(sqrt(EbN0))

这里的EbN0是线性值。MATLAB里可以这样画理论曲线:

EbN0_dB = 0:0.5:12; EbN0_lin = 10.^(EbN0_dB/10); BER_theory = 0.5 * erfc(sqrt(EbN0_lin)); semilogy(EbN0_dB, BER_theory);

然后跑一个最简单的BPSK蒙特卡洛仿真,随机生成大量比特,映射到±1,加复高斯噪声,硬判决,统计误码率,和理论曲线叠在一起画。如果你的换算对了,实测点会紧贴理论曲线;如果整体偏了3dB,大概率是噪声功率分配的问题;偏了10log10(N),则是过采样因子没处理。

QPSK也可以直接用BPSK理论曲线验证,因为QPSK的2个正交支路各自等价于一个BPSK,在信息比特EbN0口径下BER理论相同。这一步要是过了,说明你的基本链路和换算框架是健康的。

5.2 第二步:加噪后实际测量SNR,再反向推回EbN0

理论曲线只能告诉你“对不对”,不能告诉你“差在哪”。要定位问题,最有效的办法是直接在时域上测SNR。

% 加噪前 P_signal = mean(abs(txSig).^2); % 加噪后 noise = rxSig - txSig; P_noise = mean(abs(noise).^2); SNR_measured_dB = 10*log10(P_signal / P_noise);

拿着这个实测的SNR_measured_dB,再用你的换算公式反推EbN0:

EbN0_estimated_dB = SNR_measured_dB - 10*log10(k*r/N); disp(['EbN0估计值: ', num2str(EbN0_estimated_dB), ' dB']);

如果反推出来的值和目标EbN0一致,说明加噪那一步没问题。如果对不上,就说明问题出现在信号功率计算、噪声方差设置或者公式本身。

需要注意,如果做了脉冲成型和匹配滤波,测量SNR的位置会影响结果。建议在“加噪点”测量信号与噪声总功率,而不是在匹配滤波输出端测;后者的噪声已经被滤波器整形,测量口径不同。

5.3 第三步:从易到难逐级加模块,每加一级都对一次曲线

这也是我屡试不爽的工程方法。不要一上来就写“16QAM + LDPC + 升余弦 + 多径信道”,而是从最简单的配置出发,每加一个模块,就重新对比一次理论曲线。

我给自己的标准流程是:

  1. BPSK,无编码,无过采样,验证理论BER。
  2. QPSK,无编码,无过采样,验证同理论曲线且SNR+3dB。
  3. QPSK + 编码率r,无过采样,验证编码率因子。
  4. QPSK + 编码率r + 2倍过采样,验证过采样因子。
  5. 换成16QAM/64QAM,注意星座平均能量归一化。
  6. 再加脉冲成型、信道均衡等模块。

每上升一级,都只引入一个新变量。这样任何一个环节出错,都能立刻定位到是哪一步加的因子不对。这个方法看起来慢,但实际调试时间反而最少。

5.4 关于QAM平均符号能量的补充提醒

提到16QAM/64QAM,还有一个容易忽略的细节:这些高阶QAM的星座点能量并不相等。计算Es时要用全星座的平均能量,而不是拿某个角点的能量来代替。

constellation = qammod(0:M-1, M, 'gray'); Es = mean(abs(constellation).^2);

如果你用MATLAB的qammod,并且想直接让平均功率归一化为1,可以加‘UnitAveragePower’参数:

constellation = qammod(0:M-1, M, 'gray', 'UnitAveragePower', true);

归一化之后,星座点能量均匀缩放,Es/N0和EbN0的换算关系不会变,但你在手算时如果拿原始星座坐标算能量,单位就乱了。所以我在写脚本时都会先打印一下Es的值,确保和自己手算一致。

最后说个我自己的习惯。每次写完新的仿真链路,我会先把所有理论曲线和实测曲线的对比图保存成一个PNG,再把换算公式写在代码注释的头部,比如“此脚本中SNR_dB = EbN0_dB + 10log10(k*r/N)”。这样哪怕三个月后翻出这段代码,我也不用重新推导一遍。编码率、过采样倍数、调制阶数、是否做了脉冲成型,这四个参数写在注释里,能省下大量的返工时间。这个习惯也推荐给你,通信仿真这种东西,最怕的从来不是数学难,而是细节记不清。

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

海底鱼类检测数据集:VOC/COCO/YOLO三格式+YOLO11训练脚本

简介:面向目标检测与水下视觉方向的开发者,这份海底鱼类检测数据集可有效支撑水下机器人、海洋牧场等场景的鱼类识别与监测项目。数据涵盖浅海珊瑚礁、深海沉船周边、沙底海草区等多样环境,并特意加入遮挡与严重遮挡样本,统一标注…

作者头像 李华
网站建设 2026/9/20 14:48:51

PyAutoGUI桌面自动化完全指南:鼠标键盘操作与图像识别避坑

简介:这是一份Python桌面自动化入门教程,聚焦pyautogui模块在鼠标与键盘模拟方面的应用,适合需要开展UI自动化测试、批量重复操作或桌面应用脚本开发的开发者,也可作为自动化测试初学者的上手参考。PDF文档以实例为主线&#xff0…

作者头像 李华
网站建设 2026/9/20 14:48:48

SpringBoot+Vue新闻推荐系统实战:从开发到Docker部署

简介:本资源是一套完整的新闻推荐系统毕业设计项目,面向计算机专业本科生及全栈开发初学者,解决前后端分离架构下推荐功能落地的实战难题。压缩包共742个文件,14.82MB,涵盖90个Java后端核心类(含SpringBoot…

作者头像 李华
网站建设 2026/9/20 14:46:55

ESP32 + MAX30102 实战:从传感器原理到心率血氧监测全解析

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

作者头像 李华
网站建设 2026/9/20 14:45:39

销售团队CRM选型与落地实战:数据清洗、权限设计与自动化流程

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

作者头像 李华
网站建设 2026/9/20 14:44:06

Agent 工具 30+ 上下文混淆?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 …

作者头像 李华