STM32 FOC电机PID学习笔记

这篇具有很好参考价值的文章主要介绍了STM32 FOC电机PID学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

在系统上存在外部干扰的情况下反馈是最好的选择否则使用前馈网络。为扭矩、通量和速度实施的调节器实际上是比例(P Proportional )、积分(I Integral)、微分 (D Derivative ) 调节器。 STM32中实际提供了3种,但是实际上我们常用的电机反馈调节使用的是PI反馈控制就够了。
PID的理解
带动图非常清晰

基维百科很好的资料

综上所述,
P—比例控制系统的响应快速性,快速作用于输出,好比"现在"(现在就起作用,快);
I—积分控制系统的准确性,消除过去的累积误差,好比"过去"(清除过去积怨,回到准确轨道);
D—微分控制系统的稳定性,具有超前控制作用,好比"未来"提前根据预测调整状态(放眼未来,未雨绸缪,稳定才能发展)。
当然这个结论也不可一概而论,只是想让初学者更加快速的理解PID的作用。

foc pid,stm32,学习,笔记

其中

Kp:比例增益,是调适参数
Ki:积分增益,也是调适参数
Kd:微分增益,也是调适参数
e:误差=设定值(SP)- 回授值(PV)
t:目前时间
τ:积分变数(dτ 的符号),数值从0到目前时间τ
用更专业的话来讲,PID控制器可以视为是频域系统的滤波器。在计算控制器最终是否会达到稳定结果时,此性质很有用。如果数值挑选不当,控制系统的输入值会反复振荡,这导致系统可能永远无法达到预设值。

PID控制器的一般转移函数是:
foc pid,stm32,学习,笔记

其中C是一个取决于系统带宽的常数。

foc pid,stm32,学习,笔记

参考《STM32F PMSM single/dual FOC SDK》文档

PID原理

文档章节4
并联:
我们平时使用到并联的PI的示意图,不过该图对于不同频率的时候的响应有缺陷。
foc pid,stm32,学习,笔记
串联:

foc pid,stm32,学习,笔记

并行模式Ki Kp变化的时候的0点对于频率的位置是变化的,即闭环的频率响应中实现极点0点消除。
foc pid,stm32,学习,笔记
使用串行模式可以避免频率变化0点对应位置不变。相关的参数变为了Ka和Kb,Ka=Kp ,Kb=Ki/Kp。

foc pid,stm32,学习,笔记

对应设置:
foc pid,stm32,学习,笔记

总结:

  1. 两种拓扑结构,在书本上并联PI的结构可能要比串联的常见。
  2. 并联拓扑,kp和ki并不解耦,都影响着系统的零点。
  3. 串联拓扑,kp影响系统增益,ki影响系统带宽,相互独立的。
  4. 两种拓扑的软件复杂度是相同的
  5. 实际工程中,工程师可能更倾向使用串联拓扑,工程的角度,kp和ki直接和系统参数相关联。

ST例子的PI实现

下面代码的实现:OUT=(erroKp)/KpPow+(erroKi)/KpPow 和保存 sum_Ki*erro其中做了一些范围限定。

/**
 * @brief  This function compute the output of a PI regulator sum of its
 *         proportional and integral terms
 * @param  pHandle: handler of the current instance of the PID component
 * @param  wProcessVarError: current process variable error, intended as the reference
 *         value minus the present process variable value
 * @retval computed PI output
 */
