Link

步进电机配置

所有步进电机都由StepperMotor类处理。该类实现了:

  • 步进电机FOC算法
  • 运动控制环路
  • 监控功能

使用SimpleFOC驱动步进电机主要有两种方式:

驱动器类型优点缺点
StepperMotorStepperDriver4PWM
StepperDriver2PWM
✔️ 能利用电机的全电压容量(速度)
✔️ 易于理解
❌ 2/4 PWM步进驱动器的选择非常有限
HybridStepperMotorBLDCDriver3PWM
BLDCDriver6PWM
✔️ 可以使用任何常规的BLDC驱动器❌ 电机的电压容量(速度)降低(最大70%)
❌ 理解起来稍复杂

这两个类都实现了FOC算法、运动控制环路以及电流检测,因此类的选择取决于可用的硬件。

步骤1. 创建步进电机实例

要创建步进电机实例,需要指定电机的极对数

StepperMotor HybridStepperMotor

// StepperMotor(  int pp, (optional R, KV, L))
// - pp            - pole pair number 
// - R             - motor phase resistance - optional
// - KV            - motor kv rating (rmp/v) - optional
// - L             - motor phase inductance [H] - optional
StepperMotor motor = StepperMotor(50, 1.5, 20.6, 0.01);
// HybridStepperMotor(  int pp, (optional R, KV, L))
// - pp            - pole pair number
// - R             - motor phase resistance - optional
// - KV            - motor kv rating (rmp/v) - optional
// - L             - motor phase inductance [H] - optional
HybridStepperMotor motor = HybridStepperMotor(50, 1.5, 20.6, 0.01);

极对数

大多数步进电机是每转200步的电机,这使它们成为50极对电机。实际上,可以通过将每转步数除以4来得到pole_pairs数。

如果不确定pole_pairs数是多少,库提供了一个示例代码来估算pole_pairs数,该示例在examples/utils/calibration/find_pole_pairs_number.ino中。

经验法则:KV值

我们建议将提供给库的KV值设置为比数据手册中给出的或通过实验确定的KV值高50-70%。根据电机的机械结构,合适的值将在电机KV额定值的100%到200%之间。

查找KV额定值

如果不确定电机的KV值,可以很容易地在电压扭矩控制下,将设定值设为1伏时电机的速度来确定,即velocity_at_one_volt。KV额定值的单位是转每分钟每伏,而SimpleFOC使用弧度每秒而不是转每分钟。因此,当获得1伏设定值下达到的速度时,可以将其乘以\(30/\pi\)

KV = velocity_at_one_volt * 30/pi

你也可以使用提供的库示例examples/utils/calibration/find_KV_rating.ino

电机相电阻和KV额定值

结合相电阻(在基于电流的扭矩模式foc_currentdc_current中不太常用)提供KV额定值,将使用户能够在不测量电流的情况下控制电机电流。用户将能够使用电压控制模式来控制(和限制)电机的估计电流。更多信息请参见扭矩控制文档

在许多方面,使用电流而不是电压更好,因为BLDC电机的扭矩与电流成正比,而不是与电压成正比,特别是因为相同的电压值会对不同的电机产生非常不同的电流(由于不同的相电阻)。一旦提供了相电阻值,用户将能够为其BLDC电机设置电流限制,而不是电压限制,这更容易理解。

需要说明的是,一旦指定了相电阻值,很可能需要重新调整速度运动控制角度运动控制参数,因为电压和电流值的数量级不同。经验法则是将所有PID增益除以motor.phase_resistance值,这将是一个很好的起点。

最后,如果要在基于电压(电压模式)和基于电流(直流电流FOC电流)的扭矩控制策略之间实时切换,建议使用此参数。因为这样所有的扭矩控制环路都将以电流作为输入(目标值),用户不必更改运动控制参数(PID值)。

开环运动控制将使用KV和相电阻值

KV额定值和相电阻值也将用于开环控制,以允许用户限制电机消耗的电流,而不是限制电压。更多信息请参见 开环运动控制文档

步骤2. 连接传感器

