PID原理及python简单实现与调参

一、前言

近期在实际项目中使用到了PID控制算法,于是就该算法做一总结。

二、PID控制算法详解

2.1 比例控制算法

例子:
假设一个水缸,需要最终控制水缸的水位永远维持在1米的高度。

水位目标:T
当前水位:Tn
加水量:U
误差:error
error=T-Tn
比例控制系数:kp
U = k p ∗ e r r o r U = k_p * error U=kperror
initial: T=1; Tn=0.2, error=1-0.2=0.8; kp=0.4

2.1.1 比例控制python简单示意

T=1
Tn=0.2
error=1-0.2
kp=0.4

for t in range(1, 10):
    U = kp * error
    Tn += U
    error = T-Tn
    print(f't={t} | add {U:.5f} => Tn={Tn:.5f} error={error:.5f}')
   
"""
t=1 | add 0.32000 => Tn=0.52000 error=0.48000
t=2 | add 0.19200 => Tn=0.71200 error=0.28800
t=3 | add 0.11520 => Tn=0.82720 error=0.17280
t=4 | add 0.06912 => Tn=0.89632 error=0.10368
t=5 | add 0.04147 => Tn=0.93779 error=0.06221
t=6 | add 0.02488 => Tn=0.96268 error=0.03732
t=7 | add 0.01493 => Tn=0.97761 error=0.02239
t=8 | add 0.00896 => Tn=0.98656 error=0.01344
t=9 | add 0.00537 => Tn=0.99194 error=0.00806
"""

2.1.2 比例控制存在的一些问题

根据kp取值不同,系统最后都会达到1米,只不过kp大了达到的更快。不会有稳态误差。
若存在漏水情况,在相同情况下,经过多次加水后,水位会保持在0.75不在再变化,因为当U和漏水量一致的时候将保持不变——即稳态误差
U = k p ∗ e r r o r = 0.1 = > e r r o r = 0.1 / 0.4 = 0.25 U=k_p*error=0.1 => error = 0.1/0.4 = 0.25 U=kperror=0.1=>error=0.1/0.4=0.25,所以误差永远保持在0.25

T=1
Tn=0.2
error=1-0.2
kp=0.4
extra_drop = 0.1

for t in range(1, 100):
    U = kp * error
    Tn += U - extra_drop
    error = T-Tn
    print(f't={t} | add {U:.5f} => Tn={Tn:.5f} error={error:.5f}')

"""
t=95 | add 0.10000 => Tn=0.75000 error=0.25000
t=96 | add 0.10000 => Tn=0.75000 error=0.25000
t=97 | add 0.10000 => Tn=0.75000 error=0.25000
t=98 | add 0.10000 => Tn=0.75000 error=0.25000
t=99 | add 0.10000 => Tn=0.75000 error=0.25000
"""

实际情况中,这种类似水缸漏水的情况往往更加常见

  • 比如控制汽车运动,摩擦阻力就相当于是"漏水"
  • 控制机械臂、无人机的飞行,各类阻力和消耗相当于"漏水"

所以单独的比例控制,很多时候并不能满足要求

2.2 积分控制算法(消除稳态误差)

比例+积分控制算法:
U = k p ∗ e r r o r + k i ∗ ∑ e r r o r U = k_p*error + k_i * \sum error U=kperror+kierror

  • ∑ e r r o r \sum error error: 误差累计
  • k i k_i ki: 积分控制系数

2.2.1 python简单实现

T=1
Tn=0.2
error=1-0.2
kp=0.4
extra_drop = 0.1
ki=0.2
sum_error = 0

for t in range(1, 20):
    sum_error += error
    U = kp * error + ki * sum_error
    Tn += U - extra_drop
    error = T-Tn
    print(f't={t} | add {U:.5f} => Tn={Tn:.5f} error={error:.5f}')


"""
t=14 | add 0.10930 => Tn=0.97665 error=0.02335
t=15 | add 0.11025 => Tn=0.98690 error=0.01310
t=16 | add 0.10877 => Tn=0.99567 error=0.00433
t=17 | add 0.10613 => Tn=1.00180 error=-0.00180
t=18 | add 0.10332 => Tn=1.00512 error=-0.00512
t=19 | add 0.10097 => Tn=1.00608 error=-0.00608
"""

