音频进阶学习十九——逆系统(简单进行回声消除)

文章目录


前言

在上一篇音频进阶学习十八------幅频响应相同系统、全通系统、最小相位系统文章中,我们在最小相位系统中提到了逆系统:一个稳定因果的LTI系统,同时会有一个稳定因果的逆系统。

本文中将会介绍逆系统的含义,通过对于零极点的分析,来对比逆系统和原系统的关联。最后,将会使用逆系统来对于给定的近端信号和参考信号来进行回声消除。

|版本声明:山河君,未经博主允许,禁止转载


一、可逆系统

1.定义

如果一个系统的输入是 x [ n ] x[n] x[n],经过系统 H ( z ) H(z) H(z)后系统的输出是 y [ n ] y[n] y[n],那么将 y [ n ] y[n] y[n]通过系统 G ( z ) G(z) G(z)后输出的是 x [ n ] x[n] x[n],此时系统 G ( z ) G(z) G(z)是系统 H ( z ) H(z) H(z)的逆系统。

而 G ( z ) G(z) G(z)和 H ( z ) H(z) H(z)的关系:

  • 频域关系
    G ( z ) H ( z ) = 1 = > g [ n ] = h [ n ] G(z)H(z)=1=>g[n]=h[n] G(z)H(z)=1=>g[n]=h[n]
  • 时域关系
    对于系统 H H H的脉冲响应有: y [ n ] = x [ n ] ∗ h [ n ] y[n]=x[n]*h[n] y[n]=x[n]∗h[n]
    对于系统 G G G的脉冲响应有: x ′ [ n ] = y [ n ] ∗ g [ n ] = ( x [ n ] ∗ h [ n ] ) ∗ g [ n ] = x [ n ] ∗ ( h [ n ] ∗ g [ n ] ) x'[n]=y[n]*g[n]=(x[n]*h[n])*g[n]=x[n] *(h[n]*g[n]) x′[n]=y[n]∗g[n]=(x[n]∗h[n])∗g[n]=x[n]∗(h[n]∗g[n])
    如果想要 x ′ [ n ] = x [ n ] x'[n]=x[n] x′[n]=x[n],那么 h [ n ] ∗ g [ n ] = δ n h[n]*g[n]=\delta n h[n]∗g[n]=δn

2.解卷积

对于给定的输入信号 x [ n ] x[n] x[n]和输出信号 y [ n ] y[n] y[n],我们知道通过系统有:
X ( f ) = F { x [ n ] } , Y ( f ) = F { x [ n ] } , H ( f ) = X ( f ) Y ( f ) X(f)=F\{x[n]\},Y(f)=F\{x[n]\},H(f)=\frac{X(f)}{Y(f)} X(f)=F{x[n]},Y(f)=F{x[n]},H(f)=Y(f)X(f)

通过逆变换, h [ n ] = F − 1 { H ( f ) } h[n]=F^{-1}\{H(f)\} h[n]=F−1{H(f)}得到冲激响应的这一过程,叫做解卷积

3.逆系统恢复原始信号过程

首先结合具体场景,假设发射了一个信号 x [ n ] x[n] x[n],通过多条途径例如墙体、窗户的反射,导致接收的信号是 y [ n ] y[n] y[n],我们想通过 y [ n ] y[n] y[n]恢复为原始的 x [ n ] x[n] x[n],如下图:

  • 发射约定好的信号 T [ n ] T[n] T[n]
  • 接收 T ′ [ n ] = T [ n ] ∗ h [ n ] T'[n]=T[n]*h[n] T′[n]=T[n]∗h[n]
  • 通过解卷积得到 h [ n ] h[n] h[n]
  • 构建逆系统 g [ n ] g[n] g[n]
  • 通过逆系统获得原始信号 x [ n ] = y [ n ] ∗ G [ n ] x[n]=y[n]*G[n] x[n]=y[n]∗G[n]

值得注意的是,在实际场景中,过程会比较复杂,例如不存在发射约定好的信号,或者场景变换导致冲激响应的变化等等,这就需要一个自适应滤波器不断的估计回声信号,当然这是以后的内容。

4.逆系统与原系统的零极点关系

对于Z变换的频率响应,它的逆系统
H ( z ) = ∏ m = 1 M ( 1 − c m z − 1 ) ∏ n = 1 N ( 1 − d n z − 1 ) , G ( z ) = ∏ n = 1 N ( 1 − d n z − 1 ) ∏ m = 1 M ( 1 − c m z − 1 ) H(z)=\frac{\prod_{m=1}^M(1-c_mz^{-1})}{\prod_{n=1}^N(1-d_nz^{-1})},G(z)=\frac{\prod_{n=1}^N(1-d_nz^{-1})}{\prod_{m=1}^M(1-c_mz^{-1})} H(z)=∏n=1N(1−dnz−1)∏m=1M(1−cmz−1),G(z)=∏m=1M(1−cmz−1)∏n=1N(1−dnz−1)

