FOC电机库 定点PID代码分析

本文详细介绍了STM32FOC电机库中PID控制器的结构体定义,包括PID增益参数、积分项、微分项、输出限制及增益除数等关键元素。代码示例展示了PI和PID控制函数的实现,强调了定点PID运算的优化技巧,如使用算术右移提高执行速度,并提到了参数调整的注意事项,建议先调整除法项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码来源于STM32 FOC电机库中的pid_regulator.c。

PID结构体

typedef struct PID_Handle
{
  int16_t   hDefKpGain;           /**< Default @f$K_{pg}@f$ gain */
  int16_t   hDefKiGain;           /**< Default @f$K_{ig}@f$ gain */
  int16_t   hKpGain;              /**< @f$K_{pg}@f$ gain used by PID component */
  int16_t   hKiGain;              /**< @f$K_{ig}@f$ gain used by PID component */
  int32_t   wIntegralTerm;        /**< integral term */
  int32_t   wUpperIntegralLimit;  /**< Upper limit used to saturate the integral
                                       term given by @f$\frac{K_{ig}}{K_{id}} @f$ * integral of
                                       process variable error */
  int32_t   wLowerIntegralLimit;  /**< Lower limit used to saturate the integral
                                       term given by Ki / Ki divisor * integral of
                                       process variable error */
  int16_t   hUpperOutputLimit;    /**< Upper limit used to saturate the PI output */
  int16_t   hLowerOutputLimit;    /**< Lower limit used to saturate the PI output */
  uint16_t  hKpDivisor;           /**< Kp gain divisor, used in conjuction with
                                       Kp gain allows obtaining fractional values.
                                       If FULL_MISRA_C_COMPLIANCY is not defined
                                       the divisor is implemented through
                                       algebrical right shifts to speed up PI
                                       execution. Only in this case this parameter
                                       specifies the number of right shifts to be
                                       executed */
  uint16_t  hKiDivisor;           /**< Ki gain divisor, used in conjuction with
                                       Ki gain allows obtaining fractional values.
                                       If FULL_MISRA_C_COMPLIANCY is not defined
                                       the divisor is implemented through
                                       algebrical right shifts to speed up PI
                                       execution. Only in this case this parameter
                                       specifies the number of right shifts to be
                                       executed */
  uint16_t  hKpDivisorPOW2;       /**< Kp gain divisor expressed as power of 2.
                                       E.g. if gain divisor is 512 the value
                                       must be 9 as 2^9 = 512 */
  uint16_t  hKiDivisorPOW2;       /**< Ki gain divisor expressed as power of 2.
                                       E.g. if gain divisor is 512 the value
                                       must be 9 as 2^9 = 512 */
  int16_t   hDefKdGain;           /**< Default Kd gain */
  int16_t   hKdGain;              /**< Kd gain used by PID component */
  uint16_t  hKdDivisor;           /**< Kd gain divisor, used in conjuction with
                                       Kd gain allows obtaining fractional values.
                                       If FULL_MISRA_C_COMPLIANCY is not defined
                                       the divisor is implemented through
                                       algebrical right shifts to speed up PI
                                       execution. Only in this case this parameter
                                       specifies the number of right shifts to be
                                       executed */
  uint16_t  hKdDivisorPOW2;       /*!< Kd gain divisor expressed as power of 2.
                                       E.g. if gain divisor is 512 the value
                                       must be 9 as 2^9 = 512 */
  int32_t   wPrevProcessVarError; /*!< previous process variable used by the
                                       derivative part of the PID component */
} PID_Handle_t;
  •  变量命名的开头:h表示half word,w表示word。

(1)hKpGain、hKiGain、hKdGain

  • 代码中用到的PID增益参数

(2)hDefKpGain、hDefKiGain、hDefKdGain

  • 默认PID增益设置,在电机初始化时设置给hKpGain、hKiGain、hKdGain。个人感觉没必要再用这三个变量,老版本的ST电机库也没有这些参数。

(3)wIntegralTerm

  • 积分项

(4)wPrevProcessVarError

  • 微分项需要记录的每次的误差

(5)wUpperIntegralLimit、wLowerIntegralLimit

  • 积分项最大值限制、积分项最小值限制

(6)hUpperOutputLimit、hLowerOutputLimit

  • PID输出最大值限制、PID输出最小值限制

(7)hKpDivisor、hKiDivisor、hKdDivisor

  • PID的增益参数应该是小数,而定点PID的增益是整数,需要除以一个比例

