PID模块化__以stm32直流电机速度为例

这篇具有很好参考价值的文章主要介绍了PID模块化__以stm32直流电机速度为例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇使用到的基于这个STM32CubeMX 直流电机PID速度控制、HAL库、cubemx、PID、速度控制、增量式
由于上次使用的pid没有模块化,当多出使用pid的时候就会很麻烦
所以这次使用的模块化的


一、相关PID源码

.c

/* 包含头文件 ----------------------------------------------------------------*/
#include "pid.h"

/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/ 
void abs_limit(float *a, float ABS_MAX)// 对输入 a 进行限制,使其在 [-ABS_MAX, ABS_MAX] 区间内
{
  if (*a > ABS_MAX)
    *a = ABS_MAX;
  if (*a < -ABS_MAX)
    *a = -ABS_MAX;
}

// 初始化 PID 参数
static void pid_param_init(
    pid_t*   pid,          // PID 控制器结构体
    uint32_t mode,         // PID 控制器模式
    uint32_t maxout,       // PID 控制器输出最大值
    uint32_t intergral_limit, // PID 控制器积分限制
    float    kp,           // PID 控制器 P 项系数
    float    ki,           // PID 控制器 I 项系数
    float    kd)           // PID 控制器 D 项系数
{

  pid->integral_limit = intergral_limit;
  pid->max_out        = maxout;
  pid->pid_mode       = mode;

  pid->p = kp;
  pid->i = ki;
  pid->d = kd;

}
/**
  * @brief     modify pid parameter when code running
  * @param[in] pid: control pid struct
  * @param[in] p/i/d: pid parameter
  * @retval    none
  */
static void pid_reset(pid_t *pid, float kp, float ki, float kd)// 重置 PID 控制器的参数
{
  pid->p = kp;
  pid->i = ki;
  pid->d = kd;
  
  pid->pout = 0;
  pid->iout = 0;
  pid->dout = 0;
  pid->out  = 0;
  
}

/**
  * @brief     calculate delta PID and position PID
  * @param[in] pid: control pid struct
  * @param[in] get: measure feedback value
  * @param[in] set: target value
  * @retval    pid calculate output 
  */
float pid_calc(pid_t *pid, float get, float set)// 计算 PID 控制器的输出
{
  pid->get = get;
  pid->set = set;
  pid->err[NOW] = set - get;

  if ((pid->input_max_err != 0) && (pid->err[NOW] > pid->input_max_err))
      pid->err[NOW] = pid->input_max_err;
  if ((pid->input_min_err != 0) && (pid->err[NOW] < pid->input_min_err))
      pid->err[NOW] = pid->input_min_err;
  if (pid->pid_mode == POSITION_PID) //position PID// 位置式 PID 控制器
  {
      pid->pout = pid->p * pid->err[NOW];
      pid->iout += pid->i * pid->err[NOW];
      pid->dout = pid->d * (pid->err[NOW] - pid->err[LAST]);
    
      abs_limit(&(pid->iout), pid->integral_limit);
      pid->out = pid->pout + pid->iout + pid->dout;
      abs_limit(&(pid->out), pid->max_out);
  }
  else if (pid->pid_mode == DELTA_PID) //delta PID// 增量式 PID 控制器
  {
      pid->pout = pid->p * (pid->err[NOW] - pid->err[LAST]);
      pid->iout = pid->i * pid->err[NOW];
      pid->dout = pid->d * (pid->err[NOW] - 2 * pid->err[LAST] + pid->err[LLAST]);

      pid->out += pid->pout + pid->iout + pid->dout;
      abs_limit(&(pid->out), pid->max_out);
  }

  pid->err[LLAST] = pid->err[LAST];
  pid->err[LAST]  = pid->err[NOW];
  
  
  if ((pid->output_deadband != 0) && (fabs(pid->out) < pid->output_deadband))
    return 0;
  else
    return pid->out;

}
void pid_ClearIntegrals(pid_t*   pid)// 清除积分项
{
	pid->pout = 0;
    pid->iout = 0;
    pid->dout = 0;
    pid->out  = 0;
}
/**
  * @brief     initialize pid parameter
  * @retval    none
  */