定义好motor并初始化传感器后,需要通过执行以下命令将motorsensor连接起来:

// link the sensor to the motor
motor.linkSensor(&sensor);

linkSensor方法能够将电机链接到该库中实现的任何传感器。sensor将用于确定FOC算法的电机电气位置以及速度和位置的运动控制环路。有关更多信息,请参见位置传感器文档

使用开环运动控制时无需链接。

步骤3. 连接驱动器

定义好motor并初始化驱动器后,需要通过执行以下命令将motordriver连接起来:

// link the driver to the motor
motor.linkDriver(&driver);

StepperMotor类期望接收StepperDriver类实例,而HybridStepperMotor可以接收BLDCDriver实例。driver处理与特定微控制器架构和驱动器硬件相关的所有硬件特定操作。有关更多信息,请参见步进驱动器文档

驱动器类型
StepperMotorStepperDriver4PWM
StepperDriver2PWM
HybridStepperMotorBLDCDriver3PWM
BLDCDriver6PWM

步骤4. 配置

如果选择不设置某些配置参数,它们将采用defaults.h文件中定义的值。 查看库源代码以深入了解。

步骤4.1 PWM调制类型

可以通过更改motor.foc_modulation变量来设置它们:

// choose FOC modulation
// FOCModulationType::SinePWM;
motor.foc_modulation = FOCModulationType::SinePWM;

目前,StepperMotor类仅实现了正弦PWM调制 当前版本,而HybridStepperMotor类支持正弦PWM和空间矢量PWM调制,其中空间矢量PWM效率更高,能提供更好的性能。

FOC调制类型
StepperMotorFOCModulationType::SinePWM
HybridStepperMotorFOCModulationType::SinePWM
FOCModulationType::SpaceVectorPWM(推荐)

有关这些方法的理论和源代码实现的更多信息,请查看FOC实现文档或访问深入探讨部分

步骤4.2 传感器和电机对齐参数

用于电机和传感器对齐的电压设置变量motor.voltage_sensor_align

// aligning voltage [V]
motor.voltage_sensor_align = 3; // default 3V

如果传感器是编码器并且有索引引脚,可以通过设置变量motor.velocity_index_search来设置索引搜索速度值:

// incremental encoder index search velocity [rad/s]
motor.velocity_index_search = 3; // default 1 rad/s

步骤4.3 位置传感器偏移

对于某些应用,指定传感器绝对零偏移是很方便的,可以通过更改参数motor.sensor_offset来定义:

// sensor offset [rad]
motor.sensor_offset = 0; // default 0 rad

此参数可以实时更改。

步骤4.4 电机相电阻和KV额定值

电机相电阻和KV额定值是可选参数,在基于电流的扭矩模式中不使用。这些变量用于在电压扭矩模式和开环运动控制中估算电机电流。如果用户指定了motor.phase_resistancemotor.KV_rating(无论是在构造函数中还是在setup()函数中),库将允许用户使用电流值,并会自动计算必要的电压。在设置函数中,可以通过以下方式更改此参数:

// motor phase resistance [Ohms]
motor.phase_resistance = 2.54; // Ohms - default not set
// motor KV rating [rpm/V]
motor.KV_rating = 100; // rpm/volt - default not set

更多信息请参见扭矩控制文档

步骤4.5 运动控制参数

Arduino SimpleFOC实现了3种不同的闭环控制策略:

此外,SimpleFOC还实现了两种开环控制策略:

通过更改motor.controller变量来设置:

// set FOC loop to be used
// MotionControlType::torque      - torque control loop using voltage
// MotionControlType::velocity    - velocity motion control
// MotionControlType::angle       - position/angle motion control
// MotionControlType::velocity_openloop    - velocity open-loop control
// MotionControlType::angle_openloop       - position open-loop control
motor.controller = MotionControlType::angle;

重要!

此参数没有默认值,必须在实时执行开始前设置。

每种运动控制策略都有其自己的参数,更多信息可以在运动控制文档中找到。

// set control loop type to be used
motor.controller = MotionControlType::angle;

