MATLAB 工程实验:从相邻块均值手写重叠 Allan 偏差,数清有效项

前文只剩批次摘要怎么合并方差:用 n、mean、M2 保留批间差异处理总体描述方差;本篇关注不同平均时间下的相邻块差分。两者的用途与分母不同,不能把普通标准差按窗口长度改名为 Allan 偏差。

把陀螺仪的速率记录画成 Allan 曲线并不难,难点常出在三个细节:输入究竟是速率还是角度;两个平均块到底相隔几个样本;最末端还剩几个有效差分项。本实验直接从等间隔速率样本构造相邻块均值差,再用结构不同的双循环参考检查结果。

完整 demo 只使用基础 MATLAB。两条随机曲线用于观察有限样本行为;它们不构成传感器噪声的精确分类器,也不输出器件等级或可信区间。

1. 输入是速率/频率,不是角度/相位

令 x_1,...,x_N 为等间隔采样的速率或频率,周期 Ts 秒。示例将其解释为 deg/s,输出 Allan 偏差也为 deg/s;若输入为归一化的分数频率偏差,则输入和输出都是无量纲。不要把角度序列直接放入本函数后仍按速率偏差解释。

输入须为实数有限 double 向量,长度 2~20000;不接收 NaN/Inf。非零幅值范围为 [1e-250,1e6],允许精确零;Ts 必须为 double 标量,在 (0,1e6] 秒。平均因子 m 必须是 double 类型、元素取正整数值且严格递增的向量(不是 int32 等整数类型),满足 m<=floor(N/2)。行列向量均可,输出为表。

函数只有 Ts,没有逐点时间戳,因此不会自己验证等间隔采样。调用者必须根据设备时间戳、序号和丢包信息验证这一前提,不能删掉缺失值后把剩余记录当成连续等间隔数据。这里也不对角度做数值微分来掩盖输入类型差异。

2. 重叠发生在起点,两个块之间相隔 m 个样本

平均时间为 tau=m*Ts。定义长度 m 的块均值

x ˉ i ( m ) = 1 m ∑ j = 0 m − 1 x i + j . \bar{x}i^{(m)}=\frac{1}{m}\sum{j=0}^{m-1}x_{i+j}. xˉi(m)=m1j=0∑m−1xi+j.

一个差分项比较两个相邻、不重叠的块:第一个用 i ... i+m-1,第二个用 i+m ... i+2m-1。起点 i 每次只移动一个样本,所以不同差分项之间重叠。有效起点必须满足 i+2m-1<=N,故

K = N − 2 m + 1 , d i = x ˉ i + m ( m ) − x ˉ i ( m ) , σ A ( τ ) = 1 2 K ∑ i = 1 K d i 2 . K=N-2m+1,\quad d_i=\bar{x}_{i+m}^{(m)}-\bar{x}i^{(m)},\quad \sigma_A(\tau)=\sqrt{\frac{1}{2K}\sum{i=1}^{K}d_i^2}. K=N−2m+1,di=xˉi+m(m)−xˉi(m),σA(τ)=2K1i=1∑Kdi2 .

这与 NIST SP 1065,第 5.2.4 节公式 (10) 的频率形式一致:该书用 M 表示频率样本数,展开块内和时分母为 2*m^2*(M-2*m+1)。本文 N 就是其中的 M。K 是有效差分项数,重叠项一般相关,K 不是独立自由度。

主实现用 conv(x,ones(m,1)/m,'valid') 得到所有完整块的均值,再取间隔 m 的差。valid 不使用补零边缘;均匀卷积核反转不改变均值。conv 官方文档

实现先减去共同偏置 x(1),再用缩放 RMS 计算偏差:若 s=max(abs(d))>0,计算 s*sqrt(mean((d/s).^2)/2)。这避免先对很小的 d 直接平方造成典型下溢,不承诺任意动态范围都高相对精度。特别是采集时已被大偏置量化掉的微小变化,事后减偏置无法恢复。

3. 三个手算基准与故意错误做法

常数输入的两个块均值相同,任意合法 m 都应给 0。对每样本增加 s 的斜坡,两个块均值差为 s*m,所以偏差为 abs(s)*m/sqrt(2);若以物理时间斜率 b 表示,则为 abs(b)*tau/sqrt(2)。m=1 时应退化为 sqrt(mean(diff(x).^2)/2)。

