Python做信号处理的零散笔记

0. 本文目的

  • 主要辅助信号与系统课程的学习与讲解。
  • 一些零碎的记录,以后再酌情细化。

1. 利用sympy库

基本方式是直接定义函数,之后调用自带的函数做处理、计算、变换,以及调用plot函数画图。

  1. 可以描述连续函数波形。可以描述冲激信号(DiracDelta)、阶跃信号(Heaviside)、符号函数(sign)等多种函数的波形。
  2. 可以波形变换(subs)、求导、定积分(integrate)、卷积(好像没看到,但可以用定积分定义)、傅里叶变换(fourier_transform)、得到复函数的幅度相位等、以及微分方程求通解等。
  3. 可以将表达式转为numpy(lambdify),这个感觉很方便。

一些坑:

  1. 如何描述离散信号,这个还没来得及看,其实有个离散的模块,好像也有FFT之类的东西,挺全的。但感觉怎么也不会比numpy更方便,暂时不太打算看了。
  2. 自带的plot方法画图,感觉很弱,目前没有搞定的几个点:
  • 如何画虚线和网格,没看到官方的例子。
  • 如何嵌入到matplotlib,网上有一些教程,有点麻烦,还没来得及看,但感觉不值得。因为可以将sympy表达式转为numpy序列之后再画图。
  1. sympy里的sinc函数实际是Sa函数(不是Sa(pi*t)),文档里到是也说了;numpy当中的sinc就是真正的Sa函数。
  2. 尝试利用定积分函数integrate和符号替换(subs)定义连续信号的卷积,但很受限制,主要问题:
  • 很慢,感觉上这东西是“数学”工具(吗?),不能做工程工具。
  • 一些复杂函数积分的时候会卡死,懒得总结规律了。有个奇葩的例子:
x = Heaviside(t)-Heaviside(t-0.1)
h = sinc(pi * 2 * 25 * t)
#下一步做卷积有点慢,plot画图的时候卡死

x = Heaviside(t)-Heaviside(t-0.1*pi)
h = sinc(2 * 25 * t)
#一切正常,还有点快
  • 会出现一些很复杂的积分结果,无法化简,但有时候化简得就特别完美。但特别慢。
  • 积分上下限而可能需要根据计算结果调整:1,计算方波卷积sin区间的时候,上下限用无穷,可以得到一个很简化的表达式,基本是最简形式。如果自行设定一个足够大的上下限,虽然图像正确,但表达式超级复杂,simplify也简化不了;2,如果计算方波卷积方波,则给一个小的上下限比较好。积分限给正负无穷,则表达式无法化简、画图卡死。
  1. fourier_transforms对复杂函数也很难处理,情况和上面积分、卷积的情况类似,比如基本的sin、cos信号的频谱等(可能是因为包频谱还有冲激形式)。

2. 利用numpy

总的来说挺强大的,啥都能干。
一些笔记:

  1. 描述连续信号的时候会觉得有点奇怪——只说描述方式,不考虑底层原理。sympy描述的是一个连续的表达式,但numpy代码描述的是一个序列(这个实际也算不上坑)。
  2. 做FFT的时候,出现过相位谱混乱的情况,问题和解决方案:
#产生横坐标序列,如果采用np.arange,长度需要加入一个sample_interval,否则相位图会混乱,可能和arange的特性有关
t=np.arange(0,10+sample_interval,sample_interval)
#如果采用linspace则没有问题,注意最后一个参数是采样个数
t=np.linspace(0,10,int(10/sample_interval))

但总的来说,FFT相位谱的效果,如果仅利用基本的傅里叶变换原理不是很好解释。

  1. 对于Sa函数(截短),有两种定义方式,即sint/t和直接使用sinc函数:
t1=np.linspace(-10,10,int(20/sample_interval))
np.seterr(divide='ignore',invalid='ignore')#忽略除以0的警告
ht1 =np.sin( 2 * np.pi * 25 * t1)/(2 * np.pi * 25 * t1)
ht1[0]=1 #特殊点直接赋值了
ht2 = np.sinc( 2  * 25 * t1)
#看一下相减不为零的个数,对比序列长度(6875 和20480)
ht3=(ht1-ht2)
print(np.count_nonzero(ht3),len(ht3))

这两个定义方式出来的序列是不一样的,可能和np.pi的精度有关,但用起来效果基本相同。


image.png

sint/t的顶部放大(sinc的顶部是平的):


