Link

运动控制实现 v1.6

SimpleFOC 实现了 3 种运动控制环路:

  • 使用电压的扭矩控制
  • 速度运动控制
  • 位置/角度控制

通过将 motor.controller 变量设置为 ControlType 结构中的一种来选择运动控制算法:

// Motion control type
enum ControlType{
  voltage,// Torque control using voltage
  velocity,// Velocity motion control
  angle// Position/angle motion control
};

设置方式如下:

motor.controller = ControlType::voltage;
// or
motor.controller = ControlType::velocity;
// or
motor.controller = ControlType::angle;

该变量也可以实时更改!

实时执行 move()

实时运动控制在 move() 函数内部执行。移动函数根据 controller 变量执行其中一个控制环路。move() 函数的参数 new_target 是要设置到控制环路的目标值。new_target 值是可选的,不需要设置。如果未设置,运动控制将使用 motor.target 变量。

以下是实现代码:

// Iterative function running outer loop of the FOC algorithm
// Behavior of this function is determined by the motor.controller variable
// It runs either angle, velocity or voltage loop
// - needs to be called iteratively it is asynchronous function
// - if target is not set it uses motor.target value
void BLDCMotor::move(float new_target = NOT_SET) {
  // check if target received through the parameter new_target
  // if not use the internal target variable (motor.target) 
  if( new_target != NOT_SET ) target = new_target;
  // get angular velocity
  shaft_velocity = shaftVelocity();
  // choose control loop
  switch (controller) {
    case ControlType::voltage:
      // set the target voltage for FCO loop
      voltage_q =  target;
      break;
    case ControlType::angle:
      // angle set point
      shaft_angle_sp = target;
      // calculate the necessary velocity to achieve target position
      shaft_velocity_sp = positionP( shaft_angle_sp - shaft_angle );
      // calculate necessary voltage to be set by FOC loop
      voltage_q = velocityPI(shaft_velocity_sp - shaft_velocity);
      break;
    case ControlType::velocity:
      // velocity set point
      shaft_velocity_sp = target;
      // calculate necessary voltage to be set by FOC loop
      voltage_q = velocityPI(shaft_velocity_sp - shaft_velocity);
      break;
  }
}

轴速度滤波 shaftVelocity

速度运动控制的第一步是从传感器获取速度值。由于某些传感器噪声很大,特别是在大多数情况下速度值是通过对位置值求导来计算的,因此我们实现了一个低通滤波器来平滑测量值。 速度计算函数是 shaftVelocity()

// shaft velocity calculation
float BLDCMotor::shaftVelocity() {
  float Ts = (_micros() - LPF_velocity.timestamp) * 1e-6;
  // quick fix for strange cases (micros overflow)
  if(Ts <= 0 || Ts > 0.5) Ts = 1e-3; 
  // calculate the filtering 
  float alpha = LPF_velocity.Tf/(LPF_velocity.Tf + Ts);
  float vel = alpha*LPF_velocity.prev + (1-alpha)*sensor->getVelocity();
  // save the variables
  LPF_velocity.prev = vel;
  LPF_velocity.timestamp = _micros();
  return vel;
}

低通滤波器是标准的一阶低通滤波器,具有一个时间常数 Tf,可通过 motor.LPF_velocity 结构进行配置:

// Low pass filter structure
struct LPF_s{
  float Tf; // Low pass filter time constant
  long timestamp; // Last execution timestamp
  float prev; // filtered value in previous execution step 
};

低通速度滤波器原理

有关低通滤波器的更多理论信息,请访问 理论爱好者角落

使用电压的扭矩控制

由于对于大多数低成本云台电机和驱动器,通常无法进行电流测量,因此必须直接使用电压进行扭矩控制。

该控制环路假设电压与电流成正比,而电流又与扭矩成正比。一般来说这是正确的,但并非总是如此。但在广义上,它在低电流应用(云台电机)中工作得很好。

