电角度与机械角度
float zero_mechanical_angle = 0.36;
float zero_electrical_angle = 0.891460538;
float last_mechanical_angle = 0;
uint32_t pre_tick;
机械角:机械角就是空间几何角度,用圆规能够测量到的。因为电机是圆形的,所以它的范围是0-360度。
电角度:从定子来看,定子电流变化一个完整的周期定义为电角度的0-360度,称为一个电周期。一个电周期可以在空间360度完成也可以在空间180度或者90度或者60度完成,这和电动机(发电机也是这样)绕组布置成几对磁级有关。
从转子来看, 转子上分布有N对磁钢,电角度以一对磁钢为基准,形成电磁场的绕组经过一对磁钢的N极、S极、再回到另一对磁钢的N极时,完成从N、S、再到 N极一对磁钢的跨越,这就是电角度的一周。从N极只到 S极,则转子只运行了180°。一台电动机的转子转动一圈有多少电角度,视磁钢对数而定,只有一对磁钢的,电角度和自然角度一致,有两对磁钢的,为2X360°,5对磁钢的,为5X360°,依此类推。
电角度=机械角度×极对数
FOC的电角度校准
void FOC_electrical_angle_calibration() {
// FOC calibration
FOC_SVPWM(0, 8, 0);//为什么8?
HAL_Delay(400);
zero_electrical_angle = FOC_electrical_angle();
HAL_Delay(100);
FOC_SVPWM(0, 0, 0);
}
FOC_SVPWM
去掉了电流采样,所以简单了很多,有位置传感器,所以不用观测器估算位置。对空间矢量作用时间可以直接利用下面的公式
Udc表示电源电压(在代码中是voltage_limit),Uref表示设置的力矩大小(在代码中是target_voltage),Ts表示PWM周期(代码中没有把Ts体现出来,代码中的T1、T2是周期的百分比)。
void FOC_SVPWM(float Uq, float Ud, float angle) {
int sector;
// Nice video explaining the SpaceVectorModulation (FOC_SVPWM) algorithm
// https://www.youtube.com/watch?v=QMSWUMEAejg
// the algorithm goes
// 1) Ualpha, Ubeta
// 2) Uout = sqrt(Ualpha^2 + Ubeta^2)
// 3) angle_el = atan2(Ubeta, Ualpha)
//
// equivalent to 2) because the magnitude does not change is:
// Uout = sqrt(Ud^2 + Uq^2)
// equivalent to 3) is
// angle_el = angle_el + atan2(Uq,Ud)
float Uout = _sqrt(Ud * Ud + Uq * Uq) / VOLTAGE_LIMIT; // Actually, Uout is a ratio
angle = _normalizeAngle(angle + atan2(Uq, Ud));
// find the sector we are in currently
sector = floor(angle / _PI_3) + 1;
// calculate the duty cycles
float T1 = _SQRT3 * _sin(sector * _PI_3 - angle) * Uout;
float T2 = _SQRT3 * _sin(angle - (sector - 1.0f) * _PI_3) * Uout;
// two versions possible
// float T0 = 0; // pulled to 0 - better for low power supply voltage
float T0 = 1 - T1 - T2; // modulation_centered around driver->voltage_limit/2
// calculate the duty cycles(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:
// possible error state
Ta = 0;
Tb = 0;
Tc = 0;
}
// Ta, Tb, Tc range [0,1]
_writeDutyCycle3PWM(Ta, Tb, Tc);//设置三个通道的占空比
}
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c) {
// transform duty cycle from [0,1] to [0,_PWM_RANGE]
__HAL_TIM_SET_COMPARE(_STM32_TIMER_HANDLE, TIM_CHANNEL_1, _PWM_RANGE*dc_a);
__HAL_TIM_SET_COMPARE(_STM32_TIMER_HANDLE, TIM_CHANNEL_2, _PWM_RANGE*dc_b);
__HAL_TIM_SET_COMPARE(_STM32_TIMER_HANDLE, TIM_CHANNEL_3, _PWM_RANGE*dc_c);
}
Clarke&Park
void FOC_Clarke_Park(float Ia, float Ib, float Ic, float angle, float *Id, float *Iq) {
// Clarke transform
float mid = (1.f / 3) * (Ia + Ib + Ic);
float a = Ia - mid;
float b = Ib - mid;
float i_alpha = a;
float i_beta = _1_SQRT3 * a + _2_SQRT3 * b;
// Park transform
float ct = _cos(angle);
float st = _sin(angle);
*Id = i_alpha * ct + i_beta * st;
*Iq = i_beta * ct - i_alpha * st;
return;
}
电角度与机械角度获取
float FOC_get_mechanical_angle() {
return AS5600_ReadSensorRawData() / SENSOR_VALUE_RANGE * _2PI;
}
float FOC_electrical_angle() {
return _normalizeAngle(SENSOR_DIRECTION * POLE_PAIR * FOC_get_mechanical_angle() - zero_electrical_angle);
}
// rad/s
速度转化
float FOC_get_velocity() {
float ts = (float) (HAL_GetTick() - pre_tick) * 1e-3;
if (ts < 1e-3) ts = 1e-3;
pre_tick = HAL_GetTick();
float now_mechanical_angle = FOC_get_mechanical_angle();
float delta_angle = now_mechanical_angle - last_mechanical_angle;
last_mechanical_angle = now_mechanical_angle;
if (fabs(delta_angle) > _PI) {
if (delta_angle > 0) {
delta_angle -= _2PI;
} else {
delta_angle += _2PI;
}
}
// return delta_angle;
return delta_angle / ts;
}
开环-电压控制环
void FOC_open_loop_voltage_control_loop(float Uq) {
float electrical_angle = FOC_electrical_angle();
// Current sense
float Id, Iq;
cs_get_value();
FOC_Clarke_Park(cs_value[0], cs_value[1], cs_value[2], electrical_angle, &Id, &Iq);
Id = FOC_low_pass_filter(&lpf_current_d, Id);//低通滤波
Iq = FOC_low_pass_filter(&lpf_current_q, Iq);
FOC_SVPWM(Uq, 0, electrical_angle);
// debug
printf("%.1f,%.1f,%.1f,%.1f,%.1f\n", cs_value[0], cs_value[1], cs_value[2], Id, Iq);
}
电流采样
uint8_t cs_get_value() {
for (int i = 0; i < 3; ++i) {
// Enables ADC, starts conversion of regular group.
HAL_ADC_Start(&hadc1);
// Wait for regular group conversion to be completed.
HAL_ADC_PollForConversion(&hadc1, 100);
// 判断ADC是否转换成功
// if (HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC)) {
// Get ADC regular group conversion result.
// Reading register DR automatically clears ADC flag EOC (ADC group regular end of unitary conversion).
// 该函数读取寄存器DR同时自动清除了EOC(End Of unitary Conversation)标志位
// cs_value[i] = HAL_ADC_GetValue(&hadc1) - cs_zero_value;
cs_value[i] = FOC_low_pass_filter(lpf_cs + i, HAL_ADC_GetValue(&hadc1) - cs_zero_value);
// } else {
// HAL_ADC_Stop(&hadc1);
// 如果转换失败就返回-1
// return -1;
// }
}
// Stop ADC conversion of regular group, disable ADC peripheral.
HAL_ADC_Stop(&hadc1);
// cs_value[0] = -cs_value[1] - cs_value[2] + 3 * cs_zero_value;
return 0;
}
电流环控制
void FOC_current_control_loop(float target_Iq){
static float electrical_angle;
electrical_angle = FOC_electrical_angle();
// Current sense
static float Id, Iq;
cs_get_value();
FOC_Clarke_Park(cs_value[0], cs_value[1], cs_value[2], electrical_angle, &Id, &Iq);
Id = FOC_low_pass_filter(&lpf_current_d, Id);
Iq = FOC_low_pass_filter(&lpf_current_q, Iq);
// back feed
static float Uq, Ud;
Uq = pid_get_u(&pid_current_q, target_Iq, Iq);
Ud = pid_get_u(&pid_current_d, 0, Id);
// front feed
Uq += 0.008 * Iq;
FOC_SVPWM(Uq, Ud, electrical_angle);
// debug
// printf("%.1f,%.1f,%.1f,%.1f,%.1f\n", cs_value[0], cs_value[1], cs_value[2], Id, Iq);
}
速度环控制
void FOC_velocity_control_loop(float target_velocity) {
static float now_velocity;
now_velocity = FOC_low_pass_filter(&lpf_velocity, FOC_get_velocity());
float Uq = pid_get_u(&pid_velocity, target_velocity, now_velocity);
float electrical_angle = FOC_electrical_angle();
FOC_SVPWM(Uq, 0, electrical_angle);
// cs_get_value();
// printf("%d,%d,%d\n", cs_value[0], cs_value[1], cs_value[2]);
}
位置环控制
void FOC_position_control_loop(float target_angle) {
target_angle = _normalizeAngle(target_angle);
float now_angle = _normalizeAngle(FOC_get_mechanical_angle() - zero_mechanical_angle);
// float now_angle = FOC_get_mechanical_angle();
float angle_error = target_angle - now_angle;
if (angle_error < -_PI) target_angle += _2PI;
else if (angle_error > _PI) target_angle -= _2PI;
float target_velocity = pid_get_u(&pid_position, target_angle, now_angle);
float now_velocity = FOC_low_pass_filter(&lpf_velocity, FOC_get_velocity());
float Uq = pid_get_u(&pid_velocity, target_velocity, now_velocity);
float electrical_angle = FOC_electrical_angle();
FOC_SVPWM(Uq, 0, electrical_angle);
//printf("%.2f,%.2f,%.2f,%.2f\n", target_angle, now_angle, target_velocity, now_velocity);
}
模拟弹簧
void FOC_spring_loop(float target_angle, PID_Datatype *pid) {
target_angle = _normalizeAngle(target_angle);
float now_angle = _normalizeAngle(FOC_get_mechanical_angle() - zero_mechanical_angle);
float angle_error = target_angle - now_angle;
if (angle_error < -_PI) target_angle += _2PI;
else if (angle_error > _PI) target_angle -= _2PI;
float Uq = FOC_low_pass_filter(&lpf_spring, pid_get_u(pid, target_angle, now_angle));
float electrical_angle = FOC_electrical_angle();
FOC_SVPWM(Uq, 0, electrical_angle);
}
模拟旋钮
void FOC_knob_loop(uint8_t sector_num) {
float now_angle = _normalizeAngle(FOC_get_mechanical_angle() - zero_mechanical_angle);
uint8_t now_sector = (uint8_t) floor(now_angle * sector_num / _2PI);
float target_angle = now_sector * _2PI / sector_num + _PI / sector_num;
float angle_error = target_angle - now_angle;
if (angle_error < -_PI) target_angle += _2PI;
else if (angle_error > _PI) target_angle -= _2PI;
float Uq = pid_get_u(&pid_knob, target_angle, now_angle);
float electrical_angle = FOC_electrical_angle();
FOC_SVPWM(Uq, 0, electrical_angle);
char data = now_sector + '0';
HAL_UART_Transmit(&huart1, &data, 1, 0xff);
}