# pidController **Repository Path**: Sevenfite/pid-controller ## Basic Information - **Project Name**: pidController - **Description**: pid控制器 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-05-13 - **Last Updated**: 2025-07-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PID ## 使用说明 ### 快速入门使用示例 (1) 调用 `PIDInit()` 函数,传入参数为一个pid指针和一个初始化结构体 ```C //test PIDInstance pid; void pid_init(){ PID_Init_Config_s config ={ .Kp = 1.0f, .Ki = 0.1f, .Kd = 0.01f, .MaxOut = 100.0f, //限幅 .DeadBand =0,//死区 .Improve =PID_IMPROVE_NONE//不加任何优化 //... 剩下的是用于优化的可选参数 //如 .Improve = Integral_Limit //积分限幅 //.IntegralLimit = 30 //使用了积分限幅的优化,就要把相关的参数也加上,具体优化和参数介绍见下文 };//在初始化结构体时可以指定名称进行初始化,C99以上 PIDInit(&pid, &config); } ``` (2)使用`PIDCalculate()`函数计算输出值,传入传感器的反馈值和设定的目标值 ```C //test float measure = getMeasureValue();//获得传感器的反馈值 float setValue = 10;//设定目标值为10 float out = PIDCalculate(&pid,measure,10);//用PID计算输出 actuator_control(out);//把out当作执行器的输入 ``` (3)自行实现`GetDeltaT()`函数,此函数写在`PIDcontroller.h`中作为内联函数 ```C /** * @brief 获得时间间隔 * * @param lastTime 上一次的时间戳 * @return float * @note 此函数为接口函数,需自行实现,以下实现为示例。要求返回与上一次的时间间隔,并更新lastTime,此时间间隔用于积分项与微分项和滤波器的计算 * * @attention 要注意dt不能返回0,如果毫秒级精度不够,可以使用微秒级 * @note 间隔时间建议以秒为单位,这样在关于滤波器截止频率的计算中会比较方便 */ static inline float GetDeltaT(uint32_t *lastTime) { uint32_t HAL_GetTick();//函数声明 uint32_t now = HAL_GetTick(); float dt = (float)(now - *lastTime) / 1000.0f; // 转换为秒 *lastTime = now; return dt; } ``` 使用微秒级代码如下 ```C __STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void) { return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk)); } //通过滴嗒定时器获取微秒级时间,这里的滴嗒定时器每一毫秒溢出一次 static uint32_t getCurrentMicros(void) { /* Ensure COUNTFLAG is reset by reading SysTick control and status register */ GXT_SYSTICK_IsActiveCounterFlag(); const uint32_t tms = SysTick->LOAD + 1; __IO uint32_t u = tms - SysTick->VAL; if (GXT_SYSTICK_IsActiveCounterFlag()) { u = tms - SysTick->VAL; } return (u * 1000) / tms; } //获取系统时间,单位us,0~1000 uint32_t micros(void) { return getCurrentMicros(); } //通过HAL库的全局时间函数获取时间戳 uint32_t millis(void) { return HAL_GetTick(); } //以上代码是获得全局微秒和毫秒时间戳的示例方法,请结合实际情况自行修改,并在其他地方自行实现,不要复制到.h中 //把毫秒和微秒的数据压缩到一个32位的数内 static inline float GetDeltaT(uint32_t *lastTime) { uint32_t ms, us, ms2; do { ms = millis() & 0xFFFF; us = micros(); ms2 = millis() & 0xFFFF; } while (ms != ms2); // 保证ms和us属于同一毫秒 uint32_t last_ms = (*lastTime) >> 16; uint32_t last_us = (*lastTime) & 0xFFFF; // 处理溢出,利用无符号数特性 uint32_t d_ms = (ms - last_ms)&0xFFFF; int32_t d_us = (int32_t)us - (int32_t)last_us; if (d_us < 0) { d_us += 1000; d_ms -= 1; } uint32_t delta_us = d_ms * 1000 + d_us; // 更新lastTime *lastTime = (ms << 16) | (us & 0xFFFF); return delta_us / 1000000.0f; // 秒 } ``` ### 初始化结构体的说明 ```C typedef struct // config parameter { // 基础参数 float Kp; float Ki; float Kd; float MaxOut; // 输出限幅 float DeadBand; // 死区 // 优化选项 uint8_t Improve; float IntegralLimit; // 积分限幅 float CoefA; // AB为变速积分参数,变速积分实际上就引入了积分分离 float CoefB; // ITerm = Err*((A-abs(err)+B)/A) when B<|err|A+B \end{cases} $$ 此时积分项的公式为 $$ \sum_{k=0}^{} Ki*f(e[k])*e[k]*dt $$ #### (7) 积分分离 优化选项名称:`PID_Integral_Separate` 相关参数:`Intergral_Separate` 当误差的**绝对值**大于`Intergral_Separate`时,积分不起作用,积分的输出直接为0 当小于时,会重新开始积分,与上述变速积分有差异,上述变速积分当误差大于`A+B`时,只是当前积分项为0,总的累加积分依然保持输出,而积分分离则是总的积分也为0 #### (8) 微分先行 优化选项名称:`PID_Derivative_On_Measurement` 微分项由 $$ Kd*(err-lastErr)/dt $$ 变为 $$ Kd*(LastMeasure-Measure)/dt $$ 即算输入值的差值 #### (9) 梯形积分 优化选项名称:`PID_Trapezoid_Intergral` 当前积分项由 $$ Ki*err*dt $$ 变为 $$ Ki*(err+lastErr)/2*dt $$ #### (10) 滤波器 优化选项名称:`PID_OutputFilter` 和 `PID_DerivativeFilter` 相关参数:`Output_LPF_RC`和`Derivative_LPF_RC` 这两个分别是总输出值和微分输出的低通滤波器 关于低通滤波器,参考链接:`https://blog.csdn.net/weixin_42887190/article/details/125749509` 相关参数的值为RC值,又称时间常数$\tau$,截止频率定义为原来频率的$\frac{1}{\sqrt{2} } $时,即幅频响应曲线衰减 -3db,截止频率$f_h=\frac{1}{2\pi \tau}=\frac{1}{\omega}$ #### (11) 时间间隔限幅 优化选项名称: `PID_DeltaT_Limit` 相关参数: `DeltaT_Limit_Max`和`DeltaT_Limit_Min` 对于时间间隔dt,是用于计算积分项和微分项和滤波器相关参数的重要指标,可以对其做一个限幅,请自行注意修改后的相关影响