本文已参与「新人创作礼」活动,一起开启掘金创作之路。
想做一个PID温控电烙铁,首先需要测定系统参数,便于以后的仿真和选择PID参数 设电烙铁发热器的发热功率为(输入),比外界温度高为(输出),热导为,热容量为,可列出以下微分方程: 时间常数 该微分方程的通解 先来看看仿真结果
# 参数
C = 20
G = 1
t = np.arange(0, 600, 0.1)
Pf = lambda t: 1 + np.sin(t**2/5000)
P = Pf(t) #发热功率必须为正数
# 生成数据,加入噪声
#pn = P.mean()/50
#P += np.random.normal(0, pn, P.shape) # 实际功率噪声,odeint无效
# 使用通解进行计算,但数值太大,可能会浮点溢出,不建议使用
T = 1/C*np.exp(-G/C*t)*np.cumsum(P*np.exp(G/C*t))*(t[1]-t[0])
# 使用scipy求解ODE,不能仿真实际功率噪声
#T = odeint(lambda T,t_:(Pf(t_)-T*G)/C, 0, t)[:, 0]
pn = P.mean()/50
P += np.random.normal(0, pn, P.shape) # 功率测量噪声
tn = T.mean()/50
T += np.random.normal(0, tn, T.shape) # 温度测量噪声
# 温度测量比功率测量落后(>0)或超前(<0)多少帧
dt = 0
if dt > 0:
P, T, t = P[:-dt], T[dt:], t[:-dt]
if dt < 0:
P, T, t = P[-dt:], T[:dt], t[:dt]
# 绘图
plt.plot(t, P, c='r')
plt.twinx()
plt.plot(t, T, c='b')
plt.show()
傅里叶变换一下,看看频谱
Pw = np.fft.rfft(P)
Tw = np.fft.rfft(T)
PwTw = Pw/Tw
w = np.linspace(0, 2*np.pi/(2*(t[1]-t[0])), Pw.shape[0])
n = 30
plt.plot(w[:n], np.log(np.abs(Pw[:n])), c='r')
plt.twinx()
plt.plot(w[:n], np.log(np.abs(Tw[:n])), c='b')
plt.show()
经过傅里叶变换,变为 实部 虚部 实际测量到的数据是有噪声的
n = 30
plt.plot(w[:n], PwTw.real[:n], c='r')
plt.twinx()
plt.plot(w[:n], PwTw.imag[:n], c='b')
plt.twiny()
plt.xlim(-1, n+1) # 数组索引
plt.show()
只要X(s)输入啁啾信号,这样可以在实验中测定和
另外,实际测量到的数据是有噪声的,为了提高分辨率,需要进行最小二乘法
\frac{\partial Q}{\partial G}=-2\sum[ Re(\varepsilon_i)Re(Y_i)+Im(\varepsilon_i)Im(Y_i)]=0 \\
\frac{\partial Q}{\partial U}=2\sum[Re(\varepsilon_i)Im(Y_i)-Im(\varepsilon_i)Re(Y_i)]=0 \\
\end{cases}$$
$Re(X_i-j\omega_iCY_i-GY_i)Re(Y_i)+Im(X_i-j\omega_iCY_i-GY_i)Im(Y_i)=0$
$\sum Re(X_i)Re(Y_i)+Im(X_i)Im(Y_i)=G\sum [Re^2(Y_i)+Im^2(Y_i)]$
$\hat G=\frac{\sum[Re(X_i)Re(Y_i)+Im(X_i)Im(Y_i)]}{Re^2(Y_i)+Im^2(Y_i)}=\frac{\sum(\bar XY+X\bar Y)}{2\sum\left|Y_i\right|^2}$
$\sum Re(X_i-j\omega_iCY_i-GY_i)Im(Y_i)-Im(X_i-j\omega_iCY_i-GY_i)Re(Y_i)=0$
$\sum Im(X_i)Re(Y_i)-Re(X_i)Im(Y_i)=C\sum\omega_i[Re^2(X_i)+Im^2(Y_i)]$
$\hat C=\frac{[\sum Im(X_i)Re(Y_i)-Re(X_i)Im(Y_i)]}{\omega_i[Re^2(X_i)+Im^2(Y_i)]}=\frac{\sum(X\bar Y-\bar XY)}{2i\sum\omega_i\left|Y_i\right|^2}$
```python
n = 10
Tw2 = Tw[:n]
Pw2 = Pw[:n]
w2 = w[:n]
G_ = np.sum(Pw2.real*Tw2.real+Pw2.imag*Tw2.imag) / np.sum(np.abs(Tw2)**2)
print(G_)
C_ = np.sum(Pw2.imag*Tw2.real-Pw2.real*Tw2.imag) / np.sum(w2*np.abs(Tw2)**2)
print(C_)
```
```
1.0280348744030454
19.339144120677265
```
接下来尝试另外一种线性近似的噪声分析的方法:
$X_测(\omega)=X(\omega)+X_噪(\omega)$
$Y_测(\omega)=Y(\omega)+Y_噪(\omega)$
假设噪声为相互独立的白噪声,即时域上是$x_噪(t)$和$y_噪(t)$是与$t$无关的正态随机变量。则频域上$X_噪(\omega)$和$Y_噪(\omega)$的实部和虚部都是与$\omega$无关的正态随机变量
由于噪声很小,可以采取线性近似。好像有点不严谨,因为这些都是复数,我复变函数这门课忘光了,可能会用错。
$\frac{X_测}{Y_测}=\frac{X+X_噪}{Y+Y_噪}\approx(1+\frac{\partial}{\partial X}X_噪+\frac{\partial}{\partial Y}Y_噪)\frac{X}{Y}$
$\frac{X_测}{Y_测}-\frac{X}{Y}\approx(\frac{\partial}{\partial X}X_噪+\frac{\partial}{\partial Y}Y_噪)\frac{X}{Y}\approx(\frac{\partial}{\partial X_测}X_噪+\frac{\partial}{\partial Y_测}Y_噪)\frac{X_测}{Y_测}$
$=(\frac{1}{Y_测}X_噪-\frac{X_测}{Y_测^2}Y_噪)\frac{X_测}{Y_测}$
在实际FFT计算中,根据帕塞瓦尔定理
$std(X_噪)=\sqrt N*std(x_噪)$
$std(Y_噪)=\sqrt N*std(y_噪)$
$H^{-1}_噪(\omega)=\frac{X_测}{Y_测}-\frac{X}{Y}\approx\sqrt N[\frac{1}{Y_测}std(x_噪)-\frac{X_测}{Y_测^2}std(y_噪)]\frac{X_测}{Y_测}$
使用加权平均估计$G$和$C$
$\hat G=\sum_{\omega=0}^{n}\frac{1/H^{-1}_噪[\omega]}{\sum_{i=1}^{n}(1/H^{-1}_噪[i])}Re(\frac{X_测}{Y_测})$
$\hat C=\sum_{\omega=0}^{n}\frac{\omega/H^{-1}_噪[\omega]}{\sum_{i=1}^{n}(\omega/H^{-1}_噪[i])}\frac{1}{\omega}Im(\frac{X_测}{Y_测})$
```python
n = 12
Tw2 = Tw[:n]
Pw2 = Pw[:n]
w2 = w[:n]
PwTw2 = Pw2/Tw2
H1z = np.sqrt(len(t))*(pn/Tw2-Pw2/Tw2**2*tn)
H1z = np.abs(H1z)**(1/8) # H^-1的误差
wG = 1/H1z/(1/H1z).sum()
G_ = np.sum(wG*PwTw2.real)
plt.plot(w2, wG, c='r') # 画热导的权重曲线
print(G_)
w2 = w2[2:]
H1z = H1z[2:]
PwTw2 = PwTw2[2:]
wC = w2/H1z/(w2/H1z).sum()
C_ = np.sum(wC*PwTw2.imag/w2)
plt.twinx()
plt.plot(w2, wC, c='b') # 画热容的权重曲线
print(C_)
plt.show()
```
```
1.0217989734791504
19.80496304275252
```

$G$需要选取适当的高频截断点,$C$需要选择一定频谱范围,精度与最小二乘法差不多。因为使用了加权,是有偏估计。还有不稳定,选择不恰当的参数会使误差会很大。操作起来还会很麻烦。
花了那么多精力计算和整理,这段文字还是保留下来吧。提示以后不要用随便想出来的算法,真的想用也至少得先去验证一下,跟常规算法的性能比较一下再应用。
我不想在CSDN上写博客了,贴张图都很麻烦。我想在jupyter notebook上写,再挂到gitee上供大家浏览。
另外PID仿真程序的代码已经写好了,但没有什么好说的,我有空会发到gitee,有需要可以联系我。