void PID_struct_init(		// 初始化 PID 结构体
    pid_t*   pid,
    uint32_t mode,
    uint32_t maxout,
    uint32_t intergral_limit,

    float kp,
    float ki,
    float kd)
{
  pid->f_param_init = pid_param_init;
  pid->f_pid_reset  = pid_reset;
  pid->f_pid_calc = pid_calc;
  pid->f_pid_ClearIntegrals = pid_ClearIntegrals;

  pid->f_param_init(pid, mode, maxout, intergral_limit, kp, ki, kd);
  pid->f_pid_reset(pid, kp, ki, kd);
}



.h

#ifndef __PID_H__
#define __PID_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include "stdint.h"
#include "math.h"
/* 类型定义 ------------------------------------------------------------------*/
enum
{
  LLAST = 0,
  LAST,
  NOW,
  POSITION_PID,
  DELTA_PID,
};
typedef struct pid_t
{
  float p;
  float i;
  float d;

  float set;
  float get;
  float err[3];

  float pout;
  float iout;
  float dout;
  float out;

  float input_max_err;    //input max err;
  float input_min_err;    //input max err;
  float output_deadband;  //output deadband;
  
  uint32_t pid_mode;
  uint32_t max_out;
  uint32_t integral_limit;

  void  (*f_param_init)(struct pid_t *pid, 
                        uint32_t      pid_mode,
                        uint32_t      max_output,
                        uint32_t      inte_limit,
                        float         p,
                        float         i,
                        float         d);
  void  (*f_pid_reset)(struct pid_t *pid, float p, float i, float d);
  float (*f_pid_calc)(struct pid_t *pid, float get, float set);
  void  (*f_pid_ClearIntegrals)(struct pid_t*   pid);
} pid_t;
/* 宏定义 --------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void PID_struct_init(
    pid_t*   pid,
    uint32_t mode,
    uint32_t maxout,
    uint32_t intergral_limit,
    float kp,
    float ki,
    float kd);
float pid_calc(pid_t *pid, float get, float set);
#endif  // __PID_H__


二、如何使用

1.创建变量

在main.c或者其他位置创建pid的变量

pid_t a_moto_pid;
pid_t b_moto_pid;

2.初始化

注意一定要在pid计算之前初始化all_moto_pid_init,不然会导致stm32硬件错误!!!!

void all_moto_pid_init(void)
{
    PID_struct_init(
        &a_moto_pid,              // PID 控制器对象
        DELTA_PID,      // 控制器模式
        7500,               // 输出最大值
        0,                // 积分限制
        45.0f,              // P 项系数
        25.0f,              // I 项系数
        0.0f               // D 项系数
    );
    PID_struct_init(
        &b_moto_pid,              // PID 控制器对象
        DELTA_PID,      // 控制器模式
        7500,               // 输出最大值
        0,                // 积分限制
        45.0f,              // P 项系数
        25.0f,              // I 项系数
        0.0f               // D 项系数
    );	

}

3.运算


int a_moto_pid_calc(float current_value,float target_value)/*current_value当前值target_value目标值*/
{
// 使用 PID 控制器计算控制输出
	int control_output = a_moto_pid.f_pid_calc(&a_moto_pid, current_value, target_value);	
	return control_output;
}
int b_moto_pid_calc(float current_value,float target_value)/*current_value当前值target_value目标值*/
{
// 使用 PID 控制器计算控制输出
	int control_output = b_moto_pid.f_pid_calc(&b_moto_pid, current_value, target_value);	
	return control_output;
}

4.修改pid参数

/*
修改pid的值
*/
void angle_pid_set(float  p,float i ,float d )
{
	angle_pid.f_pid_reset(&angle_pid, p, i, d);
}

三、积分分离、变速积分的实现

积分分离