__weak int16_t PI_Controller( PID_Handle_t * pHandle, int32_t wProcessVarError )
{
  int32_t wProportional_Term, wIntegral_Term, wOutput_32, wIntegral_sum_temp;
  int32_t wDischarge = 0;
  int16_t hUpperOutputLimit = pHandle->hUpperOutputLimit;
  int16_t hLowerOutputLimit = pHandle->hLowerOutputLimit;
  /* Proportional term computation*/
  wProportional_Term = pHandle->hKpGain * wProcessVarError;//输入误差首先Kp比例调整
  /* Integral term computation */
  if ( pHandle->hKiGain == 0 )//判断I的积分是否为0p
  {
    pHandle->wIntegralTerm = 0;
  }
  else
  {
    wIntegral_Term = pHandle->hKiGain * wProcessVarError;//误差乘以Ki积分比例
    wIntegral_sum_temp = pHandle->wIntegralTerm + wIntegral_Term;//将本次值加入到积分内容
    if ( wIntegral_sum_temp < 0 )//如果最新的积分值小于0(判断是否超出INT32的界限)不过由于后面有最大最小的限制,如果能保证wIntegral_sum_temp 的范围这个界限判断应该可以忽略
    {  //如果正+正=负肯定是超界了需要纠正为正最大
      if ( pHandle->wIntegralTerm > 0 )
      {
        if ( wIntegral_Term > 0 )
        {
          wIntegral_sum_temp = INT32_MAX;
        }
      }
    }
    else//如果最新的积分值大于0
    {	//如果负+负=正肯定是超界了需要纠正为负最大
      if ( pHandle->wIntegralTerm < 0 )
      {
        if ( wIntegral_Term < 0 )
        {
          wIntegral_sum_temp = -INT32_MAX;
        }
      }
    }
    //限制最大最小值,其实有这个模块我认为上面的限制应该可以简化
    if ( wIntegral_sum_temp > pHandle->wUpperIntegralLimit )
    {
      pHandle->wIntegralTerm = pHandle->wUpperIntegralLimit;
    }
    else if ( wIntegral_sum_temp < pHandle->wLowerIntegralLimit )
    {
      pHandle->wIntegralTerm = pHandle->wLowerIntegralLimit;
    }
    else
    {
      pHandle->wIntegralTerm = wIntegral_sum_temp;
    }
  }
  //根据硬件选择较优的计算方式(注意的是P是除hKpDivisor ,I是除hKiDivisor 代码中定义的是2的14次方) ,为何要除或者位移2的N次方,是因为避免使用小数计算。所有值进行了整数放大后在对应缩小
//MISRA_C 是一种汽车级的C语言编程标准,设置该模式不能使用2位移的方式。
#ifdef FULL_MISRA_C_COMPLIANCY
  wOutput_32 = ( wProportional_Term / ( int32_t )pHandle->hKpDivisor ) + ( pHandle->wIntegralTerm /
               ( int32_t )pHandle->hKiDivisor );
#else
  /* WARNING: the below instruction is not MISRA compliant, user should verify
             that Cortex-M3 assembly instruction ASR (arithmetic shift right)
wProportional_Term              is used by the compiler to perform the shifts (instead of LSR
             logical shift right)*/
  wOutput_32 = ( wProportional_Term >> pHandle->hKpDivisorPOW2 ) + ( pHandle->wIntegralTerm >> pHandle->hKiDivisorPOW2 );
#endif
  if ( wOutput_32 > hUpperOutputLimit )//判断输出是否超界限如果超界限,那么I的积分部分需要减去超界值
  {
    wDischarge = hUpperOutputLimit - wOutput_32;
    wOutput_32 = hUpperOutputLimit;
  }
  else if ( wOutput_32 < hLowerOutputLimit )
  {
    wDischarge = hLowerOutputLimit - wOutput_32;
    wOutput_32 = hLowerOutputLimit;
  }
  else { /* Nothing to do here */ }
  pHandle->wIntegralTerm += wDischarge;
  return ( ( int16_t )( wOutput_32 ) );
}

ST例子的使用理解

