吃透 AM32 无人机电调:从源码架构到工作原理的全方位解析(附实践指南)(中)

我们计算出了三相电压的作用时间(Va、Vb、Vc),但实际应用中需限制占空比范围(0~100%),避免 “过调制” 导致电机失控。以下是完整的 SVPWM 生成代码及解析:核心代码片段(位于svpwm.c):

完整代码片段(位于svpwm.c

c

运行

#include "svpwm.h"
#include "tim.h"
#include "math.h"

#define PWM_PERIOD 1000  // PWM周期(对应TIM1的ARR=999,占空比范围0~1000)
uint16_t duty_u = 0, duty_v = 0, duty_w = 0;  // U、V、W相占空比(0~1000)

/**
 * @brief SVPWM生成:根据Vα、Vβ生成三相PWM占空比
 * @param Valpha:α轴电压(Q15格式)
 * @param Vbeta:β轴电压(Q15格式)
 */
void svpwm_generate(int16_t Valpha, int16_t Vbeta)
{
  int32_t Va, Vb, Vc;
  int32_t t1, t2, t0;  // 相邻有效矢量作用时间、零矢量作用时间
  uint8_t sector;       // 电压矢量所在扇区(1~6)

  // 1. 计算电压矢量的扇区(1~6,通过Vα、Vβ的正负和大小判断)
  sector = svpwm_calc_sector(Valpha, Vbeta);

  // 2. 根据扇区计算t1、t2(相邻有效矢量作用时间)
  switch (sector)
  {
    case 1:
      // 扇区1:有效矢量为V0(001)和V1(011),t1对应Vβ,t2对应Vα-Vβ
      t1 = (int32_t)Vbeta * PWM_PERIOD / 32768;  // Q15格式转换为实际值(除以32768)
      t2 = (int32_t)(Valpha - Vbeta) * PWM_PERIOD / 32768;
      break;
    case 2:
      // 扇区2:有效矢量为V1(011)和V2(010),t1对应Vα,t2对应-Vβ
      t1 = (int32_t)Valpha * PWM_PERIOD / 32768;
      t2 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
      break;
    case 3:
      // 扇区3:有效矢量为V2(010)和V3(110),t1对应-Valpha,t2对应-Valpha-Vbeta
      t1 = (int32_t)(-Valpha) * PWM_PERIOD / 32768;
      t2 = (int32_t)(-Valpha - Vbeta) * PWM_PERIOD / 32768;
      break;
    case 4:
      // 扇区4:有效矢量为V3(110)和V4(100),t1对应-Vbeta,t2对应-Valpha
      t1 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
      t2 = (int32_t)(-Valpha) * PWM_PERIOD / 32768;
      break;
    case 5:
      // 扇区5:有效矢量为V4(100)和V5(101),t1对应Valpha,t2对应Valpha+Vbeta
      t1 = (int32_t)Valpha * PWM_PERIOD / 32768;
      t2 = (int32_t)(Valpha + Vbeta) * PWM_PERIOD / 32768;
      break;
    case 6:
      // 扇区6:有效矢量为V5(101)和V0(001),t1对应Vbeta,t2对应-Vbeta
      t1 = (int32_t)Vbeta * PWM_PERIOD / 32768;
      t2 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
      break;
    default:
      t1 = 0;
      t2 = 0;
      break;
  }

  // 3. 计算零矢量作用时间(t0 = 周期 - t1 - t2,零矢量用于补充周期,避免占空比溢出)
  t0 = PWM_PERIOD - t1 - t2;
  // 零矢量时间分配(上下桥臂均衡,减少MOS管发热:t0/2分配到周期开头和结尾)
  int32_t t0_half = t0 / 2;

  // 4. 根据扇区分配时间,计算三相占空比(Va、Vb、Vc为各相的导通时间)
  switch (sector)
  {
    case 1:
      Va = t1 + t2 + t0_half;  // U相:t1 + t2 + 半零矢量
      Vb = t2 + t0_half;       // V相:t2 + 半零矢量
      Vc = t0_half;            // W相:半零矢量
      break;
    case 2:
      Va = t1 + t0_half;       // U相:t1 + 半零矢量
      Vb = t1 + t2 + t0_half;  // V相:t1 + t2 + 半零矢量
      Vc = t0_half;            // W相:半零矢量
      break;
    case 3:
      Va = t0_half;            // U相:半零矢量
      Vb = t1 + t2 + t0_half;  // V相:t1 + t2 + 半零矢量
      Vc = t2 + t0_half;       // W相:t2 + 半零矢量
      break;
    case 4:
      Va = t0_half;            // U相:半零矢量
      Vb = t1 + t0_half;       // V相:t1 + 半零矢量
      Vc = t1 + t2 + t0_half;  // W相:t1 + t2 + 半零矢量
      break;
    case 5:
      Va = t2 + t0_half;       // U相:t2 + 半零矢量
      Vb = t0_half;            // V相:半零矢量
      Vc = t1 + t2 + t0_half;  // W相:t1 + t2 + 半零矢量
      break;
    case 6:
      Va = t1 + t2 + t0_half;  // U相:t1 + t2 + 半零矢量
      Vb = t0_half;            // V相:半零矢量
      Vc = t1 + t0_half;       // W相:t1 + 半零矢量
      break;
    default:
      Va = 0;
      Vb = 0;
      Vc = 0;
      break;
  }

  // 5. 占空比限制:防止过调制(占空比必须在0~PWM_PERIOD之间,避免MOS管持续导通)
  duty_u = (Va < 0) ? 0 : (Va > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Va;
  duty_v = (Vb < 0) ? 0 : (Vb > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Vb;
  duty_w = (Vc < 0) ? 0 : (Vc > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Vc;

  // 6. 更新定时器CCR寄存器,输出PWM(TIM1的CCR1=U相,CCR2=V相,CCR3=W相)
  tim_set_pwm_duty(TIM1, TIM_CHANNEL_1, duty_u);
  tim_set_pwm_duty(TIM1, TIM_CHANNEL_2, duty_v);
  tim_set_pwm_duty(TIM1, TIM_CHANNEL_3, duty_w);
}

/**
 * @brief 定时器PWM占空比更新函数
 * @param htim:定时器句柄
 * @param channel:定时器通道(1~3)
 * @param duty:占空比(0~PWM_PERIOD)
 */
void tim_set_pwm_duty(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t duty)
{
  switch (channel)
  {
    case TIM_CHANNEL_1:
      htim->Instance->CCR1 = duty;  // 更新CCR1寄存器(U相)
      break;
    case TIM_CHANNEL_2:
      htim->Instance->CCR2 = duty;  // 更新CCR2寄存器(V相)
      break;
    case TIM_CHANNEL_3:
      htim->Instance->CCR3 = duty;  // 更新CCR3寄存器(W相)
      break;
    default:
      break;
  }
}

代码解析

  • 过调制保护:通过duty_u = (Va < 0) ? 0 : (Va > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Va限制占空比在 0~1000 之间,防止 MOS 管因持续导通(占空比 100%)过热烧毁;
  • 零矢量分配:将零矢量时间(t0)拆分为两半(t0_half),分别放在 PWM 周期的开头和结尾,确保上下桥臂的开关频率均衡,减少功率损耗;
  • 寄存器更新:直接操作定时器的CCR(捕获比较寄存器),实时更新占空比 ——MCU 每执行一次svpwm_generate(),就会刷新一次 PWM 信号,确保电机响应速度。
3.2.6 电流环 PID 控制:“精准控制电机转矩”

FOC 算法中,电流环是核心控制环节 —— 通过 PID 调节器,将实际电流(Id、Iq)与目标电流(Id_ref=0,Iq_ref 由油门指令换算)的偏差消除,输出精准的电压指令(Vd、Vq)。

电流环的控制精度直接决定电机的平顺性:若 PID 参数不当,会出现电机抖动、响应迟缓等问题。以下是 AM32 源码中电流环 PID 的实现与解析。

(1)PID 控制原理与 AM32 的优化

传统 PID 公式为:\(U(k) = K_p \cdot e(k) + K_i \cdot \sum_{0}^{k} e(i) + K_d \cdot [e(k) - e(k-1)]\)其中:

  • \(U(k)\):PID 输出(电压指令 Vd/Vq);
  • \(e(k)\):当前误差(目标电流 - 实际电流);
  • \(K_p\)(比例系数):快速响应误差,越大响应越快,但易超调;
  • \(K_i\)(积分系数):消除静态误差,越大静态误差越小,但易震荡;
  • \(K_d\)(微分系数):抑制超调,越大抗干扰越强,但易放大噪声。

AM32 源码对传统 PID 做了两处优化:

  1. 积分分离:当误差较大时(如电机启动瞬间),关闭积分项,避免积分饱和导致超调;
  2. 抗积分饱和:当 PID 输出达到上限(如 Vd/Vq 最大电压)时,停止积分累积,防止后续误差反向时积分项 “拖后腿”。
(2)电流环 PID 源码实现

PID 核心代码位于pid.c,包含 PID 初始化、参数更新、计算三个函数。以下是完整代码:

代码片段(位于pid.c

c

运行

#include "pid.h"

/**
 * @brief PID初始化:设置PID参数、积分限制、输出限制
 * @param pid:PID结构体指针
 * @param kp:比例系数(Q15格式)
 * @param ki:积分系数(Q15格式)
 * @param kd:微分系数(Q15格式)
 * @param integral_limit:积分上限(防止积分饱和)
 * @param output_limit:输出上限(防止过调制)
 */
void pid_init(PID_HandleTypeDef *pid, int16_t kp, int16_t ki, int16_t kd, 
              int32_t integral_limit, int16_t output_limit)
{
  pid->kp = kp;
  pid->ki = ki;
  pid->kd = kd;
  pid->integral_limit = integral_limit;  // 积分上限(如±10000)
  pid->output_limit = output_limit;      // 输出上限(如±32767,Q15格式)
  pid->integral = 0;       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值