
文章目录simple-pid仿真测试simple-pidsimple-pid是一个简单到令人发指的PID模块只实现了一个类即PIDpip install simple-pid-i https://pypi.tuna.tsinghua.edu.cn/simplePID类的初始化参数包括PID(Kp1.0,Ki0.0,Kd0.0,setpoint0,sample_time0.01,output_limits(None,None),auto_modeTrue,proportional_on_measurementFalse,differential_on_measurementTrue,error_mapNone,time_fnNone,starting_output0.0)其中Kp, Ki, Kd分别是比例(Proportional)、积分(Integral)和微分(Derivative)增益。在控制过程中K p K_pKp决定了系统对当前误差的反应强度K i K_iKi用于消除系统的稳态误差其累积效应确保系统最终能精确达到目标值但设置过大会导致系统响应变慢并产生超调K d K_dKd用于预测误差的变化趋势提供阻尼作用。它可以抑制系统的震荡减小超调使系统更稳定。对噪声比较敏感。【setpoint】是目标设定值表示PID 控制器努力想要让系统达到的目标状态。【sample_time】为采样时间如果设为None则每次调用都会立即重新计算。【output_limits】为输出限制边界格式为二元组分别表示下限和上限。限制边界除了用于保护物理设备之外还具备抗积分饱和的功能当输出达到极限时控制器会停止积分项的继续累加防止积分项变得过大。否则当误差反向时系统需要很长时间才能“消耗”掉多余的积分导致严重的超调和响应延迟。【proportional_on_measurement】和【differential_on_measurement】表示比例项或微分项基于测量值计算。【auto_mode】为自动模式开关设为 True 时PID 正常工作并计算输出设为 False 时PID 暂停计算相当于手动控制模式。配合 set_auto_mode() 方法可以实现手自动无扰动切换。【error_map】是误差映射函数可传入一个自定义函数在PID内部计算前对误差进行预处理。最典型的应用是角度控制如无人机的偏航角。角度是循环的-180° 到 180°从 170° 到 -170° 的实际误差是 20° 而不是 -340°。你可以传入一个处理角度回绕的函数给 error_map让 PID 总是选择最短路径进行纠正。【time_fn】是时间获取函数默认使用系统的 time.monotonic() 获取真实时间。在仿真程序中仿真时间的流逝速度可能与真实时间不同这是就可以传入一个返回“仿真时间”的函数让 PID 按照仿真时间轴正确计算积分和微分。【starting_output】为初始输出基准值。假设你要控制一个悬停的无人机维持悬停需要 50% 的油门。如果启动时目标值就是当前高度传统的 PID 初始输出可能是 0导致无人机瞬间掉落。将 starting_output 设为 50可以让积分项从这个基准值开始累加实现平滑启动 (Bumpless Start)避免系统偏离稳态。PID类实现了__call__方法从而在实例化之后能够像函数一样被调用其函数签名如下其中input_是一个需要被控制的量dt是时间步长。__call__(input_,dtNone)仿真测试下面用PID做一个简单的温控系统我们的目标是在t 5 t5t5s时将目标温度设为80℃而在t 25 t25t25s时将目标温度设为50℃。设室温为20℃且在5 15 515515s时骤降10℃效果如下上图表示PID控制和目标之间的关系下图为I分量的变化。测试代码为importmatplotlib.pyplotasplt plt.rcParams[font.sans-serif]Times New Romanfromsimple_pidimportPIDimportnumpyasnpclassHeaterSystem:def__init__(self,env_temp20.0):self.tempenv_temp self.env_tempenv_tempdefupdate(self,heat_power,dt,disturbance0.0):heat_gainheat_power*0.2# 加热增益heat_loss(self.temp-self.env_temp)*0.1# 自然散热self.temp(heat_gain-heat_loss)*dtdisturbancereturnself.temp dt0.1stepsint(50.0/dt)times,setpoints,measurements,outputs,i_terms[],[],[],[],[]systemHeaterSystem()pidPID(Kp2.0,Ki0.15,Kd0.5,setpoint20.0,sample_timedt,output_limits(0,100))foriinrange(steps):ti*dtif5.0t25.0:pid.setpoint80.0elift25.0:pid.setpoint50.0disturbance-10.0if15.0t15.1else0.0# 传入仿真步长control_outputpid(system.temp,dtdt)_,i_term,_pid.components current_tempsystem.update(control_output,dt,disturbance)times.append(t)setpoints.append(pid.setpoint)measurements.append(current_temp)outputs.append(control_output)i_terms.append(i_term)fig,(ax1,ax2)plt.subplots(2,1,figsize(10,9),sharexTrue,gridspec_kw{height_ratios:[2,1]})ax1.plot(times,setpoints,r--,labelSetpoint,linewidth2.5)ax1.plot(times,measurements,b-,labelMeasurement,linewidth2)ax1.axvline(x15.0,colorpurple,linestyle:,labelDisturbance (t15s))ax1.set_ylabel(Temperature (°C))ax1.set_title(PID Control Test (fixed: dt passed to PID))ax1.legend()ax1.grid(alpha0.5,linestyle--)ax2.plot(times,outputs,k-,labelControl Output,linewidth2)ax2.plot(times,i_terms,g--,labelI term,linewidth1.5)ax2.fill_between(times,0,outputs,alpha0.15,colororange)ax2.set_ylim(-5,110)ax2.set_xlabel(Time (s))ax2.set_ylabel(Heat Power (%))ax2.legend()ax2.grid(alpha0.5,linestyle--)plt.tight_layout()plt.show()