1. 为什么卫星导航圈开始认真对待CNN
头几年在导航圈里提卷积神经网络,多数人的第一反应是"这玩意儿跟伪距解算有什么关系"。我最初接触这个方向时也带着同样的怀疑,直到在一次城市峡谷场景的实测中,看到同一段中频采样数据,传统窄相关加门限判据把三颗强多径卫星判成了视距信号,而一个只有五层卷积的小网络把其中两颗揪了出来,我才意识到这条路值得认真走一遍。这篇内容想聊的就是CNN在卫星导航中的应用这条线:它现在到底能做哪些事、输入数据怎么构造、网络怎么选、端侧怎么部署、哪些坑是必须提前知道的。
先把定位说清楚。这篇不是论文导读,也不是给算法岗看的公式推导,而是一个从接收机信号处理一路做到嵌入式落地的工程视角总结。适合三类人看:做基带信号处理、想引入数据驱动方法的工程师;做定位算法、被NLOS和多径误差折磨过的同行;以及刚入门、手里有采集数据但不知道从哪儿下手的同学。全文围绕北斗信号体制展开,因为B1I、B3I、B1C这几路信号在带宽、码率、调制方式上的差异,直接决定了CNN输入张量的构造方式,这一点比很多人想象中更关键。
需要提前打个预防针:CNN在卫星导航里不是万能钥匙。它擅长的是从高维、强噪声、弱模型的数据里抠出模式,比如"这一段相关器输出像不像多径"、"这帧时频图里有没有扫频干扰"。它不擅长的是替代轨道动力学、替代钟差建模、替代最小二乘解算这些有严格物理约束的环节。搞清楚这个边界,后面所有的选型和调参才有意义。
2. 传统接收机链路的三道坎
2.1 门限判据的天生局限
传统接收机处理多径和干扰,靠的是一套人工设计的判据链。以多径抑制为例,窄相关、双差相关、脉冲孔径相关这些技术,本质都是通过调整相关器间距和组合方式,压制多径在相关峰上造成的畸变。这套方法在开阔场景下非常有效,我做过的一组静态测试里,窄相关能把多径引起的伪距偏差从米级压到分米级。
问题出在判据的刚性上。几乎所有的多径检测指标,最终都要落到一个或几个标量上:相关峰对称性、Early-Late差值比、载噪比跳变、伪距残差。人为设定一个门限,超过就判多径。可现实场景里,弱多径会让指标缓慢漂移而始终不越线,强NLOS又可能因为信号衰减导致载噪比掉下来被误判为低质量视距信号。标量判据丢掉的是相关峰整体的形状信息,而这恰恰是区分多径类型最有效的线索。
2.2 城市峡谷里的信号形态远比模型复杂
做城市峡谷测试的人都清楚,真实的多径环境不是教科书里那种"一条直射加一条反射"的干净模型。高楼玻璃幕墙、金属雨棚、行驶中的大型车辆,会同时产生多条幅度接近、时延接近的反射路径,叠加之后的相关峰可能呈现出双峰、肩部隆起、整体展宽等多种畸变形态。
更麻烦的是北斗GEO卫星。GEO卫星仰角基本不变,在某些固定安装场景里,某颗GEO的多径环境是长期稳定的,这意味着误差呈现出强系统性而非随机性。用统计方法去处理系统性偏差,效果天然受限。这类场景恰恰是数据驱动方法的机会:形态稳定意味着可学习,只要有足够多的标注样本,网络能记住这种"稳定畸变"长什么样。
2.3 干扰与欺骗的对抗升级
压制式干扰和欺骗信号的识别,传统手段主要靠载噪比监控、AGC增益变化、相关峰异常检测这几招。对付宽带压制干扰还行,遇到窄带、扫频、分段脉冲这类低占空比干扰,平均功率指标很容易被稀释掉,等检测出来时跟踪环早就失锁了。
欺骗更难。转发式欺骗在初期阶段信号功率、码相位都和真实信号高度接近,单纯靠功率监控基本无效。真正有效的线索往往藏在细微处:多普勒变化率的物理合理性、多颗卫星信号到达时间的一致性、相关峰形的微小差异。这些线索单看都不足以定论,但组合起来就构成了一个高维特征空间,正好是CNN的主场。
3. CNN能吃下的任务和吃不下的任务
3.1 适合交给网络的四类任务
按我这几年看到的落地情况和自己的实践,CNN在卫星导航里真正跑出效果的任务集中在四类。第一类是信号层面的分类与检测,包括干扰类型识别、NLOS判别、欺骗检测,输入是时频图或相关器输出矩阵。第二类是误差建模与修正,用网络预测多径误差量、伪距残差,再喂给后面的解算模块。第三类是观测量质量控制,给每颗卫星每个历元打一个质量分,用于加权或剔除。第四类是捕获跟踪的辅助决策,比如自适应调整环路带宽、辅助码相位搜索。
这四类的共同点是:输入数据维度高、人工特征难以穷举、但标注相对可获得。只要有采集设备和一套同步的真值记录手段,样本是能攒出来的。
3.2 千万别让网络去干的事
反过来说,有几件事我强烈建议别碰。一是用网络直接输出位置坐标。卫星几何构型、钟差、电离层延迟这些因素在不同历元变化剧烈,端到端回归位置的结果是训练集外几乎不可用,而且出了问题完全无法归因。二是用网络替代伪距解算。最小二乘和卡尔曼滤波有明确的误差传播模型,可解释、可做完好性分析,换成黑盒之后RAIM这类完好性监测手段直接失效。三是用小样本硬训一个大模型。导航领域的标注数据获取成本极高,动辄几十万参数的网络在几千个样本上必然过拟合。
一个判断标准:如果这个环节的输出需要参与完好性判定或者承担安全责任,就不要用纯数据驱动方案;如果它只是给后续模块提供特征或初值,那可以大胆用。
4. 把导航信号变成网络看得懂的"图"
4.1 时频图:最直观,也最容易被误用
时频图是入门首选。把中频采样做短时傅里叶变换,得到二维的功率谱密度矩阵,再转成对数刻度归一化,直接就是一张单通道灰度图。干扰检测这个任务用它非常自然:窄带干扰表现为一条水平亮线,扫频干扰是一条斜线,脉冲干扰是一段一段的竖条,宽带压制则是整片抬升的底噪。
但这里有个参数选择的坑,我见过不止一个团队栽在上面。假设处理的是B3I信号,标称带宽20.46MHz,考虑抗混叠后采样率取40.92Msps。做1024点STFT,频率分辨率是40.92e6/1024约等于40kHz。这个分辨率对宽带压制干扰够用,对窄带干扰就太粗了:一条50kHz的窄带干扰会被摊到相邻两三个频点里,和底噪几乎分不开。
想让频率分辨率到1kHz,窗长得到40960点,时间分辨率随之恶化。而扫频干扰的斜率特征又要求时间分辨率足够细。这两个需求是直接矛盾的,解决办法有两个:一是做多分辨率拼接,用长短两种窗分别做STFT再在通道维度拼起来;二是先降采样再分析,把40.92MHz降到4.092MHz,1024点窗对应的分辨率就是4kHz,同时帧率还能提上去。
import numpy as np def make_spectrogram(iq, fs=40.92e6, decim=10, nfft=1024, hop=256): """ iq: 复数中频采样, 一维 numpy array decim: 整数抽取倍数, 先降采样再算STFT """ # 简单抗混叠: 频域截断后逆变换 n = len(iq) spec = np.fft.fftshift(np.fft.fft(iq)) keep = n // decim c = n // 2 half = keep // 2 spec = spec[c - half: c + half] iq_low = np.fft.ifft(np.fft.ifftshift(spec)) * (keep / n) fs_low = fs / decim # 分帧加汉宁窗 win = np.hanning(nfft) frames = [] for start in range(0, len(iq_low) - nfft, hop): seg = iq_low[start:start + nfft] * win frames.append(np.abs(np.fft.fftshift(np.fft.fft(seg)))) s = np.array(frames).T # [freq, time] s = 10 * np.log10(s ** 2 + 1e-12) # 转 dB s = (s - s.mean()) / (s.std() + 1e-8) # 逐样本标准化 return s.astype(np.float32)这段代码里我特意保留了频域截断降采样这一步。很多团队直接用切片抽取,结果频谱混叠把干净的干扰特征搅乱了,训练出来的模型在外场一塌糊涂。降采样这一步做干净,后面的活都好干。
4.2 相关器域输入:更贴近导航本质
时频图看的是频谱特征,但多径和NLOS的区别主要藏在相关峰形状里。所以做多径相关任务时,我更倾向于用相关器域数据作为输入。
具体做法是:对每颗卫星、每个积分周期,取一组不同间距的相关器输出。常见配置是E-P-L间距为0.1、0.25、0.5、0.75、1.0码片共11个支路,把11个值拼成一维向量。单颗卫星一个历元太单薄,就把连续M个历元的向量拼成M×11的矩阵,当作一张"小图"送进2D卷积;或者把同一历元多颗卫星的向量按卫星编号排成N×11,让网络学习卫星间的联合模式。
我实际跑过的一个方案是这样组织的:输入张量形状为[批次, 通道, 卫星数, 相关器支路数],通道维度放I路和Q路两路信息,卫星数固定取12(不足补零),相关器支路取11。这样一张输入就是2×12×11,参数量可以控制得很小,普通Cortex-A53核上单次推理三毫秒以内。
这里有个细节值得说:别把支路值直接送进去,先做归一化。相关器输出的绝对值受载噪比影响极大,同一形态的多径在强信号和弱信号下数值差好几个数量级。稳妥的做法是用峰值归一化,把11个支路值除以最大值,得到相对形状;再单独把峰值功率取对数作为一个标量通道补进去。这样网络学形状,功率信息单独保留,泛化性明显好。
4.3 观测量序列:1D卷积的地盘
如果手上只有接收机输出、拿不到原始采样,那能做的主要是观测量层面的任务。每颗卫星每个历元能拿到的量不算少:载噪比、仰角、方位角、伪距、伪距率、多普勒、周跳标志、锁相环稳定度指标。把同一颗卫星连续M个历元的这些量拼成M×K的矩阵,用1D卷积沿时间轴扫,或者用2D卷积把时间和观测量维度都覆盖。
这条路的好处是数据门槛低,很多存量设备就能直接吐出来;坏处是可分性天然受限,因为观测量是基带处理之后的压缩结果,很多信号层面的信息已经丢了。我的经验是:做干扰分类、做粗粒度的NLOS筛选,观测量够用;想做精细的多径误差建模,还是得回到相关器域甚至原始采样域。
5. 三种输入构造方式的横向对比
| 维度 | 时频图(IQ域) | 相关器域矩阵 | 观测量序列 |
|---|---|---|---|
| 数据门槛 | 高,需原始中频采样 | 中,需接收机开放相关器接口 | 低,常规输出即可 |
| 单样本数据量 | 大(数十KB到MB) | 中(百字节级) | 小(几十字节) |
| 频率分辨率依赖 | 强,窗长选择敏感 | 弱 | 无 |
| 适合任务 | 干扰检测分类、欺骗初筛 | 多径/NLOS识别、误差建模 | 质量控制、异常历元筛查 |
| 端侧算力占用 | 高 | 低 | 极低 |
| 标注难度 | 中,干扰类型易标注 | 高,多径真值需参考站 | 中 |
| 跨场景泛化 | 中 | 较好 | 差,强依赖设备型号 |
这张表是我自己的经验总结,不是绝对结论。有人用观测量序列做NLOS也做出了不错的效果,前提是训练集和测试集来自同一型号设备、同一城市。一旦换城市换设备,性能掉得很厉害。做工程选型的时候,先想清楚这套东西最终部署在什么场景,再倒推该用哪种输入。
6. 多径与NLOS识别:最成熟的落地场景
6.1 问题定义与标签怎么来
多径识别的标签问题是整个任务里最费劲的部分。理想情况下,用一台高精度参考接收机架在已知坐标点上,通过载波相位后处理反算出每颗卫星每个历元的真实伪距误差,超过某个阈值的打上NLOS标签。这套流程在实验室里跑得通,野外做起来成本极高。
实际工程里更常见的折中方案是分层标注。第一层用几何遮挡模型做粗筛:根据三维城市模型或者实测的遮挡角图,仰角低于遮挡线的卫星大概率被遮挡,标为候选NLOS。第二层用双天线或者多接收机一致性检验做精筛:两个相距几米的接收机,同一颗卫星如果伪距残差差异显著,基本可以确认存在局部多径。第三层靠人工抽检确认。三层筛下来,标注准确率能到九成以上,够训练用了。
需要提醒一句:别指望靠公开数据集解决问题。公开的城市峡谷数据集场景、星座、信号体制和你的实际部署环境差异太大,拿来验证思路可以,直接迁移会失望。
6.2 网络设计的几个关键取舍
多径识别这个任务,我给的建议是"小网络、强先验"。原因很直接:样本量有限,场景差异大,大网络学到的往往是场景特有的偏置而不是多径的本质形态。
我实际用的一个结构是这样:先做两层1D卷积沿相关器支路方向扫,提取峰形特征;然后接一层跨卫星的注意力或者说简单的池化聚合,让网络能利用卫星间的上下文;最后接全连接输出二分类或者误差回归值。整个网络参数量控制在五万以内。
跨卫星聚合这一步是精髓。单看一颗卫星的相关峰,很难分辨是轻微多径还是信号本身弱导致的峰形畸变;但如果你同时看到这颗卫星仰角低、方位角朝向一片高楼、而其他高仰角卫星峰形正常,判断就有底气了。传统方法把每颗卫星独立处理,恰恰丢掉了这个上下文。
import torch import torch.nn as nn class CorrNet(nn.Module): def __init__(self, n_sat=12, n_lag=11, ch=2): super().__init__() self.local = nn.Sequential( nn.Conv1d(ch, 16, 3, padding=1), nn.BatchNorm1d(16), nn.ReLU(), nn.Conv1d(16, 32, 3, padding=1), nn.BatchNorm1d(32), nn.ReLU(), ) # 跨卫星聚合: 把卫星维度当作特征维做全局池化 self.fuse = nn.Sequential( nn.Linear(32 * n_lag, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, 1), ) def forward(self, x): # x: [B, n_sat, ch, n_lag] B, S, C, L = x.shape x = x.reshape(B * S, C, L) x = self.local(x) # [B*S, 32, L] x = x.reshape(B, S, 32 * L) x = x.mean(dim=1) # 卫星维池化 return self.fuse(x).squeeze(-1)参数量算一下:第一层卷积16×2×3=96,第二层32×16×3=1536,全连接64×352=22528,最后64。总共不到两万五。这个规模在嵌入式上跑毫无压力,在样本量只有几千条的时候也不至于严重过拟合。
6.3 实测效果与边界
在一组城市道路实测数据上,这套方案相对传统窄相关的伪距误差RMS改善了约35%,把95分位误差从十几米压到了七米左右。但我要强调,这个改善是有条件的:测试路段的多径类型在训练集里出现过。换到一个完全陌生的场景,比如从玻璃幕墙密集区换到高架桥下,前两周的实测性能会明显回落,需要补几百条新场景样本做微调才能恢复。
这引出一个重要结论:多径识别模型必须配持续的数据回流机制。一次性训练然后永久部署,在导航这个场景里基本行不通。
7. 压制式干扰检测与分类
7.1 为什么这个任务比多径好做
干扰检测是CNN在导航领域最容易出效果的落地点,原因有三个。第一,标签容易获取:做干扰测试时你是主动发射的,什么时间发什么类型全都清楚,标注几乎零成本。第二,特征明显:不同干扰类型在时频图上形态差异极大,网络很容易学到。第三,评价客观:检测率和虚警率都是硬指标,不像多径误差那样依赖参考站。
我在一个封闭场地做过一组对比测试,用同一个时频图数据集训练,7类干扰(单频、窄带、扫频、脉冲、宽带噪声、线性调频、复合)的分类准确率能到96%以上,而用传统的能量检测加谱峰搜索方案,复合干扰和低占空比脉冲这两类基本分不开。
7.2 输入构造的关键参数
做干扰分类,时频图的参数选择有一套明确的逻辑。先确定你要区分的最小频率间隔,比如单频干扰和窄带干扰的分界设在100kHz,那频率分辨率至少要做到50kHz以内,1024点窗在40.92Msps下是40kHz,勉强够。再确定要区分的最短脉冲,比如10微秒的脉冲,帧移必须小于10微秒,在40.92Msps下就是409个采样点,帧移取256点约6.25微秒,满足要求。
两个条件一卡,参数就定死了:窗长1024、帧移256。这是个很典型的工程推导过程,不是拍脑袋调出来的。
另一个实用技巧是多通道堆叠。把原始时频图、一阶时间差分(突显扫频和脉冲)、一阶频率差分(突显窄带和单频)作为三个通道送进去,效果比单通道稳定不少。差分通道相当于把人工先验注入了输入,网络不用再从零学这些边缘特征,收敛快很多,小样本下尤其明显。
7.3 干扰与NLOS的联合处理
实际系统里这两个问题经常同时出现。强干扰会导致载噪比下降,进而使相关峰畸变,如果多径识别模块不知道有干扰存在,就会把它误判成多径。我见过一个系统就是因为这个原因,在干扰环境下疯狂剔除"多径"卫星,最后可用卫星不够解算。
解决办法是把两个任务做成多任务网络,共享前面的特征提取层,后面接两个分类头。共享层学到的是"信号质量异常"的通用表征,两个头分别判断是干扰还是多径。这样在只有单任务标签的时候也能做半监督训练,实测下来两个任务的性能都比单独训练要好一些。
顺带说一句,多任务方案还能省算力。端侧设备上跑两个独立模型的开销,明显大于跑一个共享骨干的多任务模型。
8. 欺骗信号识别的可行路径
8.1 特征在哪
欺骗检测这块,纯靠单帧时频图基本没戏,因为转发式欺骗的信号在频谱上和真实信号几乎一样。真正有判别力的特征集中在两类:一是多星一致性,欺骗源通常用一个天线发射多路伪造信号,这些信号的到达角、多普勒变化率会呈现出不自然的规律性;二是时序演化,欺骗信号在接管过程中功率是逐渐爬升的,而真实信号功率变化遵循一定的物理规律。
工程上可行的做法是把多颗卫星的载噪比时序排成二维矩阵,行是卫星、列是时间,送进2D卷积网络。欺骗接管的过程中,这个矩阵会出现一个非常特征化的模式:某几颗卫星的功率同时、同斜率地爬升。这个模式人眼不容易在几十颗卫星的数据里看出来,网络可以。
8.2 数据从哪来
这是最大的障碍。真实欺骗数据获取门槛很高,绝大多数团队只能靠仿真。仿真的问题在于,真实的欺骗设备有各种不完美:相位噪声、时延抖动、功率控制精度有限,这些不完美恰恰是检测的线索。纯理想仿真训出来的模型,遇到真实攻击未必管用。
我的建议是仿真加"半实物"混合。用信号源模拟器生成基础场景,再通过可编程衰减器和移相器人为注入各种不完美,这样生成的数据在统计特性上更接近真实情况。虽然还是不如真实攻击数据,但比纯理想仿真强得多。
8.3 现实定位
必须说实话:目前这条路还处在"能提供辅助告警"的阶段,做不到可靠确证。比较务实的用法是把它做成一个告警分,和传统的信号质量监控、多普勒一致性检验结合使用,多个弱证据叠加起来提高置信度。单独依赖任何单一手段做欺骗判别,风险都很高。
9. 网络结构、训练配置与端侧部署
9.1 骨干选型:别一上来就上大模型
导航领域的数据有个显著特点:样本量小、场景差异大、标注成本高。这三点决定了网络规模必须克制。我的一般原则是参数量控制在十万以内,超过这个数量级就要非常谨慎地评估过拟合风险。
具体结构上,做时频图任务我喜欢用五到七层的全卷积结构,不用池化用步长卷积,保持空间信息的连续性。做相关器域任务用1D卷积加跨卫星聚合。不用ResNet这类深残差结构,因为层数根本到不了需要残差连接的深度,加了只是徒增参数。
如果非要给个量化参考:输入128×128的单通道时频图,五层卷积(通道数16-32-32-64-64)、全局平均池化、单层全连接,参数量大约在八万左右,在ARM Cortex-A53 1.2GHz上单帧推理大约四毫秒。这个延迟对于干扰检测这个任务是可以接受的,因为干扰检测本来就不需要做到毫秒级响应。
9.2 训练配置的实操细节
优化器用AdamW,初始学习率3e-4,权重衰减1e-2。批大小能开多大开多大,显存允许的情况下32或64。学习率调度用余弦退火加三到五个epoch的线性预热。这些都不算特殊,但有几个导航领域特有的细节值得说。
第一是数据划分必须按场景分,不能随机分。如果同一个采集时段的样本被随机分到训练集和验证集,因为相邻帧高度相关,验证精度会虚高十几个点。正确做法是按采集批次、按路段、按设备划分,让验证集和训练集在场景上完全不重叠。这一条踩过的人极多。
第二是类别不平衡的处理。干扰检测里正常样本远多于异常样本,NLOS识别里视距样本占大头。不要简单用加权损失,更好的做法是分层采样配合Focal Loss,同时监控每一类的召回率而不只看总体准确率。
第三是输入标准化要用全局统计量。均值方差必须从训练集算出来存下来,推理时用同一组值。我见过有人推理时用当前批次自己的统计量做标准化,结果单样本推理和批量推理结果对不上,排查了两天才找到原因。
import torch, torch.nn as nn, torch.optim as optim from torch.optim.lr_scheduler import CosineAnnealingLR model = CorrNet().cuda() opt = optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-2) crit = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([2.5]).cuda()) sched = CosineAnnealingLR(opt, T_max=60, eta_min=1e-5) def warmup_lr(epoch, warm=5): if epoch < warm: return (epoch + 1) / warm return 1.0 for epoch in range(60): model.train() scale = warmup_lr(epoch) for g in opt.param_groups: g['lr'] = 3e-4 * scale if epoch < 5 else g['lr'] for x, y in train_loader: x, y = x.cuda(), y.cuda().float() opt.zero_grad() loss = crit(model(x), y) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 1.0) opt.step() if epoch >= 5: sched.step() # 验证集按场景分组统计梯度裁剪这一步别省。导航数据的数值动态范围可能很大,偶尔出现一个异常样本会导致梯度爆炸,裁剪到1.0能避免训练中途崩掉。
9.3 部署:从服务器到接收机
模型落地这一步,很多人低估了工作量。服务器上训练出来的PyTorch模型,直接搬到嵌入式设备上跑,通常会有三倍到五倍的速度损失,而且内存占用可能翻倍。
实际路径一般是这样走的:先在服务器上做结构精简,把BatchNorm在推理时折叠进卷积,去掉所有训练专用的层;然后导出ONNX,用推理引擎做图优化和量化;最后在目标硬件上做精度回归测试。
量化这一步要特别小心。导航数据经过对数变换和标准化后,数值分布相对温和,INT8量化通常能保持精度,但我实测过时频图输入在INT8下有一到两个点的精度损失,主要是因为底噪区域的动态范围被压掉了。解决办法是对输入层保持FP16或者做逐通道量化,别用全局量化。
内存这块,输入张量按128×128单通道算,FP32是64KB,中间特征图峰值大概几百KB,加上权重,整体控制在2MB以内没问题。如果是多任务模型或者多通道输入,要重新算一遍峰值,别到时候跑起来才发现内存不够。
一个实操提醒:端侧部署前一定要在设备上跑一遍真实数据,不要只看服务器上的验证集指标。我在一个项目里遇到过服务器精度92%、设备上只有78%的情况,最后查出来是设备端浮点运算和服务器存在细微差异,某些边缘样本正好卡在决策边界上。加一层输入归一化的容错处理就好了。
10. 定位误差建模与完好性辅助
10.1 残差预测这条路
用网络预测伪距残差,然后把预测值作为修正量,或者作为加权最小二乘的权重依据,这是我见过的第二成熟的落地方式。相比直接输出坐标,这种做法的好处是每一步都可解释:网络输出的是每颗卫星的误差估计,后续解算还是走标准流程,出了问题能定位到具体卫星。
实现上有两种接法。第一种是前馈修正,把网络预测的误差从原始伪距里扣掉,再送进解算器。这种做法上限高但风险也高,预测错了会直接把解算带偏。第二种是权重调整,网络输出每颗卫星的置信度,用来调制观测权重。这种做法更稳健,即使网络输出有偏差,最坏情况也只是退化到等权解算。
我推荐先做第二种,跑稳了再考虑第一种。工程上渐进式推进比一步到位靠谱得多。
10.2 完好性监测里的角色
完好性监测对可解释性要求极高,这里我不建议让网络直接参与保护级计算。可行的用法是把它当作一个前置筛查器:网络输出异常分,超过阈值时触发更严格的传统检测流程。
这样做的好处是安全边界清晰。网络的误判最多导致多跑几次传统检测,不会直接影响完好性结论。而且这个用法对网络的精度要求也没那么苛刻,召回率做到95%以上、虚警率控制在5%以内就很有价值了。
11. 踩过的坑与问题排查实录
11.1 离线指标漂亮、外场掉链子
这是最常见也最伤人的问题。我在一个项目里遇到过训练集准确率98%、验证集95%,装到设备上外场实测只有70%出头的情况。排查了一周多,最后定位到三个叠加原因。
第一个原因是数据泄漏。采集的时候为了省事,把连续采样的数据按帧随机划分,相邻帧之间相关性极高,等于变相把测试集信息泄漏到了训练集。改成按采集批次划分后,验证集准确率立刻掉到85%,这才是真实水平。
第二个原因是归一化统计量不匹配。训练数据用的是实验室环境下的底噪水平,外场底噪高了十几dB,虽然做了标准化,但分布已经偏移了。解决办法是在标准化之后再加一层自适应增益控制,或者干脆把输入改成相对值而非绝对值。
第三个原因是硬件链路差异。实验室用的是采集卡直接采中频,外场走的是接收机内部AGC之后的信号,动态范围被压缩过。这个差异在数据上表现为直方图形态不同。后来我们在训练集里混入了不同增益设置下的数据做增广,问题基本解决。
11.2 问题速查表
我把这几年遇到的典型问题和处理办法整理成了一张表,遇到类似症状可以先对照排查。
| 症状 | 可能原因 | 排查方法 | 处理办法 |
|---|---|---|---|
| 训练损失不下降 | 输入未标准化 / 学习率过大 | 打印输入均值方差、损失曲线 | 加标准化层,学习率降到1e-4 |
| 验证精度远低于训练 | 数据泄漏 / 过拟合 | 按场景重新划分后再测 | 按批次划分,加Dropout和权重衰减 |
| 外场精度骤降 | 域偏移 / 硬件链路差异 | 对比内外场输入直方图 | 数据增广,加自适应归一化 |
| 单样本推理与批量推理结果不一致 | 用了批次统计量做标准化 | 固定batch=1重测 | 存下训练集全局统计量 |
| 端侧推理超时 | 模型过大 / 未做量化 | 统计各层耗时 | 剪枝、量化、算子融合 |
| 某类干扰完全检不出 | 样本不均衡 / 该类样本太少 | 看混淆矩阵 | 分层采样,Focal Loss |
| 模型对温度敏感 | 训练集场景单一 | 分季节数据对比 | 扩充不同温区样本 |
| 干扰环境下多径误判激增 | 任务间耦合 | 人工检查误判样本的频谱 | 改多任务联合训练 |
11.3 几条我认为最有价值的经验
第一,先做传统方法,再上网络。这句话听着像老生常谈,但真的很重要。如果你连传统方法在这个场景下的性能基线都不清楚,就没法判断网络到底带来了多少增益。我见过团队花三个月训模型,最后发现还不如调一调窄相关间距。先花一周把基线跑出来,后面所有工作都有参照。
第二,数据质量的决定性远大于网络结构。我在两个项目上做过对比:同一个网络结构,一个用精心标注、场景覆盖充分的数据集,另一个用凑合标注的大数据集,前者的外场表现明显更好。与其花时间试各种骨干网络,不如多花时间在标注规范和场景覆盖上。
第三,一定要留出"完全陌生场景"的测试集。训练和验证都做好了,还要留一批从没见过的场景数据,只在最终评估时用一次。这批数据的作用是给你一个真实的能力预期,避免上线后被打脸。
第四,把不确定性估计加进去。简单的做法是MC Dropout或者深度集成,推理时跑多次取方差。方差大的样本大概率是训练集没覆盖到的情况,这时候可以让系统退回传统方法,或者降低这颗卫星的权重。这个机制在实际部署中价值很大,能让系统在遇到没见过的场景时不至于给出离谱的结果。
第五,别忘了算力预算这件事要前置。很多团队先训模型再想部署,结果发现目标硬件跑不动,回头改结构推倒重来。正确顺序是先确定目标平台、算出可用算力预算和内存预算,再在这个约束下设计网络。我一般会预留三倍余量,因为实际部署时的开销总比估算的高。
12. 一个绕不开的现实问题:算力和功耗
端侧部署这块值得单独说几句。导航接收机尤其是手持和车载设备,功耗预算是极紧的,整机功耗往往就几瓦,留给额外计算的余量可能只有几百毫瓦。
在这种约束下,能做的事情非常有限。我的经验是:干扰检测这类不需要连续运行的任务,可以做成占空比触发,平时只跑轻量的能量检测,检测到异常再唤醒网络做精细分类,这样平均功耗能压到很低。NLOS识别这类需要每历元运行的任务,就得把模型压到极致,参数量控制在两万以内,输入尺寸缩到64以内,用INT8量化,单次推理控制在两毫秒以内。
还有一个思路是在射频前端或者FPGA侧做特征提取,只把压缩后的特征送给主控。比如STFT这种运算,用FPGA做能耗远低于用CPU做,主控只需要跑后面的卷积部分。这个架构在实际产品里更常见,虽然开发复杂度高一些,但功耗收益明显。
提醒一句:不要忽略内存带宽这个瓶颈。有些模型参数量不大,但中间特征图很大,频繁的内存读写反而成了功耗大头。做端侧优化的时候,除了看参数量和FLOPs,还要看峰值内存占用和访存次数。
13. 关于未来的一点个人判断
我不太喜欢写展望,但有几个方向我觉得值得投入精力,算是给自己也给同行的备忘。
第一个是模型和物理模型结合。纯数据驱动在导航领域的天花板很明显,因为物理约束太强了。把多径的几何模型、信号传播模型作为先验嵌入网络结构,比如用几何关系约束网络输出的误差范围,这条路我觉得比单纯堆数据更有前途。
第二个是跨设备、跨场景的迁移。现在的模型换个设备就得重训,成本太高。域自适应、少样本微调这些技术在别的领域已经比较成熟了,搬到导航领域应该能解决不少实际问题。
第三个是数据集和评测标准的建立。这个领域目前最大的短板不是算法,是缺少公认的、覆盖多场景的、带精确标注的公开数据集。没有统一评测标准,各家的结果没法比较,整个领域的进展会被拖慢。
最后分享一个我在实际项目里养成的习惯:每次模型上线前,我都会准备一批"刁钻样本"——极低载噪比的、强多径叠加干扰的、信号刚捕获还没稳定的——单独跑一遍看结果。这批样本不参与任何训练和调参,纯粹用来给自己一个心理预期。多数时候结果都不好看,但正是这些不好看的结果,让我知道系统的边界在哪,也知道下一步该往哪个方向补。做导航这一行,知道自己不知道什么,比知道自己知道什么更重要。