//由于处理器浮点运算能力较弱,所以使用了整数运算的方式来处理,值*KP/KPDiviser这样的方式。为了进一步的提高效率,对应M3的处理器还使用了2的N次位移的方式处理,当然也可以选择使用整数除的方式。(还有个原因是PID函数返回的是16位的INT类型数据,不进行位移或者除可能会导致超界),STM32还是太弱了如果直接用浮点哪有这么多倒腾?
register_interface.c(251) :             PID_SetKP(pPIDIq[motorID], (int16_t)regdata16);  //设置Kp参数
register_interface.c(257) :             PID_SetKI(pPIDIq[motorID], (int16_t)regdata16);	 //设置Ki参数
register_interface.c(263) :             PID_SetKD(pPIDIq[motorID], (int16_t)regdata16);  //设置Kd参数
register_interface.c(429) :             PID_SetKPDivisorPOW2(pPIDIq[motorID], regdata16);   //设置Kp参数除以2的n次方表示
register_interface.c(435) :             PID_SetKIDivisorPOW2(pPIDIq[motorID], regdata16);	//设置Ki参数除以2的n次方表示
register_interface.c(441) :             PID_SetKDDivisorPOW2(pPIDIq[motorID], regdata16);   //设置Kd参数除以2的n次方表示
/*
在前面代码中的部分,位了提高效率使用位移代替除法加速
#ifdef FULL_MISRA_C_COMPLIANCY
  wOutput_32 = ( wProportional_Term / ( int32_t )pHandle->hKpDivisor ) + ( pHandle->wIntegralTerm /
               ( int32_t )pHandle->hKiDivisor );
#else
  /* WARNING: the below instruction is not MISRA compliant, user should verify
             that Cortex-M3 assembly instruction ASR (arithmetic shift right)
wProportional_Term              is used by the compiler to perform the shifts (instead of LSR
             logical shift right)*/
  wOutput_32 = ( wProportional_Term >> pHandle->hKpDivisorPOW2 ) + ( pHandle->wIntegralTerm >> pHandle->hKiDivisorPOW2 );
#endif
*/

register_interface.c(714) :    *regdata16 = PID_GetKP(pPIDIq[motorID]);//获取Kp设置值
register_interface.c(720) :    *regdata16 = PID_GetKI(pPIDIq[motorID]);//获取Ki设置值
register_interface.c(726) :    *regdata16 = PID_GetKD(pPIDIq[motorID]);//获取Kd设置值
register_interface.c(958) :    *regdataU16 = PID_GetKPDivisorPOW2(pPIDIq[motorID]);//获取Kp的除以2n次模式设置值
register_interface.c(964) :    *regdataU16 = PID_GetKIDivisorPOW2(pPIDIq[motorID]);//获取Ki的除以2n次模式设置值
register_interface.c(970) :    *regdataU16 = PID_GetKDDivisorPOW2(pPIDIq[motorID]);//获取Kd的除以2n次模式设置值
//wIntegral_Term  积分相
mc_tasks.c(667) :   PID_SetIntegralTerm(pPIDIq[bMotor], ((int32_t)0));//设置启动积分相部分(一般初始化的时候)
mc_tasks.c(903) :   Vqd.q = PI_Controller(pPIDIq[M1], (int32_t)(FOCVars[M1].Iqdref.q) - Iqd.q);//调用PI算法输入最近的差值,获取PI算法计算后的值
mc_config.c(346) : PID_Handle_t *pPIDIq[NBR_OF_MOTORS] = {&PIDIqHandle_M1};
mc_config.h(65) : extern PID_Handle_t *pPIDIq[NBR_OF_MOTORS];


void PID_SetLowerIntegralTermLimit(PID_Handle_t *pHandle, int32_t wLowerLimit)  //设置积分部分下限
void PID_SetUpperIntegralTermLimit(PID_Handle_t *pHandle, int32_t wUpperLimit)  //设置积分部分上限

void PID_SetLowerOutputLimit(PID_Handle_t *pHandle, int16_t hLowerLimit) //设置输出的下限
void PID_SetUpperOutputLimit(PID_Handle_t *pHandle, int16_t hUpperLimit) //设置输出的上限

PID时需要注意的事项

由于PID时有个d的微分项目。

void PID_SetPrevError(PID_Handle_t *pHandle, int32_t wPrevProcessVarError); //设置上一次的微分差值(微分初始化)

     wDeltaError = wProcessVarError - pHandle->wPrevProcessVarError;//记录上一次的误差和当前的误差比较,获取差值
     wDifferential_Term = pHandle->hKdGain * wDeltaError;//微分项目乘以微分增益

#ifndef FULL_MISRA_C_COMPLIANCY_PID_REGULATOR
      wDifferential_Term >>= pHandle->hKdDivisorPOW2;//微分除
