FOC算法实现过程记录:12.重构:有感FOC驱动完整实现
小于 1 分钟
12.重构
实现效果
具体实现
12.refactor_all.ino
#include <Arduino.h>
#include <Wire.h>
#include "BLDCMotor.hpp"
#include "BLDCDriver.hpp"
#include "Sensor.hpp"
#include "foc_utils.h"
#include "arduino/ArduinoBLDCDriver.hpp"
#include "arduino/ArduinoCurrentSensor.hpp"
#include "arduino/ArduinoCommand.hpp"
#include "arduino/AS5600Sensor.hpp"
#include "Command.hpp"
#define Ua 5
#define Ub 9
#define Uc 6
#define En 8
#define Ia A0
#define Ib A2
#define I_R 0.01f // 10mΩ
#define I_A 50 // 50倍
BLDCMotor motor = BLDCMotor(7, 12);
Sensor *sensor = new AS5600Sensor();
BLDCDriver *driver = new ArduinoBLDCDriver(Ua, Ub, Uc, En);
CurrentSensor *currentSensor = new ArduinoCurrentSensor(Ia, Ib, I_R, I_A);
ArduinoCommand command;
void setup()
{
Serial.begin(115200);
motor.connectDriver(driver);
motor.connectSensor(sensor);
motor.connectCurrentSensor(currentSensor);
command.connectMotor(&motor);
command.connectSerial(&Serial);
motor.connectCommand(&command);
motor.initFOC();
motor.setMode(BLDCControlMode::Current);
}
void loop()
{
motor.loopFOC();
command.update();
}
BLDCMotor.hpp
#ifndef __BLDCMotor_H__
#define __BLDCMotor_H__
#include <stdint.h>
#include "foc_utils.h"
#include "BLDCDriver.hpp"
#include "Sensor.hpp"
#include "CurrentSensor.hpp"
#include "Command.hpp"
#include "PIDControler.hpp"
#include "LowPassFilter.hpp"
enum BLDCControlMode : uint8_t
{
Unknow = 0,
Voltage = 1,
Current = 2,
VelocityCurrent = 3,
PositionCurrent = 4,
PositionVelocityCurrent = 5,
};
enum MotorDirectrion : int8_t
{
UNKNOW = 0,
ANTI_CLOCK_WISE = 1,
CLOCK_WISE = -1,
};
class BLDCMotor
{
public:
// ####################################################################################
uint8_t pole_pairs; // 极对数
float power_supply_voltage = 12.0f; // 供电电压
// ####################################################################################
float limit_voltage = 12.0f; // 限制电压
float limit_current = 10.0f; // 限制电流
float limit_velocity = 130.0f; // 限制速度
// ####################################################################################
float target = 0; // taget
BLDCControlMode controlMode = BLDCControlMode::VelocityCurrent; // mode
MotorDirectrion direction = MotorDirectrion::ANTI_CLOCK_WISE; // directron
// ####################################################################################
LowPassFilter current_q_filter{10};
LowPassFilter current_d_filter{10};
LowPassFilter shaft_velocity_filter{50};
LowPassFilter shaft_angle_filter{100};
// ####################################################################################
PIDControler pid_iq_controller;
PIDControler pid_id_controller;
PIDControler pid_velocity_controller;
PIDControler pid_position_controller;
// ####################################################################################
BLDCDriver *driver = nullptr;
Sensor *sensor = nullptr;
CurrentSensor *currentSensor = nullptr;
Command *command = nullptr;
public:
// ####################################################################################
BLDCMotor(uint8_t pole_pairs, float power_supply_voltage);
// ####################################################################################
void connectDriver(BLDCDriver *driver);
void connectSensor(Sensor *sensor);
void connectCurrentSensor(CurrentSensor *currentSensor);
void connectCommand(Command *command);
// ####################################################################################
void initFOC();
void loopFOC();
// ####################################################################################
/**
* 设置控制模式
*/
void setMode(BLDCControlMode mode);
/**
* 设置目标值(电压/力矩/速度/位置)
*/
void setTarget(float target);
/**
* 设置旋转方向
*/
void setDirection(MotorDirectrion direction);
// ####################################################################################
/**
* 获取电角度
*/
float electricalAngle();
/**
* 设置相电压
* @param u_d float [-1,1]
* @param u_q float [-1,1]
* @param e_angle float [0,2PI]
*/
void setPhraseVoltage(float u_d, float u_q, float e_angle);
/**
* 开环电压控制
*/
void open_loop_voltage_control(float target_ud, float target_uq);
// ####################################################################################
/**
* 获取dq轴电流
*/
CurrentDQ getCurrentDQ();
/**
* 闭环电流控制
*/
void close_loop_current_control(float target);
// ####################################################################################
/**
* 获取速度
*/
float shaftVelocity();
/**
* 闭环速度控制
*/
void close_loop_velocity_current_control(float target);
// ####################################################################################
/**
* 获取位置
*/
float shaftAngle();
/**
* 闭环位置控制: 位置-力矩-闭环控制
*/
void close_loop_position_current_control(float target);
/**
* 闭环位置控制: 位置-速度-力矩-闭环控制
*/
void close_loop_position_velocity_current_control(float target);
// ####################################################################################
};
#endif
BLDCMotor.cpp
#include "BLDCMotor.hpp"
#include "Sensor.hpp"
#include "CurrentSensor.hpp"
#include "Command.hpp"
BLDCMotor::BLDCMotor(uint8_t pole_pairs, float power_supply_voltage)
{
this->pole_pairs = pole_pairs;
this->power_supply_voltage = power_supply_voltage;
}
void BLDCMotor::connectDriver(BLDCDriver *driver)
{
this->driver = driver;
}
void BLDCMotor::connectSensor(Sensor *sensor)
{
this->sensor = sensor;
}
void BLDCMotor::connectCurrentSensor(CurrentSensor *currentSensor)
{
this->currentSensor = currentSensor;
}
void BLDCMotor::connectCommand(Command *command)
{
this->command = command;
}
void BLDCMotor::initFOC()
{
if (this->currentSensor)
{
this->currentSensor->initSensor();
this->currentSensor->alignSensor();
}
if (this->driver)
{
this->driver->initDriver();
this->driver->enableDriver(true);
this->driver->setPhraseVoltage(0, 0, 0);
delay(10);
}
if (this->sensor)
{
this->sensor->initSensor();
this->sensor->connectMotor(this);
this->sensor->align();
this->sensor->update();
}
}
void BLDCMotor::loopFOC()
{
if (this->sensor)
this->sensor->update();
if (this->currentSensor)
this->currentSensor->update();
switch (controlMode)
{
case BLDCControlMode::Voltage:
this->open_loop_voltage_control(0, target);
break;
case BLDCControlMode::Current:
this->close_loop_current_control(target);
break;
case BLDCControlMode::VelocityCurrent:
this->close_loop_velocity_current_control(target);
break;
case BLDCControlMode::PositionCurrent:
this->close_loop_position_current_control(target);
break;
case BLDCControlMode::PositionVelocityCurrent:
this->close_loop_position_velocity_current_control(target);
break;
default:
break;
}
}
/**
* 设置控制模式
*/
void BLDCMotor::setMode(BLDCControlMode controlMode)
{
switch (controlMode)
{
case BLDCControlMode::Unknow:
break;
case BLDCControlMode::Voltage:
break;
case BLDCControlMode::Current:
// 官方 Simple FOC Shield v2.0.4 开发板 with 2804-100kv 云台电机
this->limit_voltage = 12.0; // 电机额定电压
this->limit_current = 1; // 额定电流
// kp=35 时目标电流1 实际电流0.9 精确度90%
// roc=600电机在快速正反转时抖动失控,所以取500
this->pid_id_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_iq_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
break;
case BLDCControlMode::VelocityCurrent:
// 官方 Simple FOC Shield v2.0.4 开发板 with 2804-100kv 云台电机
this->limit_voltage = 12.0; // 电机额定电压
this->limit_current = 1; // 额定电流
this->limit_velocity = 100; // 最大速度
this->pid_id_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_iq_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
// kp=0.05 时 目标速度100 实际速度90 精确度90%
// ki=0.5 时 电机目标速度从100降到0时,电机有两次过冲抖动,ki=0.25 时 电机目标速度从100降到0时,电机有一次过冲抖动,遂取ki=0.1
this->pid_velocity_controller = PIDControler(0.05, 0.1, 0, this->limit_current, 0);
break;
case BLDCControlMode::PositionCurrent: // 力位控制
// 官方 Simple FOC Shield v2.0.4 开发板 with 2804-100kv 云台电机
this->limit_voltage = 12.0; // 电机额定电压
this->limit_current = 1; // 额定电流
this->pid_id_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_iq_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_position_controller = PIDControler(0.5, 0, 0.025, this->limit_current, 0);
break;
case BLDCControlMode::PositionVelocityCurrent: // 力矩-速度-位置控制
this->limit_voltage = 12.0;
this->limit_current = 1;
this->limit_velocity = 100; // 最大速度
this->pid_id_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_iq_controller = PIDControler(35, 0, 0, this->limit_voltage, 500);
this->pid_velocity_controller = PIDControler(0.05, 0, 0, this->limit_current, 0);
this->pid_position_controller = PIDControler(10, 0, 0.1, this->limit_velocity, 0);
break;
default:
break;
}
this->controlMode = controlMode;
}
void BLDCMotor::setTarget(float target)
{
this->target = target;
}
void BLDCMotor::setDirection(MotorDirectrion dir)
{
this->direction = dir;
}
/**
* @details
* 电角度 = 机械角度 * 极对数
* @return [0,2PI]
*/
float BLDCMotor::electricalAngle()
{
return _normalizeAngle(this->sensor->getPositon() * pole_pairs);
}
/**
* 设置相电压
* @param u_d float [-1,1]
* @param u_q float [-1,1]
* @param e_angle float [0,2PI]
*/
void BLDCMotor::setPhraseVoltage(float u_d, float u_q, float e_angle)
{
float sin, cos;
// 计算三角函数
_sincos(e_angle, &sin, &cos);
// 帕克逆变换
float u_alpha = cos * u_d + -sin * u_q;
float u_beta = sin * u_d + cos * u_q;
// 克拉克逆变换(等幅值形式)
float u_a = u_alpha;
float u_b = (-1 * u_alpha + M_SQRT3 * u_beta) / 2;
float u_c = -(u_a + u_b);
// 设置相电压
driver->setPhraseVoltage(u_a, u_b, u_c);
}
void BLDCMotor::open_loop_voltage_control(float target_ud, float target_uq)
{
float e_angle = this->electricalAngle();
float voltage_ud = _constrain(-limit_voltage, target_ud, limit_voltage);
float voltage_uq = _constrain(-limit_voltage, target_uq, limit_voltage);
float u_d = _constrain(-1, voltage_ud / power_supply_voltage, 1);
float u_q = _constrain(-1, voltage_uq / power_supply_voltage, 1);
this->setPhraseVoltage(u_d, u_q, e_angle);
}
/**
* 获取Q轴电流
*/
CurrentDQ BLDCMotor::getCurrentDQ()
{
CurrentDQ i = this->currentSensor->getCurrentDQ(this->electricalAngle());
return {
.d = this->current_d_filter(i.d), // TODO: 测试新旧滤波器差异
.q = this->current_q_filter(i.q),
};
}
/**
* 电流闭环控制
*/
void BLDCMotor::close_loop_current_control(float target)
{
float target_i_d = 0;
float target_i_q = _constrain(-limit_current, target, limit_current);
CurrentDQ current = this->getCurrentDQ();
float error_d = target_i_d - current.d;
float error_q = target_i_q - current.q;
float u_d = this->pid_id_controller(error_d);
float u_q = this->pid_iq_controller(error_q);
this->open_loop_voltage_control(u_d, u_q);
// ######################################################################
static uint8_t idx = 0;
if (this->command && ++idx % 61 == 0)
{
idx = 0;
this->command->drawDragram(1, target_i_q, current.q);
}
}
/**
* 获取机械角度
*/
float BLDCMotor::shaftVelocity()
{
return shaft_velocity_filter(this->sensor->getVelocity());
}
void BLDCMotor::close_loop_velocity_current_control(float target)
{
float target_velocity = _constrain(-limit_velocity, target, limit_velocity);
float current_velocity = this->shaftVelocity();
float error = target_velocity - current_velocity;
float i_q = this->direction * this->pid_velocity_controller(error);
this->close_loop_current_control(i_q);
// ######################################################################
static uint8_t idx = 0;
if (this->command && ++idx % 62 == 0)
{
idx = 0;
this->command->drawDragram(2, target_velocity, current_velocity);
}
}
/**
* 获取机械角度
*/
float BLDCMotor::shaftAngle()
{
return shaft_angle_filter(this->sensor->getPositons());
}
void BLDCMotor::close_loop_position_current_control(float target)
{
float target_position = target;
float current_position = this->shaftAngle();
float error = target_position - current_position;
float i_q = this->direction * this->pid_position_controller(error);
this->close_loop_current_control(i_q);
// ######################################################################
static uint8_t idx = 0;
if (this->command && ++idx % 63 == 0)
{
idx = 0;
this->command->drawDragram(3, target_position, current_position);
}
}
void BLDCMotor::close_loop_position_velocity_current_control(float target)
{
float target_position = target;
float current_position = this->shaftAngle();
float error = target_position - current_position;
float velocity = this->direction * this->pid_position_controller(error);
this->close_loop_velocity_current_control(velocity);
// ######################################################################
static uint8_t idx = 0;
if (this->command && ++idx % 64 == 0)
{
idx = 0;
this->command->drawDragram(4, target_position, current_position);
}
}
Sensor.hpp
#ifndef _Sensor_H_
#define _Sensor_H_
#include <stdint.h>
#include "Timer.hpp"
#include "config.h"
class BLDCMotor;
class Sensor
{
friend class BLDCMotor;
public:
/**
* @return void
*/
virtual void initSensor() = 0;
/**
* @return [0,2PI]
*/
virtual float readSensor() = 0;
void connectMotor(BLDCMotor *motor);
void alignSensor();
void alignDirectron();
void align();
void update();
float getPositon();
float getPositons();
float getVelocity();
enum SensorDirectrion : int8_t
{
UNKNOW = 0,
CLOCK_WISE = 1,
ANTI_CLOCK_WISE = -1,
};
private:
BLDCMotor *motor = nullptr;
Timer *timer = new ConcreteTimer();
SensorDirectrion directron = SensorDirectrion::UNKNOW;
float offset = 0;
float position_pre, position_now = 0, d_position;
float rotations_pre, rotation_now = 0, d_rotation;
float positions_pre, positions_now = 0, d_positions;
float velocity = 0;
};
#endif
arduino/AS5600Sensor.hpp
#ifndef __AS5600Sensor_H__
#define __AS5600Sensor_H__
#include "../Sensor.hpp"
#include "../foc_utils.h"
#include "Arduino.h"
#define AS5600_ADDR 0x36
#define AS5600_RAW_ANGLE 0x0c
#define AS5600_ANGLE 0x0e
class AS5600Sensor : public Sensor
{
public:
void initSensor() override
{
// AS5600 最高支持1Mhz 1000000
Wire.setClock(1000000); // 100000 (standard mode) 400000 (fast mode) 1000000 (fast mode plus)
Wire.begin();
}
float readSensor() override
{
Wire.beginTransmission(AS5600_ADDR);
Wire.write(AS5600_ANGLE);
uint8_t error = Wire.endTransmission(false);
if (!!error)
return 0;
Wire.requestFrom(AS5600_ADDR, 2);
uint16_t data = 0;
*((uint8_t *)&data + 1) = Wire.read();
*((uint8_t *)&data + 0) = Wire.read();
error = Wire.endTransmission(true);
if (!!error)
return 0;
delay(1);
// as5600 12bit精度,左移4位变成16位
data <<= 4; // [0,0x0fff] -> [0,0xffff]
return data / (float)UINT16_MAX * M_TWOPI; // [0,0xffff] -> [0,2PI]
}
};
#endif
CurrentSensor.hpp
#ifndef __CurrentSensor_H__
#define __CurrentSensor_H__
#include <stdint.h>
#include "foc_utils.h"
#include <math.h>
#include "BLDCMotor.hpp"
#define M_SQRT3 1.73205080756887719000
// CurrentABC
typedef struct
{
float a;
float b;
float c;
} CurrentABC;
// CurrentAB
typedef struct
{
float alpha;
float beta;
} CurrentAB;
// CurrentDQ
typedef struct
{
float d;
float q;
} CurrentDQ;
// CurrentDC
typedef float CurrentDC;
class CurrentSensor
{
public:
virtual void initSensor();
virtual CurrentABC readSensor();
friend class BLDCMotor;
private:
public:
CurrentABC offset = {0, 0, 0};
CurrentABC cache = {0, 0, 0};
void alignSensor()
{
uint16_t times = 1000;
CurrentABC average = {0, 0, 0};
for (uint16_t n = 1; n <= times; n++)
{
CurrentABC current = this->readSensor();
average.a += current.a;
average.b += current.b;
average.c += current.c;
delay(2);
}
average.a /= times;
average.b /= times;
average.c /= times;
this->offset = average;
}
public:
void update()
{
cache = this->readSensor();
}
CurrentABC getCurrentABC()
{
return {
.a = cache.a - offset.a,
.b = cache.b - offset.b,
.c = cache.c - offset.c,
};
}
CurrentAB getCurrentAB()
{
CurrentABC i = this->getCurrentABC();
// 克拉克变换,等赋值形式
return {
.alpha = i.a, // i_alpha = 1 * i_a + 0 * i_b
.beta = (i.a + 2 * i.b) / M_SQRT3, // i_beta = 1/sqrt(3) * i_a + 2 / sqrt(3) * i_b = (i_a + 2 * i_b)/sqrt(3)
};
}
CurrentDC getCurrentDC()
{
CurrentAB i = this->getCurrentAB();
return sqrtf(i.alpha * i.alpha + i.beta * i.beta); // 向量模长
}
/**
* @param e_theta [0,2PI]
*/
CurrentDQ getCurrentDQ(float e_theta)
{
float sin, cos;
_sincos(e_theta, &sin, &cos);
CurrentAB i = this->getCurrentAB();
return {
.d = cos * i.alpha + sin * i.beta,
.q = -sin * i.alpha + cos * i.beta,
};
}
};
#endif
arduino/CurrentSensor.hpp
#ifndef __ArduinoCurrentSensor_H__
#define __ArduinoCurrentSensor_H__
#include "../CurrentSensor.hpp"
#include "Arduino.h"
class ArduinoCurrentSensor : public CurrentSensor
{
int Ia, Ib;
float R; // 检流电阻
uint8_t A; // 放大倍数
public:
ArduinoCurrentSensor(int Ia, int Ib, float R, uint8_t A)
{
this->Ia = Ia;
this->Ib = Ib;
this->R = R;
this->A = A;
}
void initSensor() override
{
pinMode(this->Ia, INPUT);
pinMode(this->Ib, INPUT);
// Arduino 板上的标准分辨率为 10 位 (0-1023)
analogReadResolution(14); // UNO R4 支持高达 14 位(0-16383)的分辨率
// 默认参考电压 5 V
analogReference(AR_DEFAULT);
// analogReference(AR_INTERNAL); // 内置参考电压 1.5 V
}
CurrentABC readSensor() override
{
// [0,16383] => [0,5] => [-2.5,2.5]
float i_a = (analogRead(this->Ia) / 16383.0f * 5.0f - 2.5f) / this->R / this->A;
float i_b = -(analogRead(this->Ib) / 16383.0f * 5.0f - 2.5f) / this->R / this->A; // b电路接反了 加符号
float i_c = -(i_a + i_b);
return {i_a, i_b, i_c};
}
};
#endif