image.png
  1. 模拟连续信号卷积,卷积结果存在幅度问题,实际就是一个连续时间卷积的数值近似方法问题,这里算是换了个方式推导一下。
image.png

按照连续卷积的定义,卷积结果的最大值应该是2,参见sympy的结果:


image.png

原因:
numpy(以及scipy)的卷积是离散卷积,卷积结果的最大值和序列的个数有关。例如:

[1,1]*[1,1]=[1,2,1]
[1,1,1]*[1,1,1]=[1,2,3,2,1]

注意上述两个卷积的形状是一样的,但高度不同。

用numpy模拟连续信号时,模拟信号的长度和实际数字序列的长度不是一回事:

t=np.arange(-10,10,delta)
t=np.linspace(-10,10,sample_amout) #sample_amout = 20 * delta

模拟信号的长度是20,数字序列的长度是10*delta=sample_amout。区间一定的情况下,序列个数越多,则离散卷积的幅度越大,因此要进行修正。修正方式:
模拟卷积结果就是:convolve 乘以 delta,公式如下:


image.png

或者convolve结果 乘以两个信号的模拟长度 ,再除以两个信号的序列个数。

  1. 扩展问题,如果卷积时两个信号的抽样率不同,则卷积结果可能有误,例如:
    定义两个u(t)-u(t-1)的方波,理论上卷积应该得到一个从0到2的三角波。
    但如果第二个方波的采样率是前者的一倍,则可以理解为是一个窄方波卷积一个宽方波,此时会得到一个梯形。


    image.png

    类似的问题结合理论分析应该还能找到其他的吧,但并不难解决。

  2. 画信号频谱图有两种方式:

  • 直接调用matplotlib的 magnitude_spectrum和phase_spectrum,输入时域的信号(signal)即可作图,最好结合实际给一个Fs抽样间隔参数(比如:1/信号长度),别的参数都是可选项,能省大概5、6条语句吧,但貌似坐标轴的label不能调,可能是没找对地方吧。
plt.magnitude_spectrum(signal, Fs=sample_freq )
plt.phase_spectrum(signal,  Fs = sample_freq)
  • 手动FFT,再用matplotlib画折线图,需要自己求幅度相位、自己搞定坐标、自己做归一化。
    好处是自己画图可以做更多的美化,但自己做FFT的
    时候需要注意一个小问题:
fft_data= fft(et)
fft_amp= np.abs(fft_data) * deltaT # 双边幅度谱
xAxis = fftfreq(len(fft_data1), deltaT )#fft的双边频域坐标

fft_amp1 = fftshift(fft_amp1)#修改坐标范围,方便画图
xAxis = fftshift(xAxis ) #修改坐标范围,方便画图

fftfreq和fft语句,输出的数据和坐标顺序为:[0到正坐标最大值,负坐标最大值到0],即不是从小到大排列的。者在画图的时候会出现一些奇怪的线,从正坐标最大值一直连到负的坐标最大值,
例如:

x = [1,2,3,4,5,-5,-4,-3,-2,-1] #x轴
y = [1,2,3,4,5, 6, 7, 8, 9, 10] #数据

出来的图为:


image.png

解决这个问题,需要执行fftshift语句,将数据的坐标顺序改为从小到达,相当于改为:

x = [1,2,3,4,5,-5,-4,-3,-2,-1]
y = [1,2,3,4,0, 0, 7, 8, 9, 10]

3. 利用Sympy再转numpy

利用sympy定义连续函数表达式,可以处理根据需要做函数变换、卷积、处理等,得到解析解,然后再转到numpy序列,再画图。

n = np.arange(-3, 3,0.01)
ft = Heaviside(t+1)-Heaviside(t-1) #sympy表达式
ftn = lambdify(t, ft, "numpy") #转为可执行函数,并且是numpy类型的函数
r = ftn(n) #np序列

或者

n = np.arange(-3, 3,0.01)
r = np.array([i*(Heaviside(i)-Heaviside(i-1)) for i in n])

4. 利用SCIPY

一是能做FFT这些,但numpy也能做,简单使用都差不多。
二是处理s域或z域的系统函数,能描述函数、求零极点(tf2zpk)、状态方程和特征值(tf2ss、eig、eigh)、求频响特性(freqs,freqz)
三是能做2D卷积,图像处理等。numpy下暂时没找到现成的二维卷积方法。

5. control库

主要是系统函数的描述(tf)、求零极点(pzmap)、频响特性(bode_plot)。方便之处是直接可以画图,比如z域零极点图可以一句话出单位圆和零极点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容