#else
      wDifferential_Term /= ((int32_t)pHandle->hKdDivisor);
#endif
      pHandle->wPrevProcessVarError = wProcessVarError;//保存本次值等下异次使用微分项目
      wTemp_output = PI_Controller(pHandle, wProcessVarError) + wDifferential_Term;//PI项加上微分项即为PID值

PID使用例子

使用时设置的Divisor 都是为了不让PID最终结果不超过int16使用的。


/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT         256
#define PID_POSIT_KI_DEFAULT         0
#define PID_POSIT_KD_DEFAULT         0


/*control loop gains dividers*/
#define POSIT_KPDIV                      1024//16384
#define POSIT_KIDIV                      8192//16384
#define POSIT_KDDIV                      256//8192
#define POSIT_KPDIV_LOG                  LOG2((POSIT_KPDIV))
#define POSIT_KIDIV_LOG                  LOG2((POSIT_KIDIV))
#define POSIT_KDDIV_LOG                  LOG2((POSIT_KDDIV))



/* USER CODE BEGIN PID_SPEED_INTEGRAL_INIT_DIV */
#define PID_POSIT_INTEGRAL_INIT_DIV 1 /*  */
/* USER CODE END PID_SPEED_INTEGRAL_INIT_DIV */

PID_Handle_t PIDHandle_POSITION_SPEED =
{
  .hDefKpGain          = (int16_t)PID_POSIT_KP_DEFAULT,
  .hDefKiGain          = (int16_t)PID_POSIT_KI_DEFAULT,

  .wUpperIntegralLimit = (int32_t)INT16_MAX * POSIT_KIDIV,//return result is int16 so mast limit 
  .wLowerIntegralLimit = (int32_t)-INT16_MAX * POSIT_KIDIV,
  .hUpperOutputLimit   = INT16_MAX,//return result is int16 so mast limit 
  .hLowerOutputLimit   = -INT16_MAX,
  .hKpDivisor          = (uint16_t)POSIT_KPDIV,
  .hKiDivisor          = (uint16_t)POSIT_KIDIV,

  .hKpDivisorPOW2      = (uint16_t)POSIT_KPDIV_LOG,
  .hKiDivisorPOW2      = (uint16_t)POSIT_KIDIV_LOG,

  .hDefKdGain           = (int16_t)PID_POSIT_KD_DEFAULT,
  .hKdDivisor           = POSIT_KDDIV,
  .hKdDivisorPOW2       = POSIT_KDDIV_LOG,
};

int position_speed=1000;
int now_speed=0;
void test_position(void){	
			int i;
	    PID_HandleInit(&PIDHandle_POSITION_SPEED);//init the 
			PID_SetIntegralTerm(&PIDHandle_POSITION_SPEED, ((int32_t)0));
			while(1){
			
				now_speed=PID_Controller(&PIDHandle_POSITION_SPEED,position_speed-now_speed);
				for(i=0;i<1000;i++);
			}
}

设置与曲线状态

PI

/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT 256
#define PID_POSIT_KI_DEFAULT 0
#define PID_POSIT_KD_DEFAULT 0

#define POSIT_KPDIV 1024
#define POSIT_KIDIV 1024
#define POSIT_KDDIV 256

只有比例误差将被固定
foc pid,stm32,学习,笔记
修改PID_POSIT_KP_DEFAULT 为1000时
foc pid,stm32,学习,笔记

#define PID_POSIT_KP_DEFAULT 256
#define PID_POSIT_KI_DEFAULT 256
foc pid,stm32,学习,笔记

PID

/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT 256
#define PID_POSIT_KI_DEFAULT 512
#define PID_POSIT_KD_DEFAULT 64

/* control loop gains dividers*/
#define POSIT_KPDIV 1024//16384
#define POSIT_KIDIV 1024//16384
#define POSIT_KDDIV 8192//8192

foc pid,stm32,学习,笔记

/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT 256
#define PID_POSIT_KI_DEFAULT 512
#define PID_POSIT_KD_DEFAULT 128

