文件里是基本的foc底层算法,pid,低通滤波,三角函数等
配置默认的参数值,可通过程序初始化设定进行改动
//电源电压
DEF_POWER_SUPPLY 12.0
//速度环PID控制器参数(速度环通常只用到PI)
DEF_PID_VEL_P 0.5
DEF_PID_VEL_I 10.0
DEF_PID_VEL_D 0.0
DEF_PID_VEL_U_RAMP 1000.0 //默认PID控制器电压斜坡值
//位置环P控制器参数(位置环这里只用到P)
DEF_P_ANGLE_P 20.0
DEF_VEL_LIM 20.0 //默认角速度限制
//索引搜索目标速度
DEF_INDEX_SEARCH_TARGET_VELOCITY 1.0 //默认索引搜索速度
//校准电压
DEF_VOLTAGE_SENSOR_ALIGN 6.0 //传感器和电机校准调零的默认电压
//低通滤波速度
DEF_VEL_FILTER_Tf 0.005 //默认低通滤波速度时间常数
头文件定义:
// 函数符号
_sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
_round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
_constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))//约束
// 基本参数定义
#define _2_SQRT3 1.15470053838
#define _SQRT3 1.73205080757
#define _1_SQRT3 0.57735026919
#define _SQRT3_2 0.86602540378
#define _SQRT2 1.41421356237
#define _120_D2R 2.09439510239
#define _PI 3.14159265359
#define _PI_2 1.57079632679
#define _PI_3 1.0471975512
#define _2PI 6.28318530718
#define _3PI_2 4.71238898038
#define NOT_SET -12345.0
角度相关计算函数:
使用int数组代替float数组进行运算,2x存储保存(int 2Byte float 4Byte)
sin*10000
int sine_array[200] = {0,79,158,237,316,395,473,552,631,710,789,867,946,1024,1103,1181,1260,1338,1416,1494,1572,1650,1728,1806,1883,1961,2038,2115,2192,2269,2346,2423,2499,2575,2652,2728,2804,2879,2955,3030,3105,3180,3255,3329,3404,3478,3552,3625,3699,3772,3845,3918,3990,4063,4135,4206,4278,4349,4420,4491,4561,4631,4701,4770,4840,4909,4977,5046,5113,5181,5249,5316,5382,5449,5515,5580,5646,5711,5775,5839,5903,5967,6030,6093,6155,6217,6279,6340,6401,6461,6521,6581,6640,6699,6758,6815,6873,6930,6987,7043,7099,7154,7209,7264,7318,7371,7424,7477,7529,7581,7632,7683,7733,7783,7832,7881,7930,7977,8025,8072,8118,8164,8209,8254,8298,8342,8385,8428,8470,8512,8553,8594,8634,8673,8712,8751,8789,8826,8863,8899,8935,8970,9005,9039,9072,9105,9138,9169,9201,9231,9261,9291,9320,9348,9376,9403,9429,9455,9481,9506,9530,9554,9577,9599,9621,9642,9663,9683,9702,9721,9739,9757,9774,9790,9806,9821,9836,9850,9863,9876,9888,9899,9910,9920,9930,9939,9947,9955,9962,9969,9975,9980,9985,9989,9992,9995,9997,9999,10000,10000};
正弦函数(用定长数组逼近正弦函数),输入0-2π角度
float _sin(float a){
if(a < _PI_2){
return 0.0001*sine_array[_round(126.6873* a)]; //整数数组优化
}else if(a < _PI){
return 0.0001*sine_array[398 - _round(126.6873*a)];
}else if(a < _3PI_2){
return -0.0001*sine_array[-398 + _round(126.6873*a)];
} else {
return -0.0001*sine_array[796 - _round(126.6873*a)];
}
}
余弦函数(用定长数组逼近正弦函数),输入0-2π角度
float _cos(float a){
float a_sin = a + _PI_2;
a_sin = a_sin > _2PI ? a_sin - _2PI : a_sin;
return _sin(a_sin);
}
弧度规格化成0-2π
float _normalizeAngle(float angle){
float a = fmod(angle, _2PI);
return a >= 0 ? a : (a + _2PI);
}
电角度计算(shaft_angle 轴角, pole_pairs 极对数)
float _electricalAngle(float shaft_angle, int pole_pairs) {
return (shaft_angle * pole_pairs);
}
低通滤波
头文件类定义:
class LowPassFilter
{
public:
/**
* @param Tf - Low pass filter time constant
*/
LowPassFilter(float Tf);
~LowPassFilter() = default;
float operator() (float x);
float Tf; //!< Low pass filter time constant
protected:
unsigned long timestamp_prev; //!< Last execution timestamp
float y_prev; //!< filtered value in previous execution step
};
函数:
LowPassFilter::LowPassFilter(float time_constant)
: Tf(time_constant)
, y_prev(0.0f)
{
timestamp_prev = _micros();
}
float LowPassFilter::operator() (float x)
{
unsigned long timestamp = _micros();
float dt = (timestamp - timestamp_prev)*1e-6f;
if (dt < 0.0f || dt > 0.5f)
dt = 1e-3f;
float alpha = Tf/(Tf + dt);
float y = alpha*y_prev + (1.0f - alpha)*x;
y_prev = y;
timestamp_prev = timestamp;
return y;
}
头文件类定义
/**
* PID controller class
*/
class PIDController
{
public:
/**
*
* @param P - Proportional gain
* @param I - Integral gain
* @param D - Derivative gain
* @param ramp - Maximum speed of change of the output value
* @param limit - Maximum output value
*/
PIDController(float P, float I, float D, float ramp, float limit);
~PIDController() = default;
float operator() (float error);
float P; //!< Proportional gain
float I; //!< Integral gain
float D; //!< Derivative gain
float output_ramp; //!< Maximum speed of change of the output value
float limit; //!< Maximum output value
protected:
float integral_prev; //!< last integral component value
float error_prev; //!< last tracking error value
float timestamp_prev; //!< Last execution timestamp
float output_prev; //!< last pid output value
};
具体函数
PIDController::PIDController(float P, float I, float D, float ramp, float limit)
: P(P)
, I(I)
, D(D)
, output_ramp(ramp) // 输出导数限制(斜坡) [volts/second]
, limit(limit) // 输出数值(电压)限制 [volts]
, integral_prev(0.0)
, error_prev(0.0)
, output_prev(0.0)
{
timestamp_prev = _micros()*1e-6;
}
// PID控制器功能
float PIDController::operator() (float error){
// 计算上一次时间
unsigned long timestamp_now = _micros();
float Ts = (timestamp_now - timestamp_prev) * 1e-6;
// 异常情况修复(时间溢出)
if(Ts <= 0 || Ts > 0.5) Ts = 1e-3;
// u(s) = (P + I/s + Ds)e(s)
// 实现离散
// 比例部分
// u_p = P *e(k)
float proportional = P * error;
// 积分部分双线性变换
// u_ik = u_ik_1 + I*Ts/2*(ek + ek_1)
float integral = integral_prev + I*Ts*0.5*(error + error_prev);
// 抗饱和-限制输出电压
integral = _constrain(integral, -limit, limit);
// 离散导数
// u_dk = D(ek - ek_1)/Ts
float derivative = D*(error - error_prev)/Ts;
// 所有部分总和
float output = proportional + integral + derivative;
// 限制输出变量
output = _constrain(output, -limit, limit);
// 斜坡处理,限制输出加速度
float output_rate = (output - output_prev)/Ts;
if (output_rate > output_ramp)
output = output_prev + output_ramp*Ts;
else if (output_rate < -output_ramp)
output = output_prev - output_ramp*Ts;
// 保存本次变量
integral_prev = integral;
output_prev = output;
error_prev = error;
timestamp_prev = timestamp_now;
return output;
}
主要包含延时函数( _delay)和获取时间戳函数( _micros)
class BLDCDriver{
public:
/** 初始化硬件 */
virtual int init();
/** 开启硬件 */
virtual void enable();
/** 关闭硬件 */
virtual void disable();
long pwm_frequency; //PWM频率(hz)
float voltage_power_supply; //电源电压
float voltage_limit; //限制电机电压
/**
* 设置相电压 (ABC相)
*
* @param Ua - phase A voltage
* @param Ub - phase B voltage
* @param Uc - phase C voltage
*/
virtual void setPwm(float Ua, float Ub, float Uc);
};
class StepperDriver{
public:
/** 初始化硬件 */
virtual int init();
/** 开启 */
virtual void enable();
/** 关闭 */
virtual void disable();
long pwm_frequency; //pwm频率(hz)
float voltage_power_supply; //电源电压
float voltage_limit; //限制电机电压
/**
* 设置相电压 (AB相)
*
* @param Ua phase A voltage
* @param Ub phase B voltage
*/
virtual void setPwm(float Ua, float Ub);
};
/**
* 方向枚举
*/
enum Direction{
CW = 1, //clockwise 顺时针
CCW = -1, //counter clockwise 逆时针
UNKNOWN = 0 //无知或无效状态
};
/**
* 上拉配置枚举
*/
enum Pullup{
INTERN, //使用内部上拉
EXTERN //使用外部上拉
};
/**
* 传感器抽象类定义
* 每个传感器都需要实现这些功能
*/
class Sensor{
public:
/** 获取当前角度 (rad) */
virtual float getAngle();
/** 获取当前角速度 (rad/s)*/
virtual float getVelocity();
/**
* 将当前角度设置为零角度
* 返回角度差(rad)
*/
virtual float initRelativeZero();
/**
* 将绝对零角设置为零角
* 返回角度差(rad)
*/
virtual float initAbsoluteZero();
// 如果自然方向为ccw逆时针,则将方向翻转为cw顺时针
int natural_direction = Direction::CW;
/**
* 如果没有绝对0度值,则返回0
* 0 - 无标志增量式编码器
* 1 - 带标志和磁传感器的编码器
*/
virtual int hasAbsoluteZero();
/**
* 如果要搜索绝对0度值,则返回0
* 0 - 磁传感器 (找到有标志的编码器)
* 1 - 带标志的编码器 (尚未找到标志)
*/
virtual int needsAbsoluteZeroSearch();
};
/**
* 运动控制类型枚举
*/
enum ControlType{
voltage, //电压转矩闭环控制
velocity, //速度闭环控制
angle, //位置/角度闭环控制
velocity_openloop, //速度开环控制
angle_openloop //角度开环控制
};
/**
* FOC调制类型
*/
enum FOCModulationType{
SinePWM, //正弦PWM调制
SpaceVectorPWM, //空间矢量脉宽调制(SVPWM)
Trapezoid_120, //梯形轨迹120
Trapezoid_150 //梯形轨迹150
};
/**
通用电机类
*/
class FOCMotor
{
public:
/**
* 默认构造函数,将所有变量设置为默认值
*/
FOCMotor();
/** 电机硬件初始化 */
virtual void init()=0;
/** 电机禁用 */
virtual void disable()=0;
/** 电机使能 */
virtual void enable()=0;
/**
* 连接电机和传感器
* @param Sensor sensor类,用于FOC算法读取电机角度和速度
*/
void linkSensor(Sensor* sensor);
/**
* 初始化FOC算法函数
* 调整传感器和电机的零位
* 如果设置了zero_electric_offset 参数,则跳过对齐过程
*
* @param zero_electric_offset 相对电机0位置的传感器的绝对位置偏移值.
* @param sensor_direction 传感器自然转向,默认为CW顺时针
*
*/
virtual int initFOC( float zero_electric_offset = NOT_SET , Direction sensor_direction = Direction::CW)=0;
/**
* 函数实时运行FOC算法
* 计算电机角度并设置适合的电压
* 相位PWM信号
* 运行周期越快越好
*/
virtual void loopFOC()=0;
/**
* 执行由无刷直流电机控制器参数设置的控制回路的函数.
* @param target 基于电机驱动的目标电压、角度或速度
* 如果这个参数未设置,电机将使用其变量中设置的目标运动目标
* 这个函数不需要在每次循环执行时运行,取决于如何使用
*/
virtual void move(float target = NOT_SET)=0;
// 状态计算方法
/** 轴角度计算 [rad] */
float shaftAngle();
/**
* 轴角计算函数 [rad/s]
* 实现了低通滤波
*/
float shaftVelocity();
// 状态变量
float target; //当前目标值
float shaft_angle;//当前电机角度
float shaft_velocity;//当前电机速度
float shaft_velocity_sp;//当前目标速度
float shaft_angle_sp;//当前目标角度
float voltage_q;//当前 u_q 设定
float voltage_d;//当前 u_d 设定
float voltage_sensor_align;//传感器和电机对齐电压参数
float velocity_index_search;//标志搜索的目标速度
int pole_pairs; //电机极对数
//限制变量
float voltage_limit; //全局电压限制
float velocity_limit; //全局速度限制
float zero_electric_angle;//绝对零角限制(如果有的话)
// 初始化构造
ControlType controller; //确定要使用的控制回路参数
FOCModulationType foc_modulation;//foc算法
PIDController PID_velocity{DEF_PID_VEL_P,DEF_PID_VEL_I,DEF_PID_VEL_D,DEF_PID_VEL_U_RAMP,DEF_POWER_SUPPLY};//速度环PI配置参数
PIDController P_angle{DEF_P_ANGLE_P,0,0,1e10,DEF_VEL_LIM};//位置环P配置参数
LowPassFilter LPF_velocity{DEF_VEL_FILTER_Tf};//速度低通滤波器配置参数
/**
* 函数为BLDCMotor类提供串口和启用监控功能
* @param serial
*/
void useMonitoring(Print &serial);
/**
* 与串口绘图仪一起使用的实用功能,用于监控电机变量,这会大大降低执行速度!
*/
void monitor();
/**
*功能设定电机的配置参数,控制回路的目标值
*并将其输出到监控端口(如果可用):
*-配置PID控制器常数
*-更改运动控制回路
*-监控电机变量
*-设定目标值
*-检查所有配置值
*
*要检查配置值,只需输入命令字母。
*例如:
*-读取速度PI控制器P增益运行:P
*-将速度PI控制器P增益设置为1.2运行:P1.2
*
*要更改目标值,只需在终端中输入一个数字:
*例如:
*-要将目标值更改为-0.1453,请输入:-0.1453
*-要获取当前目标值,请输入:V3
*
*命令列表:
*-P:速度PI控制器P增益
*I:速度PI控制器I增益
*L:速度PI控制器电压限制
*R:速度PI控制器电压斜坡
*-F:速度低通滤波器时间常数
*K:角度P控制器P增益
*N:P角控制器速度限制
*C:控制回路
*-0:电压
*-1:速度
*-2:角度
*-V:获取电机变量
*-0:当前设定电压
*-1:流速
*-2:电流角
*-3:当前目标值
*@param command包含用户命令的字符串
*对于错误返回0,对于已执行的命令返回1
*/
int command(String command);
/**
* 连接传感器:
* - 编码器
* - 磁编码器
* - 霍尔传感器
*/
Sensor* sensor;
// 监控功能
Print* monitor_port; //串口终端变量(如果有)
};
/**
* Default constructor - setting all variabels to default values
*/
FOCMotor::FOCMotor()
{
// maximum angular velocity to be used for positioning
velocity_limit = DEF_VEL_LIM;
// maximum voltage to be set to the motor
voltage_limit = DEF_POWER_SUPPLY;
// index search velocity
velocity_index_search = DEF_INDEX_SEARCH_TARGET_VELOCITY;
// sensor and motor align voltage
voltage_sensor_align = DEF_VOLTAGE_SENSOR_ALIGN;
// electric angle of comthe zero angle
zero_electric_angle = 0;
// default modulation is SinePWM
foc_modulation = FOCModulationType::SinePWM;
// default target value
target = 0;
voltage_d = 0;
voltage_q = 0;
//monitor_port
monitor_port = nullptr;
//sensor
sensor = nullptr;
}
/**
Sensor communication methods
*/
void FOCMotor::linkSensor(Sensor* _sensor) {
sensor = _sensor;
}
// shaft angle calculation
float FOCMotor::shaftAngle() {
// if no sensor linked return 0
if(!sensor) return shaft_angle;
return sensor->getAngle();
}
// shaft velocity calculation
float FOCMotor::shaftVelocity() {
// if no sensor linked return 0
if(!sensor) return 0;
return LPF_velocity(sensor->getVelocity());
}
/**
* Monitoring functions
*/
// function implementing the monitor_port setter
void FOCMotor::useMonitoring(Print &print){
monitor_port = &print; //operate on the address of print
if(monitor_port ) monitor_port->println("MOT: Monitor enabled!");
}
// utility function intended to be used with serial plotter to monitor motor variables
// significantly slowing the execution down!!!!
void FOCMotor::monitor() {
if(!monitor_port) return;
switch (controller) {
case ControlType::velocity_openloop:
case ControlType::velocity:
monitor_port->print(voltage_q);
monitor_port->print("\t");
monitor_port->print(shaft_velocity_sp);
monitor_port->print("\t");
monitor_port->println(shaft_velocity);
break;
case ControlType::angle_openloop:
case ControlType::angle:
monitor_port->print(voltage_q);
monitor_port->print("\t");
monitor_port->print(shaft_angle_sp);
monitor_port->print("\t");
monitor_port->println(shaft_angle);
break;
case ControlType::voltage:
monitor_port->print(voltage_q);
monitor_port->print("\t");
monitor_port->print(shaft_angle);
monitor_port->print("\t");
monitor_port->println(shaft_velocity);
break;
}
}
int FOCMotor::command(String user_command) {
// error flag
int errorFlag = 1;
// if empty string
if(user_command.length() < 1) return errorFlag;
// parse command letter
char cmd = user_command.charAt(0);
// check if get command
char GET = user_command.charAt(1) == '\n';
// parse command values
float value = user_command.substring(1).toFloat();
// a bit of optimisation of variable memory for Arduino UNO (atmega328)
switch(cmd){
case 'P': // velocity P gain change
case 'I': // velocity I gain change
case 'D': // velocity D gain change
case 'R': // velocity voltage ramp change
if(monitor_port) monitor_port->print(" PID velocity| ");
break;
case 'F': // velocity Tf low pass filter change
if(monitor_port) monitor_port->print(" LPF velocity| ");
break;
case 'K': // angle loop gain P change
if(monitor_port) monitor_port->print(" P angle| ");
break;
case 'L': // velocity voltage limit change
case 'N': // angle loop gain velocity_limit change
if(monitor_port) monitor_port->print(" Limits| ");
break;
}
// apply the the command
switch(cmd){
case 'P': // velocity P gain change
if(monitor_port) monitor_port->print("P: ");
if(!GET) PID_velocity.P = value;
if(monitor_port) monitor_port->println(PID_velocity.P);
break;
case 'I': // velocity I gain change
if(monitor_port) monitor_port->print("I: ");
if(!GET) PID_velocity.I = value;
if(monitor_port) monitor_port->println(PID_velocity.I);
break;
case 'D': // velocity D gain change
if(monitor_port) monitor_port->print("D: ");
if(!GET) PID_velocity.D = value;
if(monitor_port) monitor_port->println(PID_velocity.D);
break;
case 'R': // velocity voltage ramp change
if(monitor_port) monitor_port->print("volt_ramp: ");
if(!GET) PID_velocity.output_ramp = value;
if(monitor_port) monitor_port->println(PID_velocity.output_ramp);
break;
case 'L': // velocity voltage limit change
if(monitor_port) monitor_port->print("volt_limit: ");
if(!GET) {
voltage_limit = value;
PID_velocity.limit = value;
}
if(monitor_port) monitor_port->println(voltage_limit);
break;
case 'F': // velocity Tf low pass filter change
if(monitor_port) monitor_port->print("Tf: ");
if(!GET) LPF_velocity.Tf = value;
if(monitor_port) monitor_port->println(LPF_velocity.Tf);
break;
case 'K': // angle loop gain P change
if(monitor_port) monitor_port->print(" P: ");
if(!GET) P_angle.P = value;
if(monitor_port) monitor_port->println(P_angle.P);
break;
case 'N': // angle loop gain velocity_limit change
if(monitor_port) monitor_port->print("vel_limit: ");
if(!GET){
velocity_limit = value;
P_angle.limit = value;
}
if(monitor_port) monitor_port->println(velocity_limit);
break;
case 'C':
// change control type
if(monitor_port) monitor_port->print("Control: ");
if(GET){ // if get command
switch(controller){
case ControlType::voltage:
if(monitor_port) monitor_port->println("voltage");
break;
case ControlType::velocity:
if(monitor_port) monitor_port->println("velocity");
break;
case ControlType::angle:
if(monitor_port) monitor_port->println("angle");
break;
default:
if(monitor_port) monitor_port->println("open loop");
}
}else{ // if set command
switch((int)value){
case 0:
if(monitor_port) monitor_port->println("voltage");
controller = ControlType::voltage;
break;
case 1:
if(monitor_port) monitor_port->println("velocity");
controller = ControlType::velocity;
break;
case 2:
if(monitor_port) monitor_port->println("angle");
controller = ControlType::angle;
break;
default: // not valid command
errorFlag = 0;
}
}
break;
case 'V': // get current values of the state variables
switch((int)value){
case 0: // get voltage
if(monitor_port) monitor_port->print("Uq: ");
if(monitor_port) monitor_port->println(voltage_q);
break;
case 1: // get velocity
if(monitor_port) monitor_port->print("Velocity: ");
if(monitor_port) monitor_port->println(shaft_velocity);
break;
case 2: // get angle
if(monitor_port) monitor_port->print("Angle: ");
if(monitor_port) monitor_port->println(shaft_angle);
break;
case 3: // get angle
if(monitor_port) monitor_port->print("Target: ");
if(monitor_port) monitor_port->println(target);
break;
default: // not valid command
errorFlag = 0;
}
break;
default: // target change
if(monitor_port) monitor_port->print("Target : ");
target = user_command.toFloat();
if(monitor_port) monitor_port->println(target);
}
// return 0 if error and 1 if ok
return errorFlag;
}
特定硬件配置,为了适配不同mcu而使用不同的配置
atmega328_mcu
atmega2560_mcu
esp32_mcu
stm32_mcu
teensy_mcu
头文件:
/**
3 pwm bldc 驱动器类
*/
class BLDCDriver3PWM: public BLDCDriver
{
public:
/**
BLDCDriver class constructor
@param phA A phase pwm pin
@param phB B phase pwm pin
@param phC C phase pwm pin
@param en enable pin (optional input)
*/
BLDCDriver3PWM(int phA,int phB,int phC, int en = NOT_SET);
/** Motor hardware init function 电机硬件初始化 */
int init() override;
/** Motor disable function 电机禁用函数*/
void disable() override;
/** Motor enable function 电机使能函数*/
void enable() override;
// 变量
int pwmA; //!< phase A pwm pin number PWMA的引脚号
int pwmB; //!< phase B pwm pin number
int pwmC; //!< phase C pwm pin number
int enable_pin; //!< enable pin number
/**
* 设定硬件相电压
*
* @param Ua - phase A voltage
* @param Ub - phase B voltage
* @param Uc - phase C voltage
*/
void setPwm(float Ua, float Ub, float Uc) override;
private:
};
初始化:
/*相关外设成员参数初始化*/
BLDCDriver3PWM::BLDCDriver3PWM(int phA, int phB, int phC, int en){
// 引脚初始化
pwmA = phA;
pwmB = phB;
pwmC = phC;
// 使能引脚
enable_pin = en;
// 默认电源值
voltage_power_supply = DEF_POWER_SUPPLY;
voltage_limit = NOT_SET;
}
/* 使能电机驱动(使能en引脚,并输出pwm归零)*/
void BLDCDriver3PWM::enable(){
// enable_pin the driver - if enable_pin pin available
// 如果enable引脚可用,则使能
if ( enable_pin != NOT_SET ) digitalWrite(enable_pin, HIGH);
// set zero to PWM
//初始pwm设置0
setPwm(0,0,0);
}
/* 禁用电机驱动(关闭en引脚,并输出pwm归零)*/
void BLDCDriver3PWM::disable()
{
// set zero to PWM
setPwm(0, 0, 0);
// disable the driver - if enable_pin pin available
if ( enable_pin != NOT_SET ) digitalWrite(enable_pin, LOW);
}
/* 初始化硬件针脚 (配置pwm引脚输出,电压限制参数错误检测)*/
int BLDCDriver3PWM::init() {
// a bit of separation
_delay(1000);
// PWM pins
pinMode(pwmA, OUTPUT);
pinMode(pwmB, OUTPUT);
pinMode(pwmC, OUTPUT);
if(enable_pin != NOT_SET) pinMode(enable_pin, OUTPUT);
// 电压限制配置的健全性检查
if(voltage_limit == NOT_SET || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
// 将pwm频率设置为引脚
// 硬件特定功能-取决于驱动程序和mcu
_configure3PWM(pwm_frequency, pwmA, pwmB, pwmC);
return 0;
}
设定pwm引脚相电压
void BLDCDriver3PWM::setPwm(float Ua, float Ub, float Uc) {
// 限制驱动器电压
Ua = _constrain(Ua, 0.0, voltage_limit);
Ub = _constrain(Ub, 0.0, voltage_limit);
Uc = _constrain(Uc, 0.0, voltage_limit);
// 计算占空比
// 限制在 [0,1]
float dc_a = _constrain(Ua / voltage_power_supply, 0.0 , 1.0 );
float dc_b = _constrain(Ub / voltage_power_supply, 0.0 , 1.0 );
float dc_c = _constrain(Uc / voltage_power_supply, 0.0 , 1.0 );
// 硬件特定写入
// 硬件特定功能-取决于驱动程序和mcu
_writeDutyCycle3PWM(dc_a, dc_b, dc_c, pwmA, pwmB, pwmC);
}
跟3pwm类似配置
头文件
/**
6 pwm bldc driver class
*/
class BLDCDriver6PWM: public BLDCDriver
{
public:
/**
BLDCDriver class constructor
@param phA_h A phase pwm pin
@param phA_l A phase pwm pin
@param phB_h B phase pwm pin
@param phB_l A phase pwm pin
@param phC_h C phase pwm pin
@param phC_l A phase pwm pin
@param en enable pin (optional input)
*/
BLDCDriver6PWM(int phA_h,int phA_l,int phB_h,int phB_l,int phC_h,int phC_l, int en = NOT_SET);
/** Motor hardware init function */
int init() override;
/** Motor disable function */
void disable() override;
/** Motor enable function */
void enable() override;
// hardware variables
int pwmA_h,pwmA_l; //!< phase A pwm pin number
int pwmB_h,pwmB_l; //!< phase B pwm pin number
int pwmC_h,pwmC_l; //!< phase C pwm pin number
int enable_pin; //!< enable pin number
float dead_zone; //!< a percentage of dead-time(zone) (both high and low side in low) for each pwm cycle [0,1]
/**
* Set phase voltages to the harware
*
* @param Ua - phase A voltage
* @param Ub - phase B voltage
* @param Uc - phase C voltage
*/
void setPwm(float Ua, float Ub, float Uc) override;
private:
};
函数构建:
BLDCDriver6PWM::BLDCDriver6PWM(int phA_h,int phA_l,int phB_h,int phB_l,int phC_h,int phC_l, int en){
// Pin initialization
pwmA_h = phA_h;
pwmB_h = phB_h;
pwmC_h = phC_h;
pwmA_l = phA_l;
pwmB_l = phB_l;
pwmC_l = phC_l;
// enable_pin pin
enable_pin = en;
// default power-supply value
voltage_power_supply = DEF_POWER_SUPPLY;
voltage_limit = NOT_SET;
// dead zone initial - 2%
dead_zone = 0.02;
}
// enable motor driver
void BLDCDriver6PWM::enable(){
// enable_pin the driver - if enable_pin pin available
if ( enable_pin != NOT_SET ) digitalWrite(enable_pin, HIGH);
// set zero to PWM
setPwm(0, 0, 0);
}
// disable motor driver
void BLDCDriver6PWM::disable()
{
// set zero to PWM
setPwm(0, 0, 0);
// disable the driver - if enable_pin pin available
if ( enable_pin != NOT_SET ) digitalWrite(enable_pin, LOW);
}
// init hardware pins
int BLDCDriver6PWM::init() {
// a bit of separation
_delay(1000);
// PWM pins
pinMode(pwmA_l, OUTPUT);
pinMode(pwmB_h, OUTPUT);
pinMode(pwmC_h, OUTPUT);
pinMode(pwmA_l, OUTPUT);
pinMode(pwmB_l, OUTPUT);
pinMode(pwmC_l, OUTPUT);
if(enable_pin != NOT_SET) pinMode(enable_pin, OUTPUT);
// sanity check for the voltage limit configuration
if(voltage_limit == NOT_SET || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
// configure 6pwm
// hardware specific function - depending on driver and mcu
return _configure6PWM(pwm_frequency, dead_zone, pwmA_h,pwmA_l, pwmB_h,pwmB_l, pwmC_h,pwmC_l);
}
// Set voltage to the pwm pin
void BLDCDriver6PWM::setPwm(float Ua, float Ub, float Uc) {
// limit the voltage in driver
Ua = _constrain(Ua, 0, voltage_limit);
Ub = _constrain(Ub, 0, voltage_limit);
Uc = _constrain(Uc, 0, voltage_limit);
// calculate duty cycle
// limited in [0,1]
float dc_a = _constrain(Ua / voltage_power_supply, 0.0 , 1.0 );
float dc_b = _constrain(Ub / voltage_power_supply, 0.0 , 1.0 );
float dc_c = _constrain(Uc / voltage_power_supply, 0.0 , 1.0 );
// hardware specific writing
// hardware specific function - depending on driver and mcu
_writeDutyCycle6PWM(dc_a, dc_b, dc_c, dead_zone, pwmA_h,pwmA_l, pwmB_h,pwmB_l, pwmC_h,pwmC_l);
}
/**
* 配置PWM频率、分辨率和校准
* - 步进驱动器-2PWM设置
* - hardware specific
*
* @param pwm_frequency - 频率(赫兹)-如果适用
* @param pinA pinA bldc驱动
* @param pinB pinB bldc驱动
*/
void _configure2PWM(long pwm_frequency, const int pinA, const int pinB);
/**
* 配置PWM频率、分辨率和校准
* - BLDC驱动器-3PWM设置
* - hardware specific
*
* @param pwm_frequency - frequency in hertz - if applicable
* @param pinA pinA bldc driver
* @param pinB pinB bldc driver
* @param pinC pinC bldc driver
*/
void _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC);
/**
* 配置PWM频率、分辨率和校准
* - 步进驱动器-4PWM设置
* - hardware specific
*
* @param pwm_frequency - frequency in hertz - if applicable
* @param pin1A pin1A stepper driver
* @param pin1B pin1B stepper driver
* @param pin2A pin2A stepper driver
* @param pin2B pin2B stepper driver
*/
void _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B);
/**
* 配置PWM频率、分辨率和校准
* - BLDC驱动器-6PWM设置
* - hardware specific
*
* @param pwm_frequency - frequency in hertz - if applicable
* @param dead_zone duty cycle protection zone [0, 1] - both low and high side low - if applicable
* @param pinA_h pinA high-side bldc driver
* @param pinA_l pinA low-side bldc driver
* @param pinB_h pinA high-side bldc driver
* @param pinB_l pinA low-side bldc driver
* @param pinC_h pinA high-side bldc driver
* @param pinC_l pinA low-side bldc driver
*
* @return 0 if config good, -1 if failed
*/
int _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l);
/**
* 设置pwm引脚的占空比功能 (ex. analogWrite())
* - 步进驱动器-2PWM设置
* - hardware specific
*
* @param dc_a duty cycle phase A [0, 1]
* @param dc_b duty cycle phase B [0, 1]
* @param pinA phase A hardware pin number
* @param pinB phase B hardware pin number
*/
void _writeDutyCycle2PWM(float dc_a, float dc_b, int pinA, int pinB);
/**
* 设置pwm引脚的占空比功能 (ex. analogWrite())
* - BLDC 驱动器 - 3PWM设置
* - hardware specific
*
* @param dc_a duty cycle phase A [0, 1]
* @param dc_b duty cycle phase B [0, 1]
* @param dc_c duty cycle phase C [0, 1]
* @param pinA phase A hardware pin number
* @param pinB phase B hardware pin number
* @param pinC phase C hardware pin number
*/
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, int pinA, int pinB, int pinC);
/**
* 设置pwm引脚的占空比功能 (ex. analogWrite())
* - 步进驱动器 - 4PWM setting
* - hardware specific
*
* @param dc_1a duty cycle phase 1A [0, 1]
* @param dc_1b duty cycle phase 1B [0, 1]
* @param dc_2a duty cycle phase 2A [0, 1]
* @param dc_2b duty cycle phase 2B [0, 1]
* @param pin1A phase 1A hardware pin number
* @param pin1B phase 1B hardware pin number
* @param pin2A phase 2A hardware pin number
* @param pin2B phase 2B hardware pin number
*/
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, int pin1A, int pin1B, int pin2A, int pin2B);
/**
* 设置pwm引脚的占空比功能 (ex. analogWrite())
* - BLDC 驱动器 - 6PWM setting
* - hardware specific
*
* @param dc_a duty cycle phase A [0, 1]
* @param dc_b duty cycle phase B [0, 1]
* @param dc_c duty cycle phase C [0, 1]
* @param dead_zone duty cycle protection zone [0, 1] - both low and high side low
* @param pinA_h phase A high-side hardware pin number
* @param pinA_l phase A low-side hardware pin number
* @param pinB_h phase B high-side hardware pin number
* @param pinB_l phase B low-side hardware pin number
* @param pinC_h phase C high-side hardware pin number
* @param pinC_l phase C low-side hardware pin number
*
*/
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, float dead_zone, int pinA_h, int pinA_l, int pinB_h, int pinB_l, int pinC_h, int pinC_l);
// BLDCMotor( int pp) 电机极对数设置
// - pp - pole pair number
BLDCMotor::BLDCMotor(int pp)
: FOCMotor()
{
// save pole pairs number
pole_pairs = pp;
}
// 初始化硬件接口
void BLDCMotor::init() {
if(monitor_port) monitor_port->println("MOT: Initialise variables.");
// 最大电压限制
if(voltage_limit > driver->voltage_limit) voltage_limit = driver->voltage_limit;
// 限制传感器电压
if(voltage_sensor_align > voltage_limit) voltage_sensor_align = voltage_limit;
// 更新限制数据
PID_velocity.limit = voltage_limit;
P_angle.limit = velocity_limit;
_delay(500);
// 使能电机
if(monitor_port) monitor_port->println("MOT: Enable driver.");
enable();
_delay(500);
}
// 禁用电机
void BLDCMotor::disable()
{
// set zero to PWM
driver->setPwm(0, 0, 0);
// disable the driver
driver->disable();
}
// 使能电机
void BLDCMotor::enable()
{
// enable the driver
driver->enable();
// set zero to PWM
driver->setPwm(0, 0, 0);
}
/*
FOC 功能库
*/
//================ 初始化(电机与传感器对齐)====================//
int BLDCMotor::initFOC( float zero_electric_offset, Direction sensor_direction ) {
int exit_flag = 1;
// 校准电机
if(zero_electric_offset != NOT_SET){
// 提供绝对零位-不需要对齐
zero_electric_angle = zero_electric_offset;
// set the sensor direction - default CW 设置编码器方向,默认CW
sensor->natural_direction = sensor_direction;
}else{
// 校准传感器和电机
_delay(500);
exit_flag = alignSensor();//校准编码器
_delay(500);
}
if(monitor_port) monitor_port->println("MOT: Motor ready.");
return exit_flag;
}
//===== 初始化电动机和传感器角度对齐 子函数======//
int BLDCMotor::alignSensor() {
if(monitor_port) monitor_port->println("MOT: Align sensor.");
// 调整电机和编码器的电气相位
// 设置角度-90度
//从初始角转180度
float start_angle = shaftAngle(); //当前角度为初始角
for (int i = 0; i <=5; i++ ) {
float angle = _3PI_2 + _2PI * i / 6.0; //转30度,循环6次
setPhaseVoltage(voltage_sensor_align, 0, angle); //目标角度设置输出svpwm,转到3路pwm
_delay(200);
}
//从中间角转回0度
float mid_angle = shaftAngle(); //当前角度为中角
for (int i = 5; i >=0; i-- ) {
float angle = _3PI_2 + _2PI * i / 6.0; //转-30度,循环6次
setPhaseVoltage(voltage_sensor_align, 0, angle); //目标角度设置输出svpwm,转到3路pwm
_delay(200);
}
//判断起始角和中间角大小,并输出信息(监视功能开启)
if (mid_angle < start_angle) {
if(monitor_port) monitor_port->println("MOT: natural_direction==CCW");
sensor->natural_direction = Direction::CCW;
} else if (mid_angle == start_angle) {
if(monitor_port) monitor_port->println("MOT: Sensor failed to notice movement");
} else{
if(monitor_port) monitor_port->println("MOT: natural_direction==CW");
}
//令电机稳定两秒
_delay(2000);
//归零传感器数据
sensor->initRelativeZero();
_delay(500);
setPhaseVoltage(0, 0, 0);
_delay(200);
//找到零点标志位并输出反馈(如果开启了监视功能的话)
int exit_flag = absoluteZeroAlign();
_delay(500);
if(monitor_port){
if(exit_flag< 0 ) monitor_port->println("MOT: Error: Not found!");
if(exit_flag> 0 ) monitor_port->println("MOT: Success!");
else monitor_port->println("MOT: Not available!");
}
return exit_flag;
}
//=================绝对角度对齐 子函数==================//
// 输出信息
int BLDCMotor::absoluteZeroAlign() {
if(monitor_port) monitor_port->println("MOT: Absolute zero align.");
// 如果没有绝对零角返回,则返回0
if(!sensor->hasAbsoluteZero()) return 0;
if(monitor_port && sensor->needsAbsoluteZeroSearch()) monitor_port->println("MOT: Searching...");
// 用较小的速度搜索绝对零点
while(sensor->needsAbsoluteZeroSearch() && shaft_angle < _2PI){
loopFOC();
voltage_q = PID_velocity(velocity_index_search - shaftVelocity());
}
voltage_q = 0;
// 停止电机
setPhaseVoltage(0, 0, 0);
// 如果找到绝对零点,则对齐校准
if(!sensor->needsAbsoluteZeroSearch()){
// align the sensor with the absolute zero
float zero_offset = sensor->initAbsoluteZero();
// remember zero electric angle 记住零电角度
zero_electric_angle = _normalizeAngle(_electricalAngle(zero_offset, pole_pairs));
}
// 如果找到零点返回布尔体
return !sensor->needsAbsoluteZeroSearch() ? 1 : -1;
}
//==============FOC算法循环迭代函数==================//
//预先设定好Uq
//这个函数跑得频率越高越好
void BLDCMotor::loopFOC() {
// 获取当前角度
shaft_angle = shaftAngle();
// set the phase voltage - FOC heart function :)
//设置三相电压,FOC算法的核心部分
//(vq,vd,当前角度)
setPhaseVoltage(voltage_q,voltage_d,_electricalAngle(shaft_angle,pole_pairs));
}
//========================FOC外环控制函数========================//
// 迭代函数运行在FOC算法的外环上
// 函数的动作由motor.controller变量决定
// 运行角度环,速度环和电压环(注意这里不是用的电流环,具体看另一篇笔记解释,这也是为什么称为simple的原因之一)
//如果没有设置目标,则使用motor.target的值
void BLDCMotor::move(float new_target) {
if( new_target != NOT_SET )
target = new_target; // 设置内部目标变量
shaft_velocity = shaftVelocity(); // 从传感器获取轴角速度,并进行一阶低通滤波(噪音大)
// 选择控制类型
switch (controller) {
case ControlType::voltage: //电压环模式(直接给q)
voltage_q = target;
break;
case ControlType::angle: //角度环模式
// angle set point
// include angle loop
shaft_angle_sp = target;
shaft_velocity_sp = P_angle( shaft_angle_sp - shaft_angle );
voltage_q = PID_velocity(shaft_velocity_sp - shaft_velocity);
break;
case ControlType::velocity: //速度环模式
// velocity set point
// include velocity loop
shaft_velocity_sp = target;
voltage_q = PID_velocity(shaft_velocity_sp - shaft_velocity);
break;
case ControlType::velocity_openloop: //速度环开环
// velocity control in open loop
// loopFOC should not be called
shaft_velocity_sp = target;
velocityOpenloop(shaft_velocity_sp); //ud=0
break;
case ControlType::angle_openloop: //位置环开环
// angle control in open loop
// loopFOC should not be called
shaft_angle_sp = target;
angleOpenloop(shaft_angle_sp); //ud=0
break;
}
}
//=======电机速度开环控制子函数======//
// - 目标速度 - rad/s
// 使用电压限制变量
void BLDCMotor::velocityOpenloop(float target_velocity){
// get current timestamp 获取当前时间戳
unsigned long now_us = _micros();
// calculate the sample time from last call 计算上一次调用的时间
float Ts = (now_us - open_loop_timestamp) * 1e-6;
// calculate the necessary angle to achieve target velocity
// 计算达到目标速度所需的角度
shaft_angle = _normalizeAngle(shaft_angle + target_velocity*Ts);
// set the maximal allowed voltage (voltage_limit) with the necessary angle
// 设置达到所需角度最大允许电压(voltage_limit)
setPhaseVoltage(voltage_limit, 0, _electricalAngle(shaft_angle, pole_pairs));
// 保存下次调用的时间戳
open_loop_timestamp = now_us;
}
//=======电机位置开环控制子函数======//
// - 目标角度 - rad
// 使用电压限制和速度限制变量
void BLDCMotor::angleOpenloop(float target_angle){
// 获取当前时间戳
unsigned long now_us = _micros();
// 计算上次调用的时间
float Ts = (now_us - open_loop_timestamp) * 1e-6;
// calculate the necessary angle to move from current position towards target angle
// 计算从当前位置向目标角度移动所需的步进角度
// 默认最大速度 (velocity_limit)
if(abs( target_angle - shaft_angle ) > abs(velocity_limit*Ts))
shaft_angle += _sign(target_angle - shaft_angle) * abs( velocity_limit )*Ts;
else
shaft_angle = target_angle;
// set the maximal allowed voltage (voltage_limit) with the necessary angle
// 设置达到所需角度的最大允许电压(voltage_limit)
setPhaseVoltage(voltage_limit, 0, _electricalAngle(shaft_angle, pole_pairs));
// 保存下次调用的时间戳
open_loop_timestamp = now_us;
}
//================SVPWM和SinePWM算法实现======================//
// 利用FOC将Uq和Ud设置到电机最佳角度的方法
// 实现空间矢量PWM和正弦PWM算法的函数
//
//正弦近似函数
//常规sin+cos~300us(无内存使用)
//近似 \u sin + \u cos ~ 110us(400字节~20%内存)
void BLDCMotor::setPhaseVoltage(float Uq, float Ud, float angle_el) {
const bool centered = true; //只读
int sector;
float _ca,_sa;
switch (foc_modulation)//选择使用模式
{
//======= 正弦脉宽调制:逆Park变换 + Clarke变换
case FOCModulationType::SinePWM :
// 角度标准在0-2π之间,只有在使用_sin和_cos的近似函数时才需要
angle_el = _normalizeAngle(angle_el + zero_electric_angle); //限制角度0-2π
_ca = _cos(angle_el);
_sa = _sin(angle_el);
// 逆Park变换
Ualpha = _ca * Ud - _sa * Uq; // -sin(angle) * Uq; α轴电压
Ubeta = _sa * Ud + _ca * Uq; // cos(angle) * Uq; β轴电压
// Clarke 变换
Ua = Ualpha + driver->voltage_limit/2;
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta + driver->voltage_limit/2;
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta + driver->voltage_limit/2;
//这里有点不懂,只保持最多两相有电压输出
if (!centered) {
float Umin = min(Ua, min(Ub, Uc));
Ua -= Umin;
Ub -= Umin;
Uc -= Umin;
}
break;
//========= SVPWM算法
case FOCModulationType::SpaceVectorPWM :
// 如果uq为负,则反相
// 角度 + 180度
if(Uq < 0) angle_el += _PI;
Uq = abs(Uq);
// 角度标准在0-2π之间,只有在使用_sin和_cos的近似函数时才需要
angle_el = _normalizeAngle(angle_el + zero_electric_angle + _PI_2);
// 找到目前所在的扇区
sector = floor(angle_el / _PI_3) + 1;
// 计算占空比
float T1 = _SQRT3*_sin(sector*_PI_3 - angle_el) * Uq/driver->voltage_limit;
float T2 = _SQRT3*_sin(angle_el - (sector-1.0)*_PI_3) * Uq/driver->voltage_limit;
// 可能有两种版本
float T0 = 0; // pulled to 0 - better for low power supply voltage
// 拉到0 - 对于低电压电源更好
if (centered) {
T0 = 1 - T1 - T2; //以 driver->voltage_limit/2 为中心
}
// 计算三相占空比(times)
float Ta,Tb,Tc;
switch(sector){ //判断在第几扇区
case 1:
Ta = T1 + T2 + T0/2;
Tb = T2 + T0/2;
Tc = T0/2;
break;
case 2:
Ta = T1 + T0/2;
Tb = T1 + T2 + T0/2;
Tc = T0/2;
break;
case 3:
Ta = T0/2;
Tb = T1 + T2 + T0/2;
Tc = T2 + T0/2;
break;
case 4:
Ta = T0/2;
Tb = T1+ T0/2;
Tc = T1 + T2 + T0/2;
break;
case 5:
Ta = T2 + T0/2;
Tb = T0/2;
Tc = T1 + T2 + T0/2;
break;
case 6:
Ta = T1 + T2 + T0/2;
Tb = T0/2;
Tc = T1 + T0/2;
break;
default:
// 可能错误的状态
Ta = 0;
Tb = 0;
Tc = 0;
}
// calculate the phase voltages and center
// 计算三相电压
Ua = Ta*driver->voltage_limit;
Ub = Tb*driver->voltage_limit;
Uc = Tc*driver->voltage_limit;
break;
}
//设定驱动器中的电压(输出pwm)
driver->setPwm(Ua, Ub, Uc);
}