// controller configuration based on the control type 
motor.PID_velocity.P = 0.2;
motor.PID_velocity.I = 20;
motor.PID_velocity.D = 0.001;

// velocity low pass filtering time constant
motor.LPF_velocity.Tf = 0.01;

// angle loop controller
motor.P_angle.P = 20;

// motion control limits
// angle loop velocity limit
motor.velocity_limit = 50;
// either voltage limit
motor.voltage_limit = 12; // Volts -  default driver.voltage_limit
// or current limit - if phase_resistance set
motor.current_limit = 1; // Amps -  default 2 Amps

步骤4.7 配置完成 - motor.init()

最后,通过运行init()函数终止配置,该函数使用配置的值准备所有硬件和软件电机组件。

// initialize motor
motor.init();

步骤5. 对齐电机和所有传感器 - 磁场定向控制初始化

在配置好位置传感器、驱动器和电机之后,在开始运动控制之前,需要对齐所有硬件组件以初始化FOC算法。这在motor.initFOC()函数的范围内完成:

// align sensor and start FOC
motor.initFOC();

开环控制可以跳过!

如果没有连接传感器,此函数实际上不会做任何事情,但如果有必要或更方便,仍然可以调用它。

此函数做了几件事:

  • 检查驱动器(和电流检测,如果可用)是否初始化良好
  • 检查/修改位置传感器方向相对于电机方向
  • 必要时搜索编码器索引
  • 找到相对于位置传感器的电机电气偏移
  • 检查/修改电流检测引脚分配和增益符号(如果有),以确保其与驱动器对齐

如果由于某种原因initFOC失败,此函数将返回0,并会禁用电机,并显示错误信息(当使用监控时)。如果所有配置都正确,调用此函数将返回1,设置完成,FOC可以使用了!因此,建议在继续之前检查初始化函数是否成功执行:

// init current sense
if (motor.initFOC())  Serial.println("FOC init success!");
else{
  Serial.println("FOC init failed!");
  return;
}

对齐过程必须多次移动电机,这可能不是理想的行为,因此对于大多数位置传感器(编码器除外)和电流检测,可以通过步骤5.1跳过此对齐过程。

步骤5.1 跳过对齐 - 位置传感器

如果使用绝对传感器,如磁性传感器或霍尔传感器,一旦完成对齐过程,并且知道电机的零电气偏移传感器方向,就不再需要完整的校准序列。因此,可以向motor.initFOC()提供传感器偏移zero_electric_offset和传感器方向sensor_direction,以避免对齐过程:

// align sensor and start FOC
//motor.initFOC(zero_electric_offset, sensor_direction);
motor.initFOC(2.15, Direction::CW);

也可以通过使用电机参数来完成:

// align sensor and start FOC
motor.zero_electric_offset  = 2.15; // rad
motor.sensor_direction = Direction::CW; // CW or CCW
motor.initFOC();

可以通过运行find_sensor_offset_and_direction.ino示例找到这些值。

更一般地说,如果知道这两个值中的任何一个,一定要提供,iniFOC将跳过校准的该部分。例如,对于编码器传感器,零电气偏移会一直变化,但传感器方向会保持不变,因此可以提供它并跳过大部分校准序列。

步骤6. 实时运动控制

Arduino SimpleFOC的实时运动控制通过两个函数实现:

  • motor.loopFOC() - 低级扭矩控制
  • motor.move(float target) - 高级运动控制

loopFOC()函数实现扭矩控制环路。由于步进电机仅支持使用电压模式的扭矩,此函数将从传感器读取当前电机角度,将其转换为电气角度,并将q轴Uq电压命令motor.voltage_q转换为适当的相电压uaubuc,然后将这些电压设置到电机。如果提供了步进电机的相电阻和KV额定值,此函数还将计算估计电流,用户将能够直接控制此估计电流值Iq

// Function running the low level torque control loop
// it calculates the gets motor angle and sets the appropriate voltages 
// to the phase pwm signals
// - the faster you can run it the better Arduino UNO ~1ms, Bluepill ~ 100us
motor.loopFOC();

开环控制可以跳过!

如果电机以开环方式运行,此函数将无效!