/* control loop gains dividers*/
#define POSIT_KPDIV 1024//16384
#define POSIT_KIDIV 1024//16384
#define POSIT_KDDIV 8192//8192

foc pid,stm32,学习,笔记
/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT 256
#define PID_POSIT_KI_DEFAULT 512
#define PID_POSIT_KD_DEFAULT 2048

/* control loop gains dividers*/
#define POSIT_KPDIV 1024//16384
#define POSIT_KIDIV 1024//16384
#define POSIT_KDDIV 8192//8192

foc pid,stm32,学习,笔记

使用 /* Gains values for torque and flux control loops */ 参数的效果

/* Gains values for position control loops */
#define PID_POSIT_KP_DEFAULT 965
#define PID_POSIT_KI_DEFAULT 197
#define PID_POSIT_KD_DEFAULT 100

/* control loop gains dividers*/
#define POSIT_KPDIV 16384
#define POSIT_KIDIV 16384
#define POSIT_KDDIV 8192
foc pid,stm32,学习,笔记

参数的PID效果

foc pid,stm32,学习,笔记

SPEED 的PI

foc pid,stm32,学习,笔记

PID_FLUX_KP_DEFAULT 参数

foc pid,stm32,学习,笔记

foc pid,stm32,学习,笔记

PIDSpeedHandle_M1

调用积分项初始化的状态STO_ExecutePLL函数中调用,其中错误是比较小的量比如0XFFFFFFC1。

      hTorqueReference = PI_Controller(pHandle->PISpeed, (int32_t)hError);

foc pid,stm32,学习,笔记
测试发现对于该PID 不能使用太大的数值查,容易形成震旦的效果,使用下面设置可以较为稳定的进入到设定值。

				now_speed=PI_Controller(&PIDHandle_POSITION_SPEED,erro);
				if(now_speed>position_speed)
					erro=-5;
				else
					erro=4;

需要注意的事项

