[旧日谈] 再考 IIR 与 FIR 滤波器对相位影响的定量分析
IIR 与 FIR 濾波器对音频相位的影响 先前我有写过一个简单的文章分析过两种滤波器对音频相位的影响,但是我只是知其然不知其所以然。对于音频,我虽然知道相位是一个很重要的概念,但是我始终不知道相位对实际音频的印象是什么水平的。这个问题在这些年的开发过程中始终萦绕在心头。虽然不做音频了,但是我仍然对这个问题保持好奇,综上,这也是为什么有了这个文章。 一、从一个问题开始 假设我们有一个 1kHz 的正弦信号,经过一个低通滤波器之后,输出还是 1kHz 的正弦信号,幅度变小了——这很好理解,滤波器嘛,该衰减的衰减。 但仔细看输出波形,会发现它相对于输入信号产生了一个时间上的延迟。这个延迟不是简单的"整体往后挪了 N 个采样点",而是不同频率的信号延迟不一样。 1kHz 的信号延迟了 0.5ms,500Hz 的信号延迟了 0.8ms,2kHz 的信号延迟了 0.3ms——每个频率成分的延迟都不一样。 这就是相位失真。 对于音频处理来说,这个问题比听起来严重得多。人耳对相位差的感知不如幅度那么直接,但当不同频率成分的延迟差异大到一定程度时,会导致: 瞬态信号(比如鼓点、齿音)的波形被"模糊化" 立体声声像偏移 某些频段的"堆叠"或"空洞" 所以,理解滤波器的相位特性,是做音频处理的基本功。 先说结论,IIR 的相位响应受幅度响应约束(最小相位特性),无法独立控制;FIR 可以独立控制幅度和相位,因此能实现线性相位或任意指定相位。 但是至于为什么音频行业常用IIR滤波器,这个问题我将在补充后说明。 二、先回顾一下:FIR 和 IIR 是什么 FIR(有限脉冲响应) FIR 滤波器的差分方程: $$ y[n] = \sum_{k=0}^{M} b_k \, x[n-k] $$输出只依赖于当前和过去的输入,没有反馈。脉冲响应是有限长的(长度 M+1)。 IIR(无限脉冲响应) IIR 滤波器的差分方程: $$ y[n] = \sum_{k=0}^{M} b_k \, x[n-k] - \sum_{k=1}^{N} a_k \, y[n-k] $$输出同时依赖于输入和过去的输出(反馈)。脉冲响应理论上是无限长的。 两者的核心区别在于有没有反馈。这个结构上的差异,直接决定了它们的相位特性。 三、相位响应的推导 从频率响应说起 一个 LTI(线性时不变)系统的频率响应可以写成: ...
受力平衡分析:质心与扭转
这是一篇面向开发和调试的算法文档。读完后你应该能回答三件事: 算法在算什么、为什么这么算、界面上每个元素对应公式里的哪个量。 全文用一个贯穿例子:一块 5×5 的受力区域,压力沿主对角线(左上—右下)分布,左上端更重。 1. 算法要做什么 阵列式压力传感器的数据就是一张二维表:每个格子一个力值。画成三维曲面(坐标做底面、力值做高度),哪里轻哪里重一目了然。 图 1:起点。 左:压力曲面沿对角线隆起、左上端更高;右:同一组数据的 2D 视图和每格数值。人眼能看出"不均匀",算法要把这件事变成数字结论。 用户拿压头按传感器时,“按得不规范"有两种情况,物理上完全不同: 问题类型 表现 用户该怎么修 位置偏移 压头放歪了位置,但贴得是平的。压力分布形状正常,只是整体挪了位 照着箭头把压头挪过去 姿态歪斜 位置可能很正,但压头翘了边。翘起的一侧轻、压下的一侧重,分布形状被扭曲 停下来,检查贴合,重新放平 修法完全不同,所以算法输出两个相互独立的结论,各管一类: 位置通道:受力中心偏在哪 → 画面上画一个箭头; 扭转通道:压力沿哪条线失衡、哪侧重 → 画面上画一条双向轴线 + 偏重侧铺色。 后面所有设计都是围绕这两个通道展开的。第 2~3 节分别讲两个通道怎么算,第 4 节给出完整流程汇总,第 5 节讲界面怎么读,第 6 节讲能力边界和参数。 2. 位置通道:算受力中心(质心) 2.1 符号约定 后面所有公式共用这套记号: 受力区域(下称"斑块”)内的格子编号为 $i = 1, 2, \dots, n$; $V_i$:第 $i$ 格的力值(热力图上显示的那个数); $p_i = (r_i, c_i)$:第 $i$ 格中心的坐标,$r$ 是行号(向下),$c$ 是列号(向右); $\sum_i$:只对斑块内的 $n$ 个格子求和。 2.2 公式 把每格的力值当权重,对格子位置做加权平均,得到的点就是受力中心(质心): ...
一个基本问题:当我们谈论移植的时候,我们聊的是 ONNX 还是自定义算子?
这篇笔记不讨论哪条路线“更先进”,只记录一个实际选型问题:模型离开训练框架以后,究竟应该交给通用推理引擎,还是把它改写成项目自己的 C++ 实现。 面试时我常问这个问题。我关心的是,对方能不能把“能跑”和“适合长期维护”分开看。很多项目一开始只是照着现成方案接进去,等到包体积、启动时间或某个算子性能出了问题,才发现当初其实已经做了取舍。 所以这里的“移植”,不只是换一个文件格式。它更像是在两套成本之间做选择:一套成本由运行时和通用性承担,另一套成本由自己的代码、测试和维护承担。 一、ONNX 到底做了一件什么事 先从模型离开训练框架后的处境说起。 我们在 PyTorch 里训练模型,享受的是动态图、Python 生态、随时 print 随时调试。但部署环境——一个可能没有 Python、没有 torch、甚至没有文件系统的进程——什么都没有。训练与部署之间需要一次"交接"。交接最朴素的方式是序列化:把权重存下来,把结构描述出来。问题在于,每家框架的序列化格式都是私有方言:PyTorch 有 TorchScript 和 .pt,TensorFlow 有 SavedModel 和 .pb,互不相认。而部署端的推理引擎又层出不穷:ONNX Runtime、TensorRT、OpenVINO、CoreML、DirectML、各家 NPU 的私有 SDK……如果每个框架都要为每种引擎各写一条导出通道,对接复杂度是 N × M 的;在中间垫一层公共格式,它就变成 N + M。 ONNX(Open Neural Network Exchange)就是这层公共格式。具体来说,它负责三件事: 定义了一套标准算子集(operator set,简称 opset):Conv、Gemm、BatchNormalization、Relu……每个算子的语义、输入输出、数值行为都有书面规范,并按版本演进(opset 13、17、18……)。 定义了一张计算图格式:用 protobuf 描述节点(NodeProto)、张量(ValueInfoProto)、权重(Initializer),以及它们之间的连接关系。一个 .onnx 文件本质上就是一段序列化后的 protobuf。 定义了类型与张量规范:数据类型、布局约定、广播规则。 可以把它看成模型领域的交换文件:训练框架负责把图和权重写出来,推理引擎负责把它读进去。双方不必直接适配彼此,只要共同遵守这份格式即可。它能普及,主要也是因为这个生态位置,而不是因为它在每一种设备上都能做到最好。 这里有一个几乎所有初学者都会混淆、但一问就露馅的点:ONNX 只是一个格式,它自己不会执行任何东西。真正让模型跑起来的是推理引擎,最常见的就是 ONNX Runtime(下文简称 ORT)。前者是协议,后者是实现,就像 JSON 和某个 JSON 解析器的关系。我们日常说"用 onnx 跑",省略掉的主语几乎都是 ONNX Runtime。 那么这条路线的优势和代价分别是什么? 先看它为什么常常是默认选项。实际项目里,通常看重的是三点:格式标准,工具链成熟,以及性能能够满足要求。 标准:一次导出,处处可跑(至少在纸面上)。跨框架、跨引擎、跨操作系统。 成熟:ORT 背后是大量工程师十几年打磨的图优化器、算子融合 Pass,以及接驳成熟的 kernel 库(CPU 上有 MLAS 一系,移动端常用 XNNPACK)。你几乎不可能在同等工时内手写出比它更稳、覆盖面更广的实现。 快得足够:对绝大多数 PC 端、服务端场景,ORT 的默认性能已经在业务容忍范围之内;而且它提供 Execution Provider(EP)机制——CUDA、TensorRT、DirectML、CoreML、QNN——同一个 .onnx 几乎零改动就能下发到 GPU 甚至 NPU。 再说代价。代价不在"能不能跑",而在你为这份通用性交出的东西: ...
3D 曲面力场重建架构与点云显示算法研究
3D 曲面力场重建与点云显示算法研究 背景 三维曲面力场重建要解决的,是如何根据少量离散传感器的空间位置、表面方向和测量值,估计模型表面上没有传感器位置的力值,并把结果显示成连续、稳定且符合物体形状的分布。 这件事看起来像普通插值,实际包含两个性质不同的问题。第一个问题是力值应该传播到哪里。它由距离定义、曲面连通关系和传感器朝向决定。第二个问题是重建结果应该怎样显示。它由表面采样密度、颜色映射和几何位移决定。早期方案多次调整,根本原因就是把传播算法和显示载体混在一起:传播关系不正确时,仅改变颜色无法消除串色;传播关系正确时,直接在粗三角面上着色又可能产生大片色块。 最终采用的思路是先把三角模型整理成可传播的曲面图,再把每个传感器附着到模型表面,沿曲面执行有限半径的最短路径搜索,用 Wendland C² 核计算距离权重。多个传感器按连续场采样点进行归一化插值,再通过 coverage 控制支撑区域边缘的衰减。显示层不再改变模型本身,而是在灰色模型表面覆盖彩色点云,用颜色和沿法线的隆起共同表达力值。 图 Fig001:力场算法的主要演进。距离定义、边缘衰减和显示载体分别经历调整,最终形成曲面传播与点云显示的组合。 本文以左手手套三维模型作为演示对象,使用确定性的 Wave 合成力值观察动态效果。由于没有与该模型对应的真实加载实验数据,文中的结果只能说明算法行为和显示效果,不能被解释为真实设备精度验证。 从欧氏插值到曲面传播 最直接的重建方法,是计算传感器与模型顶点之间的三维直线距离,然后让距离近的传感器具有更大权重。这种方法实现简单,在平面或结构单一的缓曲面上也可能得到合理结果,但它隐含了一个不适用于复杂模型的假设:空间上接近的位置,也可以沿物体表面直接传播。 手套模型能清楚暴露这个问题。两根手指之间的空气间隙很小,手掌正反面之间也可能只有很薄的模型厚度。欧氏距离会把这些位置视为近邻,使一个传感器的影响直接到达另一根手指或模型背面。折角附近也有同样的问题。两个点的直线距离可能很短,但真实的表面路径需要绕过较长距离,甚至根本不存在连续路径。 早期的视觉修正曾把低值区域改成灰色,重新定义平滑显示与离散点显示的切换,并调整颜色过渡。这些改动提高了画面可读性,却没有改变传播距离,因此不能从根本上修复薄壁、折角和相邻部件之间的串色。 像素化重建是另一条尝试路线。它先把模型投影到高分辨率平面,在规则像素网格上插值和后处理。规则像素能够减轻原始三角面大小不均造成的色块,Gaussian 平滑也能让边缘更柔和,但单一投影难以表达任意方向、多层和自遮挡曲面。模型前后两层还可能落到相同像素位置。高分辨率重建带来的计算量也比较大。因此,这条路线适合作为视觉实验,却不适合作为一般三维模型的传播基础。 曲面传播的核心变化,是把"距离"从三维空间中的直线长度,改成模型表面的路径长度。模型顶点作为图节点,共享三角形边作为图边,边权是相邻顶点之间的几何长度。传感器先找到附近且朝向兼容的表面点,再从该点沿曲面图执行截断 Dijkstra。一个位置只有在曲面上存在合法路径,并且路径总长不超过传播半径时,才会受到该传感器影响。 这并不意味着算法完全不再使用欧氏距离。传感器的导出位置可能略微离开模型表面,因此仍要用直线距离找到附近表面作为附着点。区别在于,这个距离只用于完成传感器到表面的初始附着。附着之后的扩散范围由曲面路径决定,不能继续穿过模型内部走捷径。 曲面如何变成可计算的传播域 三角网格不能直接拿来做曲面搜索。STL 模型通常以彼此独立的三角形保存,同一个几何点可能在相邻三角形中重复出现。如果不先合并这些重复顶点,算法看到的会是大量互不连接的小三角形,最短路径也无法跨越三角形边界。 因此,曲面准备的第一步是顶点焊接。算法按照一个较小的位置容差,将坐标相同或足够接近的顶点合并为共享顶点,同时删除顶点重复或面积接近零的退化三角形。焊接容差需要与模型单位和导出精度匹配。容差过小会留下不应存在的裂缝,容差过大则可能把距离很近但实际分离的两层表面错误连接起来。 焊接之后可以根据三角形共享边建立邻接图。图边的长度取两个顶点之间的几何距离。为了避免传播穿过尖锐折角,还要比较相邻顶点法线。设相邻顶点为 $u$ 和 $v$,只有满足 $$ \mathbf{n}_u\cdot\mathbf{n}_v\ge\tau_e $$时,二者之间的边才允许参与传播。本文采用的邻接法线阈值为 $\tau_e=0.25$。这是一种硬门控:达到阈值的边可以通过,低于阈值的边完全禁止通过。它的优点是折角隔离清楚,缺点是高曲率但连续的表面也可能被切断。更平滑的替代方案是根据夹角增加路径代价,但这会引入新的衰减参数,必须通过真实数据才能判断是否更合理。 网格密度同样会影响曲面距离和点云连续性。如果最大三角形边长远大于传播半径,支撑区域内可能只有少量顶点,最短路径会变得粗糙,显示点也会过于稀疏。可以把目标边长定义为 $$ E_{target}=\max\left(\frac{R}{4},1\text{ mm}\right) $$当最大边长明显超过目标边长时,将一个三角形沿三条边的中点拆成四个子三角形。共享边的中点必须复用,避免细分后重新产生裂缝。细分还要设置顶点数上限,否则高面数模型可能快速膨胀。 细分后需要重新计算顶点法线。顶点法线既控制传播能否经过某条边,也决定点云隆起方向,因此不能把它当成单纯的光照数据。通常将相邻三角形的面积加权法线累加到顶点,再进行归一化。无条件执行 Laplacian 平滑并不稳妥,因为平滑会收缩几何并改变传感器到表面的物理距离。视觉上更圆润不代表几何关系更准确。 传感器附着也使用法线约束。设曲面点法线为 $\mathbf{n}_v$,传感器法线为 $\mathbf{n}_i$,只有满足 $$ \mathbf{n}_v\cdot\mathbf{n}_i\ge\tau_c $$的顶点才参与附着点选择。本文采用 $\tau_c=0.1$。在所有兼容顶点中选择与传感器中心最近的点作为种子。如果传感器到种子的初始距离已经大于传播半径,该传感器不产生曲面影响。 从种子开始执行 Dijkstra 搜索,路径距离由曲面边长累计: $$ d_i(v)=\min_{\pi:i\rightarrow v}\sum_{(u,w)\in\pi}\|\mathbf{x}_u-\mathbf{x}_w\| $$路径只能经过法线兼容的邻接边,并在距离超过半径 $R$ 时停止。这样得到的 $d_i(v)$ 是离散网格上的测地距离近似。它不完美依赖模型质量,但比直接使用穿过空间的直线距离更符合复杂表面的传播关系。 图 Fig004:传感器附着、曲面路径和紧支撑权重。欧氏距离只用于选择附近表面,实际传播距离由曲面路径决定。 离散测量如何形成连续场 得到曲面距离之后,仍然需要决定距离如何转化为权重。本文采用 Wendland C² 紧支撑核。对曲面距离 $d$ 和传播半径 $R$,令 $r=d/R$,权重定义为 ...
频域维纳滤波降噪算法——设计思路与工程手记
频域维纳滤波降噪算法——设计思路与工程手记 写在前头 这份笔记记录的是一个面向实时语音通信的频域降噪算法。它的设计目标很明确:在 48kHz 采样率下,以 10ms 帧长实时处理单通道语音,把稳态和准稳态背景噪声压下去,同时尽量不伤语音。算法脱胎于 WebRTC 的 Noise Suppression 模块,但经过了面向嵌入式的重构——所有动态分配被消除,所有 C++ 模板和虚函数被拍平成纯 C 结构体。 我按照信号流经的顺序来谈。先说为什么要这么做,再说具体怎么做。 1. 信号模型 一切降噪算法的出发点都是同一个假设:麦克风采集到的信号 $x(n)$ 是干净语音 $s(n)$ 和加性噪声 $d(n)$ 的叠加: $$ x(n) = s(n) + d(n) $$这个模型简单到近乎天真,但它成立的前提条件其实很苛刻:噪声和语音不相关,噪声在短时间尺度上是准平稳的。实际工程中,空调嗡嗡声、风扇声、马路上的低频轰鸣,在几十毫秒内确实变化不大——这就够了。 我们的目标是从 $x(n)$ 中恢复 $\hat{s}(n)$。在频域里,这意味着对每个频率 bin 施加一个增益 $G(k)$: $$ \hat{S}(k) = G(k) \cdot X(k) $$问题归结为:怎么求这个 $G(k)$? 2. 为什么选频域 时域降噪(比如 LMS 自适应滤波)需要参考信号,这在单麦克风场景下不现实。频域方法的好处是: 频率分辨率。噪声和语音在不同频段的能量分布差异很大。白噪声全频段平坦,语音能量集中在 300Hz–4kHz。频域处理可以逐 bin 调节衰减深度。 短时平稳假设更容易满足。一帧 10ms 内,噪声功率谱几乎不变,这给估计器提供了稳定的观测窗口。 计算量可控。256 点 FFT 在嵌入式平台上是成熟操作,远比时域长 FIR 滤波器经济。 代价是引入了帧延迟和频谱泄漏,这些后面会谈。 3. 总体处理流程 一帧 480 个采样点(48kHz × 10ms)进来后,经过以下环节: ...
琴谱 - Duvet
Duvet ← Previous Page 0 / 0 Next → 下载 PDF 正在加载 PDF 预览...
琴谱 - 蓬莱伝説 (Forest306)
蓬莱伝説 Forest306 编曲。 ← Previous Page 0 / 0 Next → 下载 PDF 正在加载 PDF 预览...
线性代数不难
线性代数不难 《线性代数不难》是生姜 DrGinger 编写的线性代数入门读物。本页面收录带书签的 PDF 版本,方便在线阅读与章节跳转。 ← Previous Page 0 / 0 Next → 下载 PDF 正在加载 PDF 预览...
散点到 3D 曲面:力场重建工程算法
散点到 3D 曲面:力场重建工程算法 一、目标与边界 本文说明如何将三角网格表面的离散 Cell 采样值重建为连续、稳定且可实时渲染的 3D 曲面力场,覆盖算法输入、几何预处理、曲面距离、插值、颜色映射、缓存、渲染和验证。 算法解决以下工程问题: STL 三角面重复顶点导致表面拓扑断裂。 三维欧氏距离会穿过薄壁、折角或独立零件传播数值。 离散 Cell 数值需要连续插值,但不能因邻近 Cell 数量增加而产生虚假峰值。 覆盖边缘需要自然回落到模型基底颜色,不能出现硬截断。 几何计算与实时帧更新必须分离,以满足连续数据刷新需求。 算法输入包括三角网格、Cell 位置与法线、当前帧采样值、显示范围和重建参数;输出为与渲染顶点一一对应的颜色数组。 1.1 算法全景流程 二、曲面采样模型 2.1 曲面距离问题 目标曲面具有曲率、折角、正反面和多个独立零件,不能把它视为平面或无拓扑关系的三维点集。 核心问题是:两个点在三维空间里看起来很近,是否意味着它们沿模型表面也很近? 2.2 Cell 语义:离散采样,不是独立力源 Cell 表示连续真实力场的离散采样点。 重建值表达附近采样值的加权平均,不把多个 Cell 的数值简单相加。 若四个相邻 Cell 都是 0.6,中间位置也应接近 0.6,不能仅因为附近有四个点就变成 2.4。 以下 12 个采样点用于说明算法在圆柱曲面上的输入形式: 点 z / mm θ / ° x / mm y / mm 归一化压力 S1 -6 -70 -5.638 37.948 0.18 S2 0 -70 -5.638 37.948 0.32 S3 6 -70 -5.638 37.948 0.46 S4 -6 -25 -2.536 34.562 0.42 S5 0 -25 -2.536 34.562 0.82 S6 6 -25 -2.536 34.562 0.68 S7 -6 25 2.536 34.562 0.55 S8 0 25 2.536 34.562 1.00 S9 6 25 2.536 34.562 0.76 S10 -6 70 5.638 37.948 0.28 S11 0 70 5.638 37.948 0.48 S12 6 70 5.638 37.948 0.36 三、Wendland C2 与归一化插值 3.1 紧支撑核 算法使用 Wendland C2 作为紧支撑权重函数: ...
Wendland Functions
Wendland Functions Wendland 函数是一类具有紧支集的径向基函数(Compactly Supported Radial Basis Functions),由 Holger Wendland 在其 1995 年的论文 Piecewise Polynomial, Positive Definite and Compactly Supported Radial Functions of Minimal Degree 中提出。 这类函数广泛应用于散乱数据插值(scattered data interpolation)、无网格方法(meshfree methods)等领域,其紧支撑特性使得插值矩阵具有稀疏性,显著降低了大规模问题的计算复杂度。 ← Previous Page 0 / 0 Next → 下载 PDF 正在加载 PDF 预览...