当我们在遇到一些情况的时候不希望pid带来超调,比如给小车进行位置控制的时候。

如果使用一般的pid位置式我们会知道,他会从一开始就进行积分的累加,如果我们设置积分的上限为1万,我们的目标是2千。
那么就会导致在前半段路程中的error(假设error为正)一直都比较大导致积分很快趋向饱和,当趋近2千这个目标位置的时候积分计算出来的值就过大,那必定会超过2000,此时error就变为了负,才逐渐较少积分的值。反应在车辆上就是车子先跑到2200的位置,再回1900再逐渐摆动到2000.

上述的情况我通过调节参数,p、i、d、积分上限的值都无法避免,所以可以在积分前乘以一个系数
这个系数是个分段函数,当error的值小于设定的值的时候再进行积分,这样可以有效减少积分饱和的现象导致我们的超调。
代码上也很简单系数的分段点 #define integral_threshold 100
代码如下:

 float pid_calc(pid_t *pid, float get, float set) // 计算 PID 控制器的输出
{
    pid->get = get;
    pid->set = set;
    pid->err[NOW] = set - get;

    if ((pid->input_max_err != 0) && (pid->err[NOW] > pid->input_max_err))
        pid->err[NOW] = pid->input_max_err;
    if ((pid->input_min_err != 0) && (pid->err[NOW] < pid->input_min_err))
        pid->err[NOW] = pid->input_min_err;

    float integral_factor;//这就是那个分段函数的值
    #define integral_threshold 100//分段函数的分界点
    // 根据误差的大小来确定积分的速度
    if (fabs(pid->err[NOW]) <integral_threshold) {
        // 当误差小于 integral_threshold 时,允许积分
        integral_factor = 1.0;
    } else {
        // 当误差大于等于 integral_threshold 时,禁止积分
        integral_factor = 0.0;
    }


    if (pid->pid_mode == POSITION_PID) // position PID// 位置式 PID 控制器
    {
        pid->pout = pid->p * pid->err[NOW];
        pid->iout += pid->i * pid->err[NOW] * integral_factor; // 乘以积分因子@@@@@@@@@@@@
        pid->dout = pid->d * (pid->err[NOW] - pid->err[LAST]);

        abs_limit(&(pid->iout), pid->integral_limit);
        pid->out = pid->pout + pid->iout + pid->dout;
        abs_limit(&(pid->out), pid->max_out);
    }
    else if (pid->pid_mode == DELTA_PID) // delta PID// 增量式 PID 控制器
    {
        pid->pout = pid->p * (pid->err[NOW] - pid->err[LAST]);
        pid->iout = pid->i * pid->err[NOW] * integral_factor; // 乘以积分因子
        pid->dout = pid->d * (pid->err[NOW] - 2 * pid->err[LAST] + pid->err[LLAST]);

        pid->out += pid->pout + pid->iout + pid->dout;
        abs_limit(&(pid->out), pid->max_out);
    }

    pid->err[LLAST] = pid->err[LAST];
    pid->err[LAST]  = pid->err[NOW];

    if ((pid->output_deadband != 0) && (fabs(pid->out) < pid->output_deadband))
        return 0;
    else
        return pid->out;
}

PID模块化__以stm32直流电机速度为例,stm32,PID,stm32,算法,嵌入式硬件

变速积分


总结

简述一下,不喜勿喷谢谢。文章来源地址https://www.toymoban.com/news/detail-621584.html