(8)hKpDivisorPOW2、hKiDivisorPOW2、hKdDivisorPOW2

  • CPU算除法效率不太高,所以上面的PID增益的除数一般设置为​​​​​​​\(2^{^{n}}\),对应​​​​​​​\(hKxDivisorPOW2=\log_2 hKxDivisor\),这样在代码中算术右移,这样大大提高了运算速率。

PID函数实现

有的控制不需要用到微分项,所以ST将PID函数分为PI控制函数和PID控制函数。代码很简单,具体参考注释。

PI控制函数

__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;

  /* 计算比例项 */
  wProportional_Term = pHandle->hKpGain * wProcessVarError;

  /* 计算积分项 */
  if ( pHandle->hKiGain == 0 )
  {
  	/* 积分增益为0,清空之前的积分项 */
    pHandle->wIntegralTerm = 0;
  }
  else
  {
  	/* 积分增益非0,计算本次误差的积分 */
    wIntegral_Term = pHandle->hKiGain * wProcessVarError;
	/* 加上之前已经累加的积分 */
    wIntegral_sum_temp = pHandle->wIntegralTerm + wIntegral_Term;
	/* 判断溢出 */
    if ( wIntegral_sum_temp < 0 )
    {
      if ( pHandle->wIntegralTerm > 0 )
      {
        if ( wIntegral_Term > 0 )
        {
          /* 如果本次积分与之前的积分都为正,其和小于0,说明超过了INT32_MAX */
          wIntegral_sum_temp = INT32_MAX;
        }
      }
    }
    else
    {
      if ( pHandle->wIntegralTerm < 0 )
      {
        if ( wIntegral_Term < 0 )
        {
          /* 如果本次积分与之前的积分都为负,其和大于0,说明超过了-INT32_MAX */
          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;
    }
  }

/* 定点PID,需要除以一个比例 */
#ifdef FULL_MISRA_C_COMPLIANCY
  wOutput_32 = ( wProportional_Term / ( int32_t )pHandle->hKpDivisor ) + ( pHandle->wIntegralTerm /
               ( int32_t )pHandle->hKiDivisor );
#else
  /* 需要保证编译器对这行代码的处理是算术右移,而不是逻辑右移 */
  wOutput_32 = ( wProportional_Term >> pHandle->hKpDivisorPOW2 ) + ( pHandle->wIntegralTerm >> pHandle->hKiDivisorPOW2 );
#endif
  /* 判断PID输出结果是否超过用户定义的范围 */
  if ( wOutput_32 > hUpperOutputLimit )
  {
    /* 超过用户定义的最大范围,将本次超过的值赋给wDischarge */
    wDischarge = hUpperOutputLimit - wOutput_32;
    wOutput_32 = hUpperOutputLimit;
  }
  else if ( wOutput_32 < hLowerOutputLimit )
  {
  	/* 超过用户定义的最小范围,将本次超过的值赋给wDischarge */
    wDischarge = hLowerOutputLimit - wOutput_32;
    wOutput_32 = hLowerOutputLimit;
  }
  else { /* Nothing to do here */ }
  /* 将wDischarge赋值给积分项,下一次计算用 */
  pHandle->wIntegralTerm += wDischarge;

  return ( ( int16_t )( wOutput_32 ) );
}

PID控制函数

__weak int16_t PID_Controller( PID_Handle_t * pHandle, int32_t wProcessVarError )
{
  int32_t wDifferential_Term;
  int32_t wDeltaError;
  int32_t wTemp_output;

  if ( pHandle->hKdGain != 0 ) /* derivative terms not used */
  {
  	/* 积分项计算本次误差和上次的误差的差值 */
    wDeltaError = wProcessVarError - pHandle->wPrevProcessVarError;
	/* 计算微分项 */
    wDifferential_Term = pHandle->hKdGain * wDeltaError;

#ifdef FULL_MISRA_C_COMPLIANCY
    wDifferential_Term /= ( int32_t )pHandle->hKdDivisor;
#else
    /* WARNING: the below instruction is not MISRA compliant, user should verify
    that Cortex-M3 assembly instruction ASR (arithmetic shift right)
    is used by the compiler to perform the shifts (instead of LSR
    logical shift right)*/
    /* 定点PID,需要除以一个比例 */
    wDifferential_Term >>= pHandle->hKdDivisorPOW2;
#endif
	/* 记录本次的微分项 */
    pHandle->wPrevProcessVarError = wProcessVarError;
	/* 计算比例项和积分项,再加上微分项作为本次输出 */
    wTemp_output = PI_Controller( pHandle, wProcessVarError ) + wDifferential_Term;
	/* 用户定义的范围限制 */
    if ( wTemp_output > pHandle->hUpperOutputLimit )
    {
      wTemp_output = pHandle->hUpperOutputLimit;
    }
    else if ( wTemp_output < pHandle->hLowerOutputLimit )
    {
      wTemp_output = pHandle->hLowerOutputLimit;
    }
    else
    {}
  }
  else
  {
  	/* 微分增益为0,只计算比例项和积分项 */
    wTemp_output = PI_Controller( pHandle, wProcessVarError );
  }
  return ( ( int16_t ) wTemp_output );
}

参数调整

应该先调整内环再调整外环。这里定点PID实际应该先调整hKxDivisorPOW2的大小,如果没调好除法项就去调PID的增益,有可能怎么调参数最后PID输出都溢出。

### FOC 控制下的三相电机波形分析与实现 #### 1. 矢量控制(FOC)基本原理 矢量控制的核心在于通过对交流电机内部电磁过程的精确建模,利用坐标变换技术将复杂的非线性变量转换为类似于直流电机的行为模式[^5]。这种控制方式能够实现对电机磁场和转矩分量的独立调节,从而显著提升动态性能。 #### 2. 坐标变换的作用 为了简化控制系统设计并提高效率,FOC 使用了多种坐标变换方法来处理原始三相电流信号。具体来说,这些变换包括 Clarke 变换和 Park 变换: - **Clarke 变换**:用于将三相静止坐标系中的电流转化为两相旋转正交坐标系 (αβ)[^3]。 - **Park 变换**:进一步把 αβ 平面内的数据映射至 dq 同步旋转框架下,其中 d 轴代表磁通方向而 q 轴对应于产生转矩的部分。 经过上述两次变化之后,在理想情况下可以获得两个相互垂直且几乎完全解耦的新变量 id 和 iq ,它们分别表示激磁电流以及负载相关的扭矩贡献部分。 #### 3. 波形生成机制 在实际应用中,当采用 SVPWM(Space Vector Pulse Width Modulation) 技术作为 PWM 发生器时,可以得到接近正弦形状的空间电压矢量分布图样 。此过程中涉及到以下几个关键环节: ##### a. PI控制器调整 对于每一个周期内采集回来的实际反馈值(比如速度或者位置),通过比例积分运算得出期望达到的目标设定点;随后再依据误差大小计算出相应的补偿量应用于后续阶段直至满足精度需求为止 [^4]. ##### b. 解调与重构 从dq域返回abc自然轴之前还需要经历逆向操作即先执行反park后再做一次inv_clarke 这样才能还原成适合硬件驱动单元使用的标准形式即u_a,u_b,u_c三个分立通道上的指令脉冲序列[]. 最终输出给功率级器件如IGBTs/MOSFETs 的PWM占空比参数将会呈现出规律性的高低交替切换状态进而形成连续平滑过渡的理想型sinusoidal waveforms across all three phases under steady state conditions while maintaining high efficiency levels throughout transient periods as well []. ```matlab % MATLAB/Simulink Code Example for Generating Three-phase Sinusoidal Waveform using Field-Oriented Control (FOC) function pwm_out = generate_foc_waveform(speed_ref, torque_const) % Define constants and parameters here... % Implement speed controller logic to obtain current references i_d* & i_q* [~, ~, error_speed] = pidController(speed_ref); % Perform necessary transformations including Clarke and Park Transforms. alpha_beta_current = clarkeTransform(measured_ia_ib_ic); dq_axis_current = parkTransform(alpha_beta_current); % Apply decoupling compensations if needed before feeding into inner loops. v_d_star = piDq(dq_axis_current.id - desired_id_setpoint); v_q_star = piDq(dq_axis_current.iq - calculated_iq_from_torque(torque_const)); % Convert back from stationary reference frame to rotating one via inverse transforms. abc_voltage_commands = inv_park_transform([v_d_star; v_q_star]); final_pwm_signals = spaceVectorModulator(abc_voltage_commands); end ``` 以上伪代码展示了如何基于输入的速度参考值speed_ref并通过一系列数学运算最终获取适用于三相桥臂开关元件工作的pwm信号集合final_pwm_signals的过程概览。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tilblackout

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

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

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

打赏作者

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

抵扣说明:

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

余额充值