2.3 微分控制算法(减少控制中的震荡)

在越靠近目标的时候则加的越少。
U = k d ∗ ( e r r o r t − e r r o r t − 1 ) U=k_d*(error_t - error_{t-1}) U=kd(errorterrort1)

  • kd: 微分控制系数
  • d_error/d_t ~= error_t - error_t_1:误差的变化

加入微分控制算法的python简单示意

令:kd=0.2; d_error = 当前时刻误差-前时刻误差

T=1
Tn=0.2
error=1-0.2
kp=0.4
extra_drop = 0.1

ki=0.2
sum_error = 0

kd=0.2
d_error = 0
error_n = 0
error_b = 0

for t in range(1, 20):
    error_b = error_n
    error_n = error
    # print(error_b1, error_b2)
    d_error = error_n - error_b if t >= 2 else 0
    sum_error += error
    U = kp * error + ki * sum_error + kd * d_error
    Tn += U - extra_drop
    error = T-Tn
    print(f't={t} | add {U:.5f} => Tn={Tn:.5f} error={error:.5f} | d_error: {d_error:.5f}')

"""
t=14 | add 0.09690 => Tn=0.96053 error=0.03947 | d_error: 0.01319
t=15 | add 0.10402 => Tn=0.96455 error=0.03545 | d_error: 0.00310
t=16 | add 0.10808 => Tn=0.97263 error=0.02737 | d_error: -0.00402
t=17 | add 0.10951 => Tn=0.98214 error=0.01786 | d_error: -0.00808
t=18 | add 0.10899 => Tn=0.99113 error=0.00887 | d_error: -0.00951
t=19 | add 0.10727 => Tn=0.99840 error=0.00160 | d_error: -0.00899
"""

2.4 PID算法总结

pid = 比例控制(基本控制) + 积分控制(消除稳态误差)+微分控制(减少震荡)
U ( t ) = K p ∗ e r r o r t + K i ∑ i = 0 t e r r o r i + K d ∗ ( e r r o r t − e r r o r t − 1 ) U(t) = K_p * error_t + K_i\sum_{i=0}^{t}error_i + K_d*(error_t - error_{t-1}) U(t)=Kperrort+Kii=0terrori+Kd(errorterrort1)

  • K p K_p Kp:快速调整 比例控制P control能够提高系统的响应速度和稳态精度,抑制扰动对系统稳态的影响。但过大的比例控制容易导致系统超调和振荡,并且有可能使得系统变得不稳定。 纯比例控制并不能消除稳态误差,存在静差。
  • K i K_i Ki:准确到最终目标,积分控制能够消除0型系统对于常值输入信号和常值扰动造成的输出稳态误差,可以与P control一起组成PI control。积分控制的常数 根据系统所需的动态进去选取,并不会影响消除误差的效果,具有一定的鲁棒性。
  • K d K_d Kd: 稳步迭代, 微分器会对高频噪音有放大的效果

for kp_i in np.linspace(0, 1, 10):
pid_plot(kp=kp_i, ki=0.2, kd=0.2)

for ki_i in np.linspace(0, 1, 10):
pid_plot(kp=0.5, ki=ki_i, kd=0.2)

for kd_i in np.linspace(0, 1, 10):
pid_plot(kp=0.5, ki=0.2, kd=kd_i)

pid_plot(kp=0.65, ki=0.05, kd=0.5, print_flag=True)

三、牛顿法调参

损失函数采用:RMSE

from scipy import optimize 
import matplotlib.pyplot as plt
import numpy as np

