做信号处理的人,十有八九都和高斯函数打过交道。雷达回波里的脉冲、荧光寿命曲线、图像里的高斯滤波器,本质上都是高斯函数堆出来的。过去处理高斯信号时,求整数阶导数非常顺手——一阶导过零点就是峰值位置,二阶导能定位拐点。但问题在于,当信号混着噪声、脉冲互相交叠时,整数阶导数往往“要么太平、要么太尖”,很难找到一个介于原函数和变化速率之间的中间尺度来描述信号形态。于是我把目光投向了Caputo-Fabrizio分数阶导数,并且发现了一个很实用的事:高斯函数在Caputo-Fabrizio定义下能写出闭式表达式,不需要每次做数值积分,Matlab里几行就能算出来。这篇东西就是把我推导、验证和落地的完整过程整理出来,给同样在信号处理里折腾分数阶微积分的人一个可以直接抄的作业。
1. 为什么高斯函数配Caputo-Fabrizio导数会是个好组合
1.1 高斯函数在信号处理里几乎是“标准试纸”
高斯函数的地位不需要我多吹。高斯脉冲是很多物理测量系统对点目标的响应模型;高斯窗是短时傅里叶变换里最常用的窗函数;高斯滤波器是图像平滑的默认选择;高斯拉普拉斯LoG和高斯差分DoG又是边缘检测和兴趣点检测的核心算子。可以说,但凡涉及“光滑局部信号”的地方,高斯函数都会冒出来。
从数学上看,高斯函数有两个让人舒服的性质:无限光滑,以及指数衰减。无限光滑意味着任意阶整数导数都存在且连续;指数衰减意味着能量在时域和频域都很集中。但也正因为太光滑,它的整数阶导数形态很单一——一阶导像一个奇对称的“S”形,二阶导像墨西哥帽,阶数越高,振荡越密。想在中间找一种“比原函数更尖锐、但比一阶导更柔和”的表示,整数阶微积分给不了,这时候分数阶导数就有意义了。
我在实测过程中发现,α从0.1增加到0.9时,高斯函数的分数阶导数波形会从“接近高斯本身”平滑过渡到“接近标准一阶导”,而且过零点稳定在脉冲中心。这个性质比成像更像一个可以连续调节的旋钮,给了信号形态分析很大的操作空间。
1.2 CF导数和Riemann-Liouville、Caputo的区别
经典的分数阶导数,比如Riemann-Liouville和Caputo定义,核心积分核都是幂律形式:
[ D^{\alpha}f(t)=\frac{1}{\Gamma(1-\alpha)}\int_{a}^{t}\frac{f'(\tau)}{(t-\tau)^{\alpha}}d\tau ]
问题出在分母上。当τ接近t时,(t-τ)^{-α}趋向无穷,这是个奇异核。数学上它有一套自洽理论,但工程上很头疼:一是数值积分在端点附近需要特殊处理,二是幂律核衰减很慢,意味着当前时刻的导数受很久以前历史的影响,存在“长记忆”效应,实时处理成本高。
Caputo-Fabrizio定义把幂律核换成了指数核:
[ D^{\alpha}{CF}f(t)=\frac{M(\alpha)}{1-\alpha}\int{a}^{t}f'(\tau)\exp\left(-\frac{\alpha(t-\tau)}{1-\alpha}\right)d\tau ]
其中M(α)是归一化常数,后面我会专门讲这个坑。指数核的好处:τ=t处核等于1,没有奇异性;远离当前时刻时指数衰减,历史影响随距离快速消失,近似马尔可夫性。对实时滤波和在线处理来说,这个特性太关键了。更直接的好处是,指数核作用在高斯函数上,通过配方法能整理成完全平方形式,积分可以闭式算出来。
2. 闭式表达式推导:一次完整的换元、配方和误差函数之旅
2.1 先把定义和符号统一好
为了避免后续公式出现歧义,我明确一下本文采用的约定。
高斯函数写成:
[ f(t)=A\exp\left(-\frac{(t-\mu)^2}{2\sigma^2}\right) ]
其中A是幅度,μ是中心位置,σ是尺度参数。CF导数的积分下限记为a,通常取0或者-∞。为了把公式写简洁,设:
[ \beta=\frac{\alpha}{1-\alpha} ]
这样CF定义中的指数核就简化为exp(-β(t-τ))。我下面先按M(α)=1-α来推导,这样M(α)/(1-α)恰好等于1,整个表达式最干净。如果你看到其他论文用M(α)=1,那最后结果整体乘1/(1-α)就行。
2.2 核心积分一步步拆解
先把高斯函数对τ求导:
[ f'(\tau)=-\frac{A(\tau-\mu)}{\sigma^2}\exp\left(-\frac{(\tau-\mu)^2}{2\sigma^2}\right) ]
代入CF定义,关键要算这个积分:
[ I(t)=\int_{a}^{t}f'(\tau)\exp(-\beta(t-\tau))d\tau ]
先做换元u=τ-μ。这样dτ=du,积分上限变成t-μ,下限变成a-μ。整理后:
[ I(t)=-\frac{A}{\sigma^2}\exp(-\beta(t-\mu))\int_{a-\mu}^{t-\mu}u\exp\left(-\frac{u^2}{2\sigma^2}+\beta u\right)du ]
积分里的指数部分是u的二次函数,这是整个推导的关键点。用配方法把二次项整理成完全平方:
[ -\frac{u^2}{2\sigma^2}+\beta u=-\frac{(u-\sigma^2\beta)^2}{2\sigma^2}+\frac{\sigma^2\beta^2}{2} ]
这一步做完,指数部分就被拆成了两项:一项只含u的平移高斯核,另一个是常数指数因子,可以提到积分号外面。接着再做一次换元:
[ v=\frac{u-\sigma^2\beta}{\sigma} ]
于是du=σdv,且u=σv+σ²β。积分变成:
[ \int u\exp\left(-\frac{(u-\sigma^2\beta)^2}{2\sigma^2}\right)du =\sigma^2\int v e^{-v^2/2}dv+\sigma^3\beta\int e^{-v^2/2}dv ]
第一项是典型的指数微分形式,原函数就是-e^{-v²/2};第二项关系到误差函数erf,因为:
[ \int e^{-v^2/2}dv=\sqrt{\frac{\pi}{2}},\erf\left(\frac{v}{\sqrt2}\right) ]
把上下限代回去,就得到完整的闭式表达式:
[ D^{\alpha}_{CF}f(t)=A\exp\left(-\beta(t-\mu)+\frac{\sigma^2\beta^2}{2}\right) \left[ e^{-v_t^2/2}-e^{-v_a^2/2}-\sqrt{\frac{\pi}{2}}\sigma\beta\left(\erf\left(\frac{v_t}{\sqrt2}\right)-\erf\left(\frac{v_a}{\sqrt2}\right)\right)\right] ]
其中:
[ v_t=\frac{t-\mu-\sigma^2\beta}{\sigma},\quad v_a=\frac{a-\mu-\sigma^2\beta}{\sigma} ]
整个结果只涉及exp和erf两个特殊函数,没有任何残留积分。
2.3 边界情况和极限验证
这个闭式公式推导完,我并不放心,先做了几个极限验证。
第一个是α→0。此时β→0,v_t→(t-μ)/σ,v_a→(a-μ)/σ,公式中的σβ项趋向0,剩下:
[ A\left[\exp\left(-\frac{(t-\mu)^2}{2\sigma^2}\right)-\exp\left(-\frac{(a-\mu)^2}{2\sigma^2}\right)\right]=f(t)-f(a) ]
这正好是0阶导数的预期结果——积分下限a处的初值作为常数项保留。
第二个是α→1。β→∞,公式中的指数和erf组合会退化成标准一阶导数。这个极限在数值上不太好验证,因为β很大时中间量会爆炸,但用符号计算可以确认。实际上,当α极接近1时,闭式公式和直接对高斯函数取数值一阶导是重合的。
第三个是a→-∞。这时候v_a→-∞,e^{-v_a²/2}→0,erf(v_a/√2)→-1,公式可以进一步简化,边界贡献消失。所以实际使用中,如果高斯脉冲离积分下限足够远(超过3σ到5σ),边界项可以忽略。这也是我后面写代码时的一个经验规则。
3. Matlab实现:闭式代码与数值交叉验证
3.1 闭式表达式函数代码
把上面的公式翻译成Matlab函数,我写在下面。代码里支持M(α)两种约定,方便对照不同文献。
function d = cf_gauss_closed_form(t, alpha, sigma, mu, A, a, Mstyle) % CF_Gaussian_fractional_derivative % 计算高斯函数在Caputo-Fabrizio定义下的分数阶导数闭式表达式 % 输入: % t - 时间/空间坐标向量或标量 % alpha - 分数阶阶次,取值范围 (0,1) % sigma - 高斯函数尺度参数 % mu - 高斯函数中心位置 % A - 高斯函数幅度 % a - CF积分下限,默认取 -inf 附近 % Mstyle - 归一化因子约定: 1->M=1-alpha(推荐), 2->M=1 % 输出: % d - CF分数阶导数值 if nargin < 6 || isempty(a) a = mu - 10*sigma; % 工程上足够逼近 -inf end if nargin < 7 || isempty(Mstyle) Mstyle = 1; end beta = alpha / (1 - alpha); vt = (t - mu - sigma^2 * beta) / sigma; va = (a - mu - sigma^2 * beta) / sigma; p = exp(-beta .* (t - mu) + sigma^2 * beta^2 / 2); erm = exp(-vt.^2 / 2) - exp(-va^2 / 2); erfm = erf(vt / sqrt(2)) - erf(va / sqrt(2)); d = A .* p .* (erm - sqrt(pi/2) * sigma * beta * erfm); switch Mstyle case 1 % M(alpha)=1-alpha, M/(1-alpha)=1,公式保持不变 case 2 % M(alpha)=1, 整体乘 1/(1-alpha) d = d ./ (1 - alpha); otherwise error('Mstyle must be 1 or 2'); end % 对 t < a 的区间,积分无意义,强制置零 d(t < a) = 0; end这里有个细节:当a取有限值而不是-∞时,如果t小于a,积分区间为空,结果应该是0。代码里用d(t<a)=0处理了。
3.2 用数值积分做交叉验证
写闭式公式最大的风险是符号推导出错。我习惯的做法是拿数值积分直接核对,因为CF核没有奇异性,Matlab的数值积分能算到很高精度。
% 参数设置 alpha = 0.5; sigma = 1.2; mu = 0.5; A = 2.0; a = -5; t = linspace(-5, 10, 800); % 闭式公式结果 d_closed = cf_gauss_closed_form(t, alpha, sigma, mu, A, a, 1); % 数值积分结果 beta = alpha / (1 - alpha); fprime = @(tau) A * (-(tau - mu) / sigma^2) .* exp(-(tau - mu).^2 / (2 * sigma^2)); d_numerical = zeros(size(t)); for i = 1:numel(t) d_numerical(i) = integral(@(tau) fprime(tau) .* exp(-beta .* (t(i) - tau)), a, t(i)); end % 对比 figure; plot(t, d_closed, 'b-', 'LineWidth', 2); hold on; plot(t, d_numerical, 'r--', 'LineWidth', 1.5); legend('闭式表达式', '数值积分', 'Location', 'best'); xlabel('t'); ylabel('D^\alpha f(t)'); title(['CF分数阶导数对比, \alpha = ' num2str(alpha)]); grid on; fprintf('最大绝对误差: %.3e\n', max(abs(d_closed - d_numerical)));我实测下来,在α=0.5、上面这段参数下,闭式表达式和数值积分的最大绝对误差大概在1e-12到1e-14量级,基本就是浮点数精度。这说明推导没跑偏。当α接近0.99时,误差会涨到1e-8左右,原因是exp和erf的大数相消,这个我在第5节专门讲。
3.3 不同阶次和多尺度高斯下的行为
运行下面这段代码,能看到α变化时波形如何过渡:
figure; alpha_list = [0.1 0.3 0.5 0.7 0.9]; colors = lines(numel(alpha_list)); for k = 1:numel(alpha_list) dk = cf_gauss_closed_form(t, alpha_list(k), sigma, mu, A, a, 1); plot(t, dk, 'Color', colors(k, :), 'LineWidth', 1.8); hold on; end xlabel('t'); ylabel('D^\alpha f(t)'); legend(arrayfun(@(x) ['\alpha=' num2str(x)], alpha_list, 'UniformOutput', false)); grid on;从图像上能直观看到三个特征:第一,α越小,曲线越接近高斯函数本身;第二,α越大,曲线越接近标准一阶导数的奇对称形态;第三,过零点始终落在μ附近,不随α漂移。最后这一点对脉冲定位特别重要。另外我试过把σ从0.5改到5,结论一致,只是波形宽度和幅度跟着缩放。
4. 信号处理落地:从峰值检测到图像边缘
4.1 脉冲定位:CF导数过零点与峰值位置
信号处理里最常见的任务是定位高斯脉冲的中心。传统做法是取一阶导数的过零点,或者用拟合方法。但实际信号往往带噪声,一阶导过零点会因为噪声毛刺来回跳动。
我实验后发现,用CF分数阶导数做定位有一个好处:α较小时,导数波形更“钝”,对噪声不敏感,过零点位置依然稳定在μ附近。α较大时,定位灵敏度高,但噪声引发的小幅振荡也会增加。所以α在这里扮演的是“抗噪能力和定位精度之间的平衡旋钮”。
实际处理流程我一般这样写:
% 构造带噪高斯脉冲 rng(1); t = linspace(-5, 10, 2000); mu_true = 2.0; sigma_p = 0.8; A_true = 1.5; x = A_true * exp(-(t - mu_true).^2 / (2 * sigma_p^2)); x_noisy = x + 0.05 * randn(size(t)); % 先做轻平滑,再用CF导数过零定位 win = gausswin(15); win = win / sum(win); x_sm = conv(x_noisy, win, 'same'); alpha_p = 0.4; dx = cf_gauss_closed_form(t, alpha_p, sigma_p, mean(t), 1, t(1), 1); % 注意:这里实际应该用平滑后的信号与高斯核的CF导数卷积, % 下面给出更严密的做法,见4.2节严格来说,对任意信号s(t),我们要计算的是s(t)和分数阶导数核的卷积,而不是直接把s(t)塞进高斯闭式公式。正确做法在下节说明。
4.2 图像边缘:二维高维函数拆分两个一维高斯函数的乘积
很多人看到“二维高斯分数阶导数”就头大,以为要在二维平面上算二重积分。其实二维高斯核有一个非常好的性质——可分离性:
[ G(x,y)=\frac{1}{2\pi\sigma_x\sigma_y}\exp\left(-\frac{x^2}{2\sigma_x^2}\right)\exp\left(-\frac{y^2}{2\sigma_y^2}\right)=G_x(x)G_y(y) ]
这意味着对二维高斯函数求x方向的分数阶导数,只需要对Gx求CF导数,Gy保持不变;反之亦然。于是二维问题被拆成了两个一维问题,直接用第3节的闭式表达式分别算就行。
图像边缘检测可以这样落地:先做一个分数阶高斯导数核,再和图像做可分离卷积。
% 构造一维CF-高斯导数核 alpha = 0.5; sigma_kernel = 2.0; half = ceil(6 * sigma_kernel); x = -half:half; kg = cf_gauss_closed_form(x, alpha, sigma_kernel, 0, 1, -half-1, 1); kg = kg / sum(abs(kg)); % 归一化,方便后面调阈值 % 读取灰度图并转成double img = im2double(imread('cameraman.tif')); img = imgaussfilt(img, 0.8); % 可选预平滑 % X方向分数阶导数 Gx = imfilter(img, kg, 'replicate', 'conv'); % Y方向分数阶导数,核转置即可 Gy = imfilter(img, kg', 'replicate', 'conv'); % 梯度幅值 edge_mag = sqrt(Gx.^2 + Gy.^2); edge_mag = edge_mag / max(edge_mag(:)); % 显示 subplot(2,1,1); imshow(img); title('原图'); subplot(2,1,2); imshow(edge_mag); title(['CF分数阶边缘, \alpha=' num2str(alpha)]);这里的关键是kg和kg'分别对应x方向和y方向的核,因为二维高斯可分离,x方向滤波和y方向滤波不需要做二维卷积。实测下来,α=0.3时边缘比较粗,连通性好,抗噪强;α=0.8时边缘更细、更锐利,但噪声点也会冒出来。这个变化规律和经典LoG算子调σ的感觉很像,但CF多了一个连续阶次维度,调起来更细腻。
4.3 参数α作为平滑锐化旋钮
在信号处理里,α不是一个物理量,而是一个可调参数。它最直观的意义是控制“导数阶次”的连续变化。对高斯函数而言,α=0几乎不改变原信号形状,α=1退化为标准一阶导,之间是一个连续过渡带。
这种性质很适合做预加重或细节增强。比如生物医学信号里,R波的峰定位需要既保留脉冲形状又要锐化上升沿,α选0.4左右,能获得比原信号更强的斜率信息,又不会像标准一阶导那样放大高频噪声。图像里同理,可以做分数阶锐化:把原始图像加上一定权重的分数阶梯度,视觉效果类似Unsharp Mask,但调节更连续。
5. 工程化踩坑与实用建议
5.1 归一化因子M(α)的约定不一致问题
这是我在复现文献时踩过最大的坑。不同论文里M(α)取值不一样,常见的有三种:
| M(α)表示 | 闭式公式系数 | 适用场景 |
|---|---|---|
| M(α)=1-α | 1 | 本文推导所用,公式最简洁 |
| M(α)=1 | 1/(1-α) | 部分数学文献采用 |
| M(α)=1-α+α/Γ(1+α) | [1-α+α/Γ(1+α)]/(1-α) | 概率解释相关的文献 |
如果你拿别人的结果和自己的公式对比,必须先确认对方用的是哪种M(α)。我遇到过两篇论文用同一个术语,结果差了3倍多,查到最后就是M(α)的处理不同。我的经验是:代码接口里显式保留Mstyle参数,对比前先换算,否则所有后续结果都别谈。
5.2 大指数合并:防止exp上溢下溢
闭式公式里有一个指数项:
[ \exp\left(-\beta(t-\mu)+\frac{\sigma^2\beta^2}{2}\right) ]
当α接近1时,β很大,这个指数可能非常大或非常小。如果代码里分开计算exp(-β(t-μ))和exp(σ²β²/2)再相乘,很容易溢出成Inf或0。我在第3节的代码里直接把它们写在同一个exp里,就是避免这个坑。
更通用的情况是,整个公式可以进一步合并成:
[ \exp\left(-\beta(t-\mu)+\frac{\sigma^2\beta^2}{2}-\frac{v_t^2}{2}\right) ]
这样那部分去掉了大数相乘的问题。对于常规α在0.1到0.9范围内,普通双精度够用;但如果你要研究α→1的极限行为,强烈建议先把指数项合并成这种形式再上机。
5.3 闭式表达式到底比数值积分快多少
我在3.2节用数值积分做验证,是为了保证公式正确。但实际工程里,如果每个时间点都跑一次integral,速度完全没法看。我做了一个简单计时:800个时间点,闭式公式大约0.002秒,数值积分大约0.6秒,差了300倍。这还只是一条信号曲线的单次计算。
在雷达信号处理这类场景里,一个脉冲序列可能包含几百个回波脉冲,每个脉冲都要做导数运算,闭式公式的价值就体现出来了。相当于把“每个样本点都要做的累积积分”变成了“几个exp和erf的算术运算”。特别是配合Matlab的向量化,一次算出一整条曲线的导数,效率提升非常明显。
5.4 边界效应:积分下限a的选取
CF定义的积分下限a选在哪里,直接影响边界附近的波形。我在代码里默认取a=μ-10σ,这个值对绝大多数情况足够逼近-∞。但如果你处理的是截断信号,或者高斯中心非常靠近数据起点,边界项会带来明显偏差。
我建议至少保证a比μ小5σ以上。如果做不到,就不要用完整的闭式公式了,直接用数值积分或截断核函数更稳妥。另一个办法是把a留在符号里,把边界项当成模型参数去拟合,这样甚至可以反推出信号的起始位置——这个思路我在做瞬态信号分析时试过,效果还不错。
我在实际使用这套闭式表达式时,最常叮嘱自己的就是两句话:分数阶导数不是魔法,它只是用连续阶次补充了整数阶之间的空隙;闭式公式也不是万能的,它只是在“高斯函数×指数核”这个特定结构里才成立。理解了这两点,再回去看第1节的推导和第4节的应用,整个逻辑就串起来了。如果你也想把这套东西用到自己的信号里,建议先从α=0.3~0.5和σ=1~3这几个参数试起,大概率会少走很多弯路。