这与我们通常对直流电机所做的假设相同。

控制环路的实现很简单,基本上是将目标电压设置到 voltage_q 变量,以便使用 FOC 算法 loopFOC() 设置到电机。

API 用法

有关如何使用此环路的更多信息,请参阅:电压环 API 文档

使用电压的扭矩控制理论

有关这种控制类型的更多理论信息,请访问 理论爱好者角落

速度运动控制

一旦我们获得了当前速度值和我们想要达到的目标值,我们需要计算要设置到电机的适当电压值,以跟随目标值。

这是通过在 velocityPI() 函数中使用 PI 控制器来完成的。

// velocity control loop PI controller
float BLDCMotor::velocityPI(float tracking_error) {
  return controllerPI(tracking_error, PID_velocity);
}

BLDCMotor 类实现了名为 controllerPI() 的通用 PI 控制器函数。

// PI controller function
float BLDCMotor::controllerPI(float tracking_error, PI_s& cont){
  float Ts = (_micros() - cont.timestamp) * 1e-6;

  // quick fix for strange cases (micros overflow)
  if(Ts <= 0 || Ts > 0.5) Ts = 1e-3; 

  // u(s) = (P + I/s)e(s)
  // Tustin transform of the PI controller ( a bit optimized )
  // uk = uk_1  + (I*Ts/2 + P)*ek + (I*Ts/2 - P)*ek_1
  float tmp = cont.I*Ts*0.5;
  float voltage = cont.voltage_prev + (tmp + cont.P) * tracking_error + (tmp - cont.P) * cont.tracking_error_prev;

  // antiwindup - limit the output voltage_q
  if (abs(voltage) > cont.voltage_limit) voltage = voltage > 0 ? cont.voltage_limit : -cont.voltage_limit;
  // limit the acceleration by ramping the the voltage
  float d_voltage = voltage - cont.voltage_prev;
  if (abs(d_voltage)/Ts > cont.voltage_ramp) voltage = d_voltage > 0 ? cont.voltage_prev + cont.voltage_ramp*Ts : cont.voltage_prev - cont.voltage_ramp*Ts;


  cont.voltage_prev = voltage;
  cont.tracking_error_prev = tracking_error;
  cont.timestamp = _micros();
  return voltage;
}

PI 控制器通过 motor.PID_velocity 结构进行配置:

// PI controller configuration structure
struct PI_s{
  float P; // Proportional gain 
  float I; // Integral gain 
  float voltage_limit; // Voltage limit of the controller output
  float voltage_ramp;  // Maximum speed of change of the output value 
  long timestamp;  // Last execution timestamp
  float voltage_prev;  // last controller output value 
  float tracking_error_prev;  // last tracking error value
};

API 用法

有关如何使用此环路的更多信息,请参阅:速度环 API 文档

PI 控制器理论

有关此库中实现的 PI 控制器的更多理论信息,请访问 理论爱好者角落

位置运动控制

现在,当我们解释了速度控制环路后,我们可以如图片所示级联构建我们的位置控制环路。

当我们有想要达到的目标角度时,我们将使用 P 控制器来计算我们需要的必要速度,然后速度环路将计算必要的电压 voltage_q 以达到我们想要的速度和角度。

位置 P 控制器在 positionP() 函数中实现:

// P controller for position control loop
float BLDCMotor::positionP(float ek) {
  // calculate the target velocity from the position error
  float velocity_target = P_angle.P * ek;
  // constrain velocity target value
  if (abs(velocity_target) > velocity_limit) velocity_target = velocity_target > 0 ? velocity_limit : -velocity_limit;
  return velocity_target;
}

它通过 motor.P_angle 结构进行配置:

// P controller configuration structure
struct P_s{
  float P; // Proportional gain 
  long timestamp; // Last execution timestamp
  float velocity_limit; // Velocity limit of the controller output
};

API 用法

有关如何使用此环路的更多信息,请参阅:角度环 API 文档