def pid_plot(args, plot_flag=True, print_flag=False):
    kp, ki, kd = args
    T=1
    Tn=0.2
    error=1-0.2
    extra_drop = 0.1
    sum_error = 0
    d_error = 0
    error_n = 0
    error_b = 0
    Tn_list = []
    for t in range(1, 100):
        error_b = error_n
        error_n = error
        d_error = error_n - error_b if t >= 2 else 0
        sum_error += error
        U = kp * error + ki * sum_error + kd * d_error
        Tn += U - extra_drop
        error = T-Tn
        Tn_list.append(Tn)
        if print_flag:
            print(f't={t} | add {U:.5f} => Tn={Tn:.5f} error={error:.5f} | d_error: {d_error:.5f}')

    if plot_flag:
        plt.plot(Tn_list)
        plt.axhline(1, linestyle='--', color='darkred', alpha=0.8)
        plt.title(f'$K_p$={kp:.3f} $K_i$={ki:.3f} $K_d$={kd:.3f}')
        plt.ylim([0, max(Tn_list) + 0.2])
        plt.show()

    loss = np.sqrt(np.mean(np.square(np.ones_like(Tn_list) - np.array(Tn_list))))
    return loss



boundaries=[(0, 2), (0, 2), (0, 2)]
res = optimize.fmin_l_bfgs_b(pid_plot, np.array([0.1, 0.1, 0.1]), args=(False, False), bounds = boundaries, approx_grad = True)

pid_plot(res[0].tolist(), print_flag=True)
pid_plot([0.65, 0.05, 0.5], print_flag=True)
  • 牛顿法调参结果图示
    在这里插入图片描述
  • 简单手动调参图示
    在这里插入图片描述