到了这里,关于PID模块化__以stm32直流电机速度为例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于STM32的智能巡检小车系统设计--STM32最小系统、直流电机、直流电源模块设计

    作者:车 邮箱:692604135@qq.com 学校:西安工程大学硕士研究生 方向:机器视觉、图像分割、深度学习 在介绍具体实现功能之前,需要介绍以下模块。 本课题选择的单片机是ST(意法半导体)开发的STM32F407VET6。 这是一款采用Corte-M4为内核的高性能32位ARM微控制器。该芯片支持

    2024年02月10日
    浏览(51)
  • STM32CubeMX 直流电机串级PID位置速度控制、HAL库、cubemx、PID、串级PID、位置控制、速度控制、双环控制

    提示:本文章的串级PID位置速度控制,是在前两篇文章速度控制,位置控制的基础上实现的,这一章节中不需要额外的cubemx的配置,只需要写简单的代码即可,复杂的地方在于串级pid的调试过程。 pid是我们在学习单片机中首先要学会的控制算法,而串级pid又是在单pid的基础上

    2024年02月14日
    浏览(51)
  • 直流减速编码电机的使用(STM32f103c8t6)L298N电机驱动模块

    直接减速电机就是在直流电机上加上霍尔编码器,霍尔编码器可用于电机转动的测速,A、B相会产生相位相差90°的方波信号。stm32可以使用硬件资源或者软件模拟来捕获编码器信号。这里我介绍的是stm32自带的编码器模式来使用直流减速电机。 以下是直流减速电机的商品图 ​

    2024年02月13日
    浏览(59)
  • K_A08_009 基于 STM32等单片机驱动TB6612模块按键控制直流电机正反转加减速启停

    目录 一、资源说明 二、基本参数         1、参数         2、引脚说明 三、驱动说明         TB6612模块驱动说明         对应程序:         ENA ENB输出PWM 四、部分代码说明         接线说明                 1、STC89C52RC+TB6612模块                 2、STM32F

    2024年02月15日
    浏览(35)
  • K_A08_005 基于 STM32等单片机驱动XY-160D模块按键控制直流电机正反转加减速启停

    目录 一、资源说明 二、基本参数 四、部分代码说明         接线说明                 1、STC89C52RC+XY-160D模块                  2、STM32F103C8T6+XY-160D模块 五、基础知识学习与相关资料下载 六、视频效果展示与程序资料获取 七、项目所有材料清单 八、注意事项 九

    2024年02月14日
    浏览(67)
  • STM32控制直流电机转向

    一、所需硬件模块 1.主控芯片:STM32F103C8T6 2.程序下载器:STLINK 3.电机:直流电机 4.电机驱动模块:L298N 二、模块介绍 1、主控芯片采用的是STM32系列的最小系统板,通过控制其引脚的输出进而实现对电机的控制; 2、STLINK下载器是用于烧录程序,其中的SWCLK、SWDLO、GND、3.3V引脚

    2024年02月07日
    浏览(35)
  • STM32PWM控制直流电机

    PWM介绍 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用 微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽 度的控制 PWM的原理: 假定定时器工作在向上计数 PWM 模式,且当 CNT=CCRx 时输出 1。那么就可以

    2024年02月08日
    浏览(47)
  • stm32+TB6612驱动直流电机

            因为单片机的不可以直接的驱动电机,所以需要在他们之间加上一个电机的驱动模块,之前的文章里面用过L298N电机驱动模块,现在再给大家推荐一个新的电机驱动模块,他比L298N好用许多,而且占用体积小,发热也少。TB6612可以一次驱动两个直流电机,输出PWM也

    2024年02月15日
    浏览(45)
  • STM32第八课:PWM控制直流电机

            TB6612FNG是一款新型驱动器件,能独立双向控制2个直流电机,它具有很高的集成度,同时能提供足够的输出能力,运行性能和能耗方面也具有优势因此在集成化、小型化的电机控制系统中,它可以作为理想的电机驱动器件。                           

    2024年03月24日
    浏览(50)
  • stm32——pwm驱动LED灯、舵机、直流电机

    改为PA15,选择重映射方式1或完全重映射 在时钟开启后写入 PWM频率=计数器更新频率 比如要产生一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形 72M/(PSC+1)/ (ARR+1)=1000 CCR/(ARR+1)=0.5 1/(ARR+1)=0.01 计算得:ARR=99,CCR=50, PSC=720-1; 常用模式为 PWM1模式1 PWM.c PWM.h main.c 参数计算 PWM.c Servo.

    2024年02月04日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包