此函数的执行时间至关重要,因此motor.loopFOC()函数的执行速度越快越好。

经验法则:执行时间

这个函数运行得越快越好 😃

最后,一旦有了使用FOC算法向电机设置扭矩命令(电流Iq或电压Uq)的方法,就可以进行运动控制了。这通过motor.move()函数完成:

// Function executing the motion control loops configured by the motor.controller parameter of the motor. 
// - This function doesn't need to be run upon each loop execution - depends of the use case
//
// target  Either torque, angle or velocity based on the motor.controller
//         If it is not set the motor will use the target set in its variable motor.target
motor.move(target);

move()方法执行算法的运动控制环路。它由motor.controller变量控制。它执行纯扭矩环路、速度环路或角度环路。

它接收一个参数float target,这是用户定义的当前目标值。

  • 如果用户运行速度环路速度开环move函数将把target解释为目标速度。
  • 如果用户运行角度环路角度开环move将把target参数解释为目标角度。
  • 如果用户运行扭矩环路move函数将把target参数解释为电压uq或电流iq(如果提供了相电阻)。

target参数是可选的,如果未设置,目标值将由公共电机变量motor.target设置。等效代码如下:

motor.target = 2;
motor.move();

步骤6.1 运动控制下采样

对于许多运动控制应用,为每个运动控制环路运行多个扭矩控制环路是有意义的。这对平滑度有很大影响,并能提供更好的高速性能。因此,该库为move()函数启用了一种非常简单的下采样策略,通过参数motor.motion_downsample设置:

// downsampling value
motor.motion_downsample = 5; // - times (default 0 - disabled)

下采样策略的工作方式非常简单,即使在每个arduinoloop中调用motor.move(),它也只会每motor.motion_downsample次调用执行一次。此参数是可选的,可以实时配置。

注意:运动控制影响

不同的下采样值可能需要对运动参数进行一些调整。

这样,就有了完整的磁场定向控制步进电机及其运动控制。

用户交互

SimpleFOC实现了两种类型的实时用户交互:

深入探讨

有关FOC算法和运动控制方法的更多理论解释和源代码实现,请查看深入探讨部分

示例代码

一个简单的基于FOC算法的使用电压的步进电机扭矩控制示例。

StepperMotor HybridStepperMotor

/**
 * Torque control example using voltage control loop.
 */
#include <SimpleFOC.h>

// Stepper motor instance
StepperMotor motor = StepperMotor( 50 );
// Stepper driver instance
StepperDriver4PWM driver = StepperDriver4PWM(9, 10, 5, 6, 7, 8);
// sensor instance
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);

void setup() { 
  
  // initialize encoder sensor hardware
  sensor.init();
  // link the motor to the sensor
  motor.linkSensor(&sensor);

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 12;
  driver.init();
  // link driver
  motor.linkDriver(&driver);

  // set motion control loop to be used
  motor.controller = MotionControlType::torque;

  // initialize motor
  motor.init();
  // align sensor and start FOC
  motor.initFOC();

  // setting target voltage
  motor.target = 2;

  _delay(1000);
}

void loop() {

  // main FOC algorithm function
  motor.loopFOC();

  // Motion control function
  motor.move();
}
/**
 * Torque control example using voltage control loop.
 */
#include <SimpleFOC.h>

// Stepper motor instance
HybridStepperMotor motor = HybridStepperMotor( 50 );
// BLDC driver instance
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 10, 5, 8);
// sensor instance
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);

void setup() { 
  
  // initialize encoder sensor hardware
  sensor.init();
  // link the motor to the sensor
  motor.linkSensor(&sensor);

  // much more efficient than SinePWM
  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 12;
  driver.init();
  // link driver
  motor.linkDriver(&driver);

  // set motion control loop to be used
  motor.controller = MotionControlType::torque;

  // initialize motor
  motor.init();
  // align sensor and start FOC
  motor.initFOC();

  // setting target voltage
  motor.target = 2;

  _delay(1000);
}

void loop() {

  // main FOC algorithm function
  motor.loopFOC();

  // Motion control function
  motor.move();
}