也就是原系统 H ( z ) H(z) H(z)和逆系统 G ( z ) G(z) G(z)的零极点是互换的,那么此时如果逆系统 G ( z ) G(z) G(z)是因果稳定的,那么逆系统 G ( z ) G(z) G(z)的零点和极点都在单位圆内,而此时原系统的 H ( z ) H(z) H(z)零点和极点同样都在单位圆内,此时都为最小相位系统。

这也是上一篇文章中最小相位系统提到的特性。

二、使用逆系统去除回声

现在使用matlab来设计一个逆系统,对于已知的输入信号和输出信号进行转换。

获取原信号的频谱

需要注意的是,这里的输入信号和输出信号是笔者自己通过pcm叠加转换的,所以频域上可能看不出特别大的区别,但是实际可以明显听到回声。

核心代码

matlab 复制代码
%输入信号和输出信号频域图
H_in=fft(input_signal);
H_out=fft(output_signal);
R_in = abs(H_in);
R_out = abs(H_out);
freq=linspace(0,fs/2, floor(min_len/2));

figure;
subplot(2,1,1);
plot(freq, R_in(1:floor(min_len/2)));
title("输入信号频域图");xlabel("频率H(z)");ylabel("幅度");grid on;
subplot(2,1,2);
plot(freq, R_out(1:floor(min_len/2)));
title("输出信号频域图");xlabel("频率H(z)");ylabel("幅度");grid on;

得到结果:

原系统和逆系统幅频响应和相频响应

通过图像幅频响应图像和相频响应的图像,可以很清楚的看到原系统和逆系统的幅频响应和相频响应是倒过来的。

同时值得注意的是:

  • 某些频率上幅值接近零(即原系统在这些频率上有很强的衰减),那么其倒数 H i n v H_{inv} Hinv可能会变得非常大,导致放大效应。
  • 在计算 H i n v H_{inv} Hinv浮点数精度问题可能会导致不稳定。
  • 幅度部分可能被正常恢复,但相位的误差会导致合成信号在时域上发生相干叠加,造成某些时刻的峰值更大,最终影响整体信号能量。

而这些问题需要结合窗口平滑来进行解决,这里是通过增加了一些阈值来减少极端值的出现。

代码如下:

matlab 复制代码
H_c = H_in ./ (H_out + eps);
%H_inv= 1 ./ (H_c + eps);
%H_inv(abs(H_c) < 1e-3) = 0;
H_inv = 1 ./ (H_c + 0.01);
H_inv(abs(H_c) < 1e-2) = 0;
R_c=abs(H_c);
R_inv=abs(H_inv);

figure;
subplot(2,2,1);
plot(freq, 20*log10(R_c(1:floor(min_len/2))));
title('原系统幅频响应');xlabel('频率 (Hz)'); ylabel('幅度 (dB)'); grid on;
subplot(2,2,2);
plot(freq, rad2deg(angle(H_c(1:floor(min_len/2)))));
title('原系统相频响应');xlabel('频率 (Hz)'); ylabel('相位(度)'); grid on;
subplot(2,2,3);
plot(freq, 20*log10(R_inv(1:floor(min_len/2))));
title('逆系统幅频响应');xlabel('频率 (Hz)'); ylabel('幅度 (dB)'); grid on;
subplot(2,2,4);
plot(freq, rad2deg(angle(H_inv(1:floor(min_len/2)))));
title('逆系统相频响应');xlabel('频率 (Hz)'); ylabel('相位(度)'); grid on;

得到

使用逆系统恢复原始信号

最后恢复的信号还是变大了,但是此时已经听不见回声了,这里在恢复时已经使用整体能量归一化来尽量减少能量放大的影响。

代码如下:

matlab 复制代码
%还原后的信号频谱
restored_signal = real(ifft(fft(output_signal) .* H_inv));
restored_signal = restored_signal / norm(restored_signal) * norm(input_signal);

restored_signal = int16(restored_signal);
fwrite(turn_file, restored_signal, 'int16');
fclose(turn_file);
H_restored = fft(restored_signal);
R_res = abs(H_restored);
figure;
subplot(1,1,1);
plot(freq, R_res(1:floor(min_len/2)));
title('还原信号的频域图');
xlabel('频率 (Hz)'); ylabel('幅度'); grid on;

得到

整体代码如下