<think>好的,用户现在问的是“PID如何调参”。之前他们问过减速比在电机控制系统中的整合,现在转向PID调参,可能是他们在实际项目中遇到了控制效果不理想的问题,需要调整PID数来优化系统响应。 首先,我需要回顾PID的基本原理,确保用户理解比例、积分、微分三个环节的作用。用户可能有嵌入式系统的背景,比如使用STM32之类的微控制器,所以在解释时需要结合代码实现,但这次的重点是调参方法而不是代码结构。 接下来,用户可能希望得到具体的调参步骤,而不仅仅是理论。他们之前接触过减速比的计算,可能对实际工程中的数调整有经验,但PID调参可能对他们来说还比较陌生。需要分步骤讲解,比如手动调参法、齐格勒-尼克尔斯法,或者现代的自适应方法,但考虑到实用性,手动和经典方法可能更合适。 用户可能在实际调试中遇到振荡、超调大、响应慢等问题,所以需要针对不同现象给出对应的调整策略。比如,如果系统振荡,可能需要减小比例增益或增加微分;如果存在稳态误差,可能需要调整积分时间。 另外,用户可能关心调参时的注意事项,比如测量噪声对微分项的影响,积分饱和的问题,以及如何平衡响应速度和稳定性。这些实际应用中的细节对于他们正确调参非常重要,需要详细说明。 还要考虑他们是否在特定系统中应用PID,比如温度控制、电机位置控制等,不同系统可能需要不同的调参策略。但根据之前的对话,用户可能涉及电机控制,所以可以举例说明电机控制中的调参注意事项,比如考虑减速比带来的影响,或者机械系统的惯性。 最后,需要总结调参的步骤,可能给出一个流程图或步骤列表,方便用户按部就班地进行调试。同时提醒他们调参是一个需要耐心和反复试验的过程,可能需要结合理论分析和实际观察来找到最佳数。</think>PID数整定是控制工程中的核心技能,下面通过工程实践角度详细讲解调参方法和注意事项: 一、PID控制本质表达式 $$ u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt} $$ 离散化形式(适用代码实现): $$ u_k = K_p e_k + K_i T_s \sum_{i=0}^k e_i + K_d \frac{e_k - e_{k-1}}{T_s} $$ 二、数物理意义对照表 | 数 | 响应影响 | 过度调节风险 | 典型调节方向 | |------|---------------------|------------|-------------------| | $K_p$| 加快响应速度 | 增大超调 | 优先调整,逐步增加 | | $K_i$| 消除稳态误差 | 积分饱和 | 最后微调,谨慎增加 | | $K_d$| 抑制振荡 | 噪声敏感 | 中后期加入,适度调整| 三、经典调参方法对比 1. **试错法**(适用于简单系统): - 步骤: 1. 设$K_i=0, K_d=0$ 2. 逐步增大$K_p$至系统出现持续振荡 3. 取振荡临界值的50-70%作为最终$K_p$ 4. 加入$K_d$消除振荡(通常$K_d=0.1K_p$起调) 5. 最后添加$K_i$消除残余误差 2. **齐格勒-尼克尔斯法**(需要测临界数): - 测得临界增益$K_u$和振荡周期$T_u$ - 数设置: | 控制类型 | $K_p$ | $T_i$ | $T_d$ | |----------|----------|------------|-----------| | P | 0.5$K_u$ | - | - | | PI | 0.45$K_u$| 0.83$T_u$ | - | | PID | 0.6$K_u$ | 0.5$T_u$ | 0.125$T_u$| 3. **继电反馈法**(自动获取临界数): ```c // 伪代码实现示例 while(!oscillation_detected){ apply_step_change(); measure_overshoot(); adjust_relay_gain(); } calculate_Ku_Tu(); // 根据振荡特征计算数 ``` 四、现代调参策略 1. **IMC(内模控制)法**: $$ K_p = \frac{\tau}{K( \lambda + \theta )} $$ $$ T_i = \tau $$ $$ T_d = \frac{\theta}{2} $$ 其中: - $K$:过程增益 - $\tau$:时间常数 - $\theta$:时滞 - $\lambda$:调节数(建议0.2-1.0) 2. **模糊自适应PID**(复杂系统适用): ```python # 模糊规则示例 if error is NB and delta_error is NB then Kp_change = PB if error is ZO and delta_error is PS then Ki_change = NS ``` 五、调试技巧故障排除 1. **典型问题处理**: | 现象 | 调整策略 | 代码修改示例 | |---------------------|---------------------------|---------------------------| | 持续振荡 | 降低$K_p$或增加$K_d$ | `Kp *= 0.7; Kd *= 1.2` | | 响应迟缓 | 增大$K_p$或减小$T_i$ | `Kp *= 1.5; Ti *= 0.8` | | 稳态误差 | 增加$K_i$或减小$T_i$ | `Ki += 0.1*Kp` | | 超调过大 | 增加$K_d$或减小$K_p$ | `Kd += 0.2*Kp; Kp *= 0.8` | 2. **抗积分饱和实现**: ```c // 带积分限幅的PID实现 float integral = 0; float max_integral = 100.0; // 根据系统设定 integral += error * Ki; if(integral > max_integral) integral = max_integral; else if(integral < -max_integral) integral = -max_integral; ``` 六、工程实践建议 1. **数归一化处理**(提升可移植性): $$ K_p' = K_p \times S_{base} $$ $$ T_i' = T_i / T_{sample} $$ $$ T_d' = T_d \times T_{sample} $$ 其中$S_{base}$为系统基准量(如额定转速) 2. **多模式数组**(应对不同工况): ```c typedef struct { float Kp; float Ki; float Kd; } PID_Params; PID_Params params_low_speed = {0.8, 0.05, 0.1}; PID_Params params_high_speed = {0.5, 0.02, 0.2}; ``` 3. **在线监测工具**: ```c void Monitor_PID_Performance() { static float prev_error = 0; float error_slope = (current_error - prev_error)/Ts; printf("ISE: %.3f | ITAE: %.3f | Slope: %.3f\n", integral_square_error, integral_time_abs_error, error_slope); prev_error = current_error; } ``` 七、安全注意事项 1. **数变更保护机制**: ```c #define MAX_KP_CHANGE 0.2 // 单次最大调整幅度 void Safe_Adjust_Kp(float target) { float delta = target - current_Kp; if(fabs(delta) > MAX_KP_CHANGE) { delta = (delta > 0) ? MAX_KP_CHANGE : -MAX_KP_CHANGE; } current_Kp += delta; } ``` 2. **故障恢复策略**: - 保存三组历史数:current/previous/emergency - 当检测到持续超调超过150%时自动回滚数 通过系统化的数整定,可使PID控制器达到以下典型性能指标: - 阶跃响应超调量:<5% - 调节时间:<3个时间常数 - 稳态误差:<0.5% 建议配合Bode图分析(频域法)或根轨迹法(时域法)进行理论验证,同时保留20%的数裕度以应对负载扰动。实际调试中推荐使用MATLAB/Simulink或Python Control库进行预仿真。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Scc_hy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值