在FOC_CurrControllerM1的PI_Controller(pPIDIq[M1]
Vqd.q = PI_Controller(pPIDIq[M1], (int32_t)(FOCVars[M1].Iqdref.q) - Iqd.q);

需要除一个比例值,因为中间是有个比例转换,从I电流的范围转到了V电压范围。而且电流是根据采样电阻和最大AD值比例来处理的。 电机最大电流值对应着输出V最大值,那么他们间就有个比例值转换。

I 电机 0- 最大Imax 对应着输出 V 0 -最大值 0X7FFF

vss = PI_Controller(pPIDIq[M1], (int32_t)(100 - vss))/0x100;//需要除个比例值否则不正常。

从ST的文档获取算法上的状态
I和V对应的原理关系图:
foc pid,stm32,学习,笔记
本身I转V,V又转回I ,其中又部分电压电流转换关系可以抵消去除
foc pid,stm32,学习,笔记
最终的关系图:

foc pid,stm32,学习,笔记
foc pid,stm32,学习,笔记文章来源地址https://www.toymoban.com/news/detail-735982.html

到了这里,关于STM32 FOC电机PID学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • stm32 滑膜观测器+PLL 锁相环 FOC 无感无刷电机控制

            上一期为大家介绍了滑膜观测器正反切的应用案例,收到不少小伙伴的反馈是否有PLL的案例,大概看了一下网上的资料,讲理论的很多,能转化成源码的几乎没有。前半年工作和家里的事情都比较多,一拖再拖,终于在6月将源码调试好了,在这里跟大家分享一下调试

    2024年02月10日
    浏览(52)
  • stm32 无刷电机 V/F控制(无刷电机变频控制)以及与foc(矢量控制)的区别

           无刷电机有三种控制方式,方波控制,foc控制以及变频控制,前两章我们讲解了方波和foc的控制方法,今天我们一起来讲一讲什么是无刷电机的变频控制(VF)以及变频控制的优势是什么。         实验用的硬件还是KY_Motor的无刷电机开发板:无刷电机开发板   什么

    2024年02月11日
    浏览(39)
  • stm32 FOC从零学习开发(一)FOC概念

    ` 一、stm32 FOC从零学习开发(一)FOC概念 二、stm32 FOC从学习开发(二)Clark变换与MATLAB仿真 三、stm32 FOC从学习开发(三)park变换与MATLAB仿真 四、stm32 FOC从学习开发(四)svpwm算法(1) 五、stm32 FOC从学习开发(五)svpwm算法(2) 六、stm32 FOC从学习开发(六)基于均值零序分量

    2024年02月07日
    浏览(41)
  • STM32G4 Simulink FOC开发实战--第四章:ST MCSDK快速启动电机

    HALL有感启动电机基于STMCSDK: https://www.bilibili.com/video/BV1W14y1k7Ct/?spm_id_from=333.999.0.0vd_source=eb375996b1aae493d63fe367f98b306c 无感启动电机基于STMCSDK: https://www.bilibili.com/video/BV1QG41197AH/?spm_id_from=333.999.0.0vd_source=eb375996b1aae493d63fe367f98b306c 电位器控制电机转速基于STMCSDK: https://www.bilibili

    2024年02月22日
    浏览(40)
  • STM32 HAL库PID控制电机 第三章 PID控制双电机

    注:本文含全部PID控制代码,保证可以运行,如不能运行可以留言回复 1 基础配置 1.1 编码器电路图及配置 引脚 定时器通道 PA0 TIM2_CH1 PA1 TIM2_CH2 PB6 TIM4_CH1 PB7 TIM4_CH2 因此需要把TIM2、TIM4配置为编码器模式。在STM32CubeIDE中找到定时器2与定时器4,进行模式配置。以下以定时器2为

    2024年02月16日
    浏览(38)
  • 基于stm32的减速直流电机PID算法控制

    本例程采用了HAL库进行项目开发(主要使用软件CubexMX和keil5),文章末尾会有代码开源,欢迎各位对文章进行指正和探讨。         硬件组成:stm32f103c8t6最小系统板;0.96寸LED12864(I2C通讯模式);智能小车12v移动电源;25GA370直流减速电机(带霍尔编码器);JDY-31蓝牙模块

    2024年02月20日
    浏览(48)
  • [FOC-Simulink]使用Simulink代码生成工具基于STM32开发板对永磁同步电机进行开环控制

    代码链接:【免费】使用Simulink代码生成工具对永磁同步电机进行开环控制资源-CSDN文库 本文介绍使用Simulink代码生成功能在STM32开发板平台上运行永磁同步电机。 硬件基础: Nucleo-G431RB开发板 X-NUCLEO-IHM07M1驱动扩展板 57BLDC-24V-210W时代超群直流电机 软件基础: MATLAB 2022b 安装

    2024年02月13日
    浏览(43)
  • STM32增量式pid直流电机调速(内附源码)

            目录 一. 1.硬件组成 2.模块分析         1.TB6612电机驱动模块         2.直流减速电机         3.电源稳压模块 二.接线 三.代码思路讲解(详见源码) 四.STM32cubmx配置 1.系统基础配置:(重要) 2.电机旋转方向引脚配置 3.TIM1每10ms触发一次的定时器中断 4.TI

    2024年02月16日
    浏览(40)
  • stm32(HAL)库编码器电机pid代码及利用VOFA+对Pid波形显示调参

    PID控制是一种经典的反馈控制算法,它通过不断地调整输出来使系统的实际值与设定值尽量接近,并保持在设定值附近。PID控制器由三个部分组成:比例§、积分(I)和微分(D)。 比例作用(P):比例作用通过测量实际值与设定值之间的偏差,乘以一个比例系数来产生输出。输出

    2024年02月13日
    浏览(62)
  • QT上位机控制stm32,并利用PID控制编码电机旋转

    QT上位机控制stm32,并利用PID控制编码电机旋转             由于最近在学习电机控制算法之类的东西,看到论文大多使用PID、或以PID衍生的ADRC作为电机的主流控制,于是自己也写了一个stm32控制L298N以驱动直流电机的程序,并用QT做了一个上位机实现了用软件改变PID的参数

    2023年04月09日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包