matlab 复制代码
fs=16000;
input_file=fopen("input.pcm",'rb');
output_file=fopen("output.pcm",'rb');
turn_file=fopen("turn.pcm",'wb+');

input_signal=fread(input_file, inf, 'int16');
output_signal=fread(output_file, inf, 'int16');
fclose(input_file);
fclose(output_file);

min_len=min(length(input_signal), length(output_signal));
input_signal=input_signal(1:min_len);
output_signal=output_signal(1:min_len);

%输入信号和输出信号频域图
H_in=fft(input_signal);
H_out=fft(output_signal);
R_in = abs(H_in);
R_out = abs(H_out);
freq=linspace(0,fs/2, floor(min_len/2));

figure;
subplot(2,1,1);
plot(freq, R_in(1:floor(min_len/2)));
title("输入信号频域图");xlabel("频率H(z)");ylabel("幅度");grid on;
subplot(2,1,2);
plot(freq, R_out(1:floor(min_len/2)));
title("输入信号相频响应");xlabel("频率H(z)");ylabel("幅度");grid on;


%原系统和逆系统幅频响应和相频响应
H_c = H_in ./ (H_out + eps);
%H_inv= 1 ./ (H_c + eps);
%H_inv(abs(H_c) < 1e-3) = 0;
H_inv = 1 ./ (H_c + 0.01);
H_inv(abs(H_c) < 1e-2) = 0;
R_c=abs(H_c);
R_inv=abs(H_inv);

figure;
subplot(2,2,1);
plot(freq, 20*log10(R_c(1:floor(min_len/2))));
title('原系统幅频响应');xlabel('频率 (Hz)'); ylabel('幅度 (dB)'); grid on;
subplot(2,2,2);
plot(freq, rad2deg(angle(H_c(1:floor(min_len/2)))));
title('原系统相频响应');xlabel('频率 (Hz)'); ylabel('相位(度)'); grid on;
subplot(2,2,3);
plot(freq, 20*log10(R_inv(1:floor(min_len/2))));
title('逆系统幅频响应');xlabel('频率 (Hz)'); ylabel('幅度 (dB)'); grid on;
subplot(2,2,4);
plot(freq, rad2deg(angle(H_inv(1:floor(min_len/2)))));
title('逆系统相频响应');xlabel('频率 (Hz)'); ylabel('相位(度)'); grid on;

%还原后的信号频谱
restored_signal = real(ifft(fft(output_signal) .* H_inv));
restored_signal = restored_signal / norm(restored_signal) * norm(input_signal);

restored_signal = int16(restored_signal);
fwrite(turn_file, restored_signal, 'int16');
fclose(turn_file);
H_restored = fft(restored_signal);
R_res = abs(H_restored);
figure;
subplot(1,1,1);
plot(freq, R_res(1:floor(min_len/2)));
title('还原信号的频域图');
xlabel('频率 (Hz)'); ylabel('幅度'); grid on;

总结

本文中所使用的逆系统是基于原系统是一个最小相位系统来构建的。那如果原系统不是最小相位系统是存在一个不会发散的逆系统呢?其实答案已经在上一篇文章中给出了,那就是使用全通分解,去除零极点在单位圆外的影响。只使用全通分解中对于最小相位系统的部分。

其次,值得注意的是,本文中的例子是给定了参考信号来进行回声消除,但在实际环境中,对于参考信号如何辨别是否是回声,这需要使用自适应滤波器来进行回声建模。

反正收藏也不会看,不如点个赞吧!

相关推荐
绿算技术9 分钟前
DPU的架构:模块化与可扩展性
科技·算法·缓存·架构
仟濹29 分钟前
【算法 C/C++】二维前缀和
c语言·c++·算法
Chenyu_31034 分钟前
04.基于C++实现多线程TCP服务器与客户端通信
linux·服务器·网络·c++·tcp/ip·算法·visualstudio
coder阿龙43 分钟前
【UNIAPP】获取视频的第一帧作为封面(基于视频URL,Canvas)复制即用
前端·uni-app·音视频
晴空对晚照1 小时前
[动手学习深度学习]12.权重衰退
人工智能·深度学习·学习
宇寒风暖1 小时前
HTML嵌入CSS样式超详解(尊享)
前端·css·笔记·学习·html
Dante7982 小时前
【数据结构】二叉搜索树、平衡搜索树、红黑树
数据结构·c++·算法
驼驼学编程2 小时前
决策树,Laplace 剪枝与感知机
算法·决策树·剪枝
坚强小葵2 小时前
实验8-2-1 找最小的字符串
c语言·算法
虾球xz2 小时前
游戏引擎学习第149天
人工智能·学习·游戏引擎