取六个速率样本 [0,1,2,3,4,5],Ts=1 s:

m K d_i ADEV
1 5 全部为 1 0.707106781186548
2 3 全部为 2 1.414213562373095
3 1 仅一个 3 2.121320343559643

m=2 的滑动均值是 [0.5,1.5,2.5,3.5,4.5]。故意把它们错移一个位置,会得到差值 1,偏差仅 0.707106781186548;正确应比较 [2.5,3.5,4.5] 与 [0.5,1.5,2.5],差值为 2。demo 保留这个错误控制项,让索引错误可以被实际结果识别。

另一种常见错误是把频率样本数与积分后的相位边界数混为一谈。N 个速率积分成 phase=[0;cumsum(x)*Ts],应有 N+1 个边界值。用二阶相位差除以 tau,再取 RMS/√2,可得到同一结果;漏掉前置零就少了一个有效起点。作者归档测试保留这条独立参考,没有用缺首点的序列冒充完整相位边界。

4. 完整可运行源码

保存为 overlap_allan_demo.m,执行 overlap_allan_demo。所有输入在文件中生成,随机种子固定为 21,函数结束恢复调用前的随机状态。可选参数指定独立结果目录。

matlab 复制代码
function r=overlap_allan_demo(outDir)
% Equal-interval RATE/frequency input; not angle/phase input. No allanvar call.
if nargin<1,outDir=fullfile(fileparts(mfilename('fullpath')),'results');end
if ~exist(outDir,'dir'),mkdir(outDir);end
old=rng;restore=onCleanup(@()rng(old)); %#ok<NASGU>
rng(21,'twister');N=4096;Ts=0.01;
white=0.02*randn(N,1);walk=0.0008*cumsum(randn(N,1));
m=[2.^(0:10),1536,2048]';
w=overlap_rate_adev(white,Ts,m);v=overlap_rate_adev(walk,Ts,m);
hand=overlap_rate_adev((0:5)',1,[1;2;3]);
assert(max(abs(hand.ADEV-[1;2;3]/sqrt(2)))<1e-12,'ALLAN:Ramp','Ramp hand result differs.');
assert(isequal(hand.Terms,[5;3;1]),'ALLAN:Count','N-2m+1 count differs.');
constant=overlap_rate_adev(7*ones(6,1),1,[1;3]);
assert(all(constant.ADEV==0),'ALLAN:Constant','Constant rate must have zero deviation.');
% Wrong: adjacent overlapping means shifted one sample, rather than m samples.
means=conv((0:5)',ones(2,1)/2,'valid');
wrong=sqrt(mean((means(2:4)-means(1:3)).^2)/2);
assert(abs(wrong-1/sqrt(2))<1e-12 && abs(hand.ADEV(2)-sqrt(2))<1e-12,'ALLAN:Shift','Wrong-shift example differs.');
tab=table(m,w.Tau_s,w.Terms,w.ADEV,v.ADEV, ...
    'VariableNames',{'m','Tau_s','Terms','WhiteRateADEV','WalkRateADEV'});
writetable(tab,fullfile(outDir,'overlap_allan.csv'));
writetable(hand,fullfile(outDir,'overlap_allan_hand.csv'));
f=figure('Visible','off','Color','w','Position',[80 80 1100 800]);
subplot(2,2,1);plot((0:N-1)*Ts,white);grid on;xlabel('Time (s)');ylabel('Rate (deg/s)');title('One finite synthetic white-rate record');
subplot(2,2,2);plot((0:N-1)*Ts,walk);grid on;xlabel('Time (s)');ylabel('Rate (deg/s)');title('One finite rate random-walk record');
subplot(2,2,3);loglog(w.Tau_s,w.ADEV,'bo-',v.Tau_s,v.ADEV,'ms-');hold on;
loglog(w.Tau_s(end),w.ADEV(end),'rx','MarkerSize',12,'LineWidth',2,'HandleVisibility','off');
loglog(v.Tau_s(end),v.ADEV(end),'rx','MarkerSize',12,'LineWidth',2,'HandleVisibility','off');
grid on;xlabel('Averaging time (s)');ylabel('ADEV (deg/s)');legend('White-rate example','Rate-walk example','Location','best');
title('Red crosses: one term only; no noise-type verdict');
subplot(2,2,4);semilogx(w.Tau_s,w.Terms,'ko-');grid on;xlabel('Averaging time (s)');ylabel('Overlapping pair terms');
title('Term count is not independent degrees of freedom');
print(f,fullfile(outDir,'overlap_allan.png'),'-dpng','-r150');close(f);
r=struct('N',N,'Ts_s',Ts,'seed',21,'hand_m2_ADEV',hand.ADEV(2), ...
    'wrongShift_m2_ADEV',wrong,'largest_m',m(end),'largest_m_terms',w.Terms(end), ...
    'whiteFirstADEV',w.ADEV(1),'whiteLastADEV',w.ADEV(end), ...
    'walkFirstADEV',v.ADEV(1),'walkLastADEV',v.ADEV(end));
save(fullfile(outDir,'overlap_allan.mat'),'r','white','walk','m','Ts','w','v','hand','tab');
fprintf('ALLAN_PASS hand_m2=%.12g wrong_shift=%.12g max_m_terms=%d\n',hand.ADEV(2),wrong,w.Terms(end));
end

function tab=overlap_rate_adev(x,Ts,m)
% N rate samples; K=N-2*m+1 adjacent non-overlapping block pairs with shifted starts.
validateattributes(x,{'double'},{'real','finite','vector','nonempty'});
validateattributes(Ts,{'double'},{'real','finite','scalar','positive','<=',1e6});
validateattributes(m,{'double'},{'real','finite','vector','integer','positive','nonempty'});
x=x(:);m=m(:);N=numel(x);
assert(N>=2&&N<=20000,'ALLAN:Length','Require 2..20000 rate samples.');
assert(all(x==0 | (abs(x)>=1e-250 & abs(x)<=1e6)),'ALLAN:Range','Nonzero rate magnitudes must be in [1e-250,1e6].');
assert(all(m<=floor(N/2))&&all(diff(m)>0),'ALLAN:Factor','m must strictly increase and not exceed floor(N/2).');
x=x-x(1); % Remove a common offset before averaging; differences are invariant.
K=N-2*m+1;adev=zeros(size(m));
for j=1:numel(m)
    z=conv(x,ones(m(j),1)/m(j),'valid');
    d=z(m(j)+1:m(j)+K(j))-z(1:K(j));
    scale=max(abs(d));
    if scale==0,adev(j)=0;else,adev(j)=scale*sqrt(mean((d/scale).^2)/2);end
end
tau=m*Ts;
assert(all(isfinite(adev))&&all(isfinite(tau)),'ALLAN:Numeric','Results must remain finite.');
tab=table(m,tau,K,adev,'VariableNames',{'m','Tau_s','Terms','ADEV'});
end

5. 真实运行结果与独立参考

2026-10-21 在 MATLAB 25.2.0.2998904 (R2025b),PCWIN64 真实运行,v2 批处理退出码 0。N=4096,Ts=0.01 s;本次两组有限记录得到:

平均时间 K 白速率示例 ADEV (deg/s) 速率随机游走示例 ADEV (deg/s)
0.01 s,m=1 4095 0.0198771996676188 0.000564511016404203
20.48 s,m=2048 1 0.0000681864877225162 0.0147401783586916

图上最大 tau 的两个点用红叉标出,因为只剩一个差分项。白速率曲线最末端的明显下降只是这次实现值,不能直接当成高可信度的长期性能。两条随机曲线的形状受记录长度、随机实现和估计误差影响;不能由一次有限曲线宣称精确识别了噪声类型。

独立参考对每个 m、每个 i,用内层循环直接累加左右两块原始样本,再按 2*K 归一化;它不调用主实现的卷积均值,也不复用主实现的中心化或缩放 RMS。两种随机记录、全部 13 个 m 的最大绝对差为 6.2450045135165055e-17 。带前置零的相位参考最大绝对差为 3.8326546797362582e-17。这两项比较的容限为 1e-12。

边界测试还覆盖 N=2 的最后一项、奇数长度、常数、斜坡、m=1、重复/越界/非整数 m、缺失输入拒绝,以及 1e-200 量级偏差仍可表示的例子。大偏置案例的差为 6.1738547623373811e-11,在 1e-10 绝对容限内;这不是任意偏置下的严格不变性证明。三篇共 47 个命名检查全部通过,独立测试进程退出码 0。

输出 overlap_allan.csv 保存每个 m 的 tau、K 和两条偏差,overlap_allan_hand.csv 保存手算样例,MAT 保存原始速率、结果表及参数。完整独立参考位于 tests-v2/run_independent_checks.m;结果与运行日志在作者归档实验包的 execution 目录。

6. 从设备记录接入,哪些事情需要另做

工程数据链路可组织为"陀螺仪/频率计 → 带序号的定时采样缓冲区 → 单位和采样连续性检查 → 静止或指定工况记录 → Allan 分析"。用于此函数的数组应保持每个原始采样周期的位置。发现丢包或时钟跳变,应回到数据采集与实验设计处理,不能静默拼接两段记录。

复用时把本地 overlap_rate_adev 提取到同名独立文件,传入 x,Ts,m,同时保留输出的 Terms。本实现为易审阅的离线演示,卷积和直接双循环参考不以大数据吞吐为目标。输入上限与数值界限也不是通用库的完整设计。量纲转换、温漂、工况混合、趋势来源、置信区间与等效自由度都需要独立分析,本实验不作自动去趋势或噪声分解。

MathWorks allanvar 官方文档 可用于核对方差、平均时间与陀螺仪使用背景;本文输出的是方差的平方根,不调用 allanvar,也不依赖其工具箱 。当前文档对 m 的条件与本文保留 K=1 数学边界的设计不同,不能声称边界行为与该 API 完全一致。当前官方页面于 2026-10-21 核对,标注 R2026b;本次实际执行版本是 R2025b。

本篇代码的执行凭据

以下是便于复用的命令模板,不是原测试机绝对路径。只复现本篇时,将上文完整源码保存到新的工作目录,将 MATLAB 可执行文件加入 PATH;把下例 你的新目录 换成实际路径。命令的标准输出与退出码应保存,以便复核。

powershell 复制代码
matlab -wait -sd "你的新目录" -batch "overlap_allan_demo"

本次实际命令、源快照与时间/退出码原文均保留在作者归档实验包的 matlab/execution,最终数值来自其中 20261021-082339-demos-v2 运行,并非模板命令的假定结果。

主运行退出码 0,独立检查退出码 0。作者归档中保留 matlab/execution/20261021-082339-demos-v2 与 matlab/execution/20261021-082407-tests-v2 下的 stdout.txt、run.json。这些是归档路径,不是本网页的下载链接;本篇不依赖该归档即可运行上文完整示例。

本篇完整 MATLAB 源码 SHA256:6c8fc35b4bb22add87799bfb19bc00d53944af62ecd0a7a1fdc7246bf4825f73。文内代码、final 源码与真实执行快照已逐字节核验一致。独立复核由不同于主作者的 AI 子代理完成,身份、发现、修复和范围记录保存在作者实验归档的 reviews;不是人工认证。

留一个工程问题

若只有 N=1200 个等间隔速率样本、Ts=0.1 s,却想报告 tau=60 s 的稳定性,那么 m 与 K 分别是多少?这个点可以计算出来,与它足以支持长期性能结论,是同一件事吗?

相关推荐
y1su4 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
residual_fan4 小时前
船用柴油机动态机动工况故障诊断:控制感知仿射网络(CAAN)的技术实现
人工智能·算法·数据挖掘·数据分析
其实防守也摸鱼5 小时前
Cylab在线靶场——CTF中的通用技能
linux·数据库·学习·算法·靶场·ctf·学习网络安全
蓝悦无人机5 小时前
《Planning algorithms》读书笔记——第8章 反馈运动规划
算法·读书笔记·反馈运动规划
RaaS1005 小时前
Agent Skills 进了 Linux Foundation:2.8 亿次安装,但装 skill 还没有安全规范
人工智能·算法·架构
CV爱数码6 小时前
CT 重建从零开始(五):ASTRA Toolbox 上手——几何、投影器与算法
图像处理·算法·计算机视觉·ct
TunerT_TQ6 小时前
超立方体Ramsey(openai/math 家族171)H11攻坚日志|D60–D69:Star分组重数、二幂坐标群、奇偶入射语义与Site‑Level候选计数形式化闭环
算法
风巽·剑染春水6 小时前
【数据处理】Matlab 环境下安装 SPM 与 CAT 工具并进行脑核磁数据预处理
matlab·spm·cat·脑部核磁·t1w图像
Rabitebla6 小时前
【系统性学习笔记】Linux 权限篇(二):文件属性、权限修改与目录权限的底层逻辑
linux·c++·笔记·学习·算法