#include "balance_car_full.h"
#include "math.h"
#include <string.h>
PID_TypeDef PID_Angle = { 0, 0, 0, 0, 0, 100.0f, -100.0f, ANGLE_P, ANGLE_I, ANGLE_D, 0, PWM_MAX, -PWM_MAX };
PID_TypeDef PID_Gyro = { 0, 0, 0, 0, 0, 50.0f, -50.0f, GYRO_P, GYRO_I, GYRO_D, 0, PWM_MAX, -PWM_MAX };
PID_TypeDef PID_Speed = { 0, 0, 0, 0, 0, 200.0f, -200.0f, SPEED_P, SPEED_I, SPEED_D, 0, PWM_MAX, -PWM_MAX };
PID_TypeDef PID_Steer = { 0, 0, 0, 0, 0, 50.0f, -50.0f, STEER_P, STEER_I, STEER_D, 0, 50, -50 };
BalanceCar_StateDef Car_State = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, MODE_STANDBY, FAULT_NONE };
float Q_angle = 0.001f;
float Q_gyro = 0.003f;
float R_angle = 0.03f;
float P[2][2] = {{1,0},{0,1}};
float K[2];
float angle_err, gyro_err;
void Attitude_Solve_Kalman(void) {
int16_t accel_x, accel_y, accel_z;
int16_t gyro_x, gyro_y, gyro_z;
float accel_angle, gyro_rate;
MPU6050_Read_Accel(&accel_x, &accel_y, &accel_z);
MPU6050_Read_Gyro(&gyro_x, &gyro_y, &gyro_z);
accel_angle = atan2((float)accel_y, sqrt(pow((float)accel_x, 2) + pow((float)accel_z, 2))) * 180.0f / M_PI;
gyro_rate = (float)gyro_x / 131.0f;
Car_State.Pitch_Angle += DT * (gyro_rate - gyro_err);
P[0][0] += DT * (DT*P[1][1] - P[0][1] - P[1][0] + Q_angle);
P[0][1] -= DT * P[1][1];
P[1][0] -= DT * P[1][1];
P[1][1] += Q_gyro * DT;
angle_err = accel_angle - Car_State.Pitch_Angle;
K[0] = P[0][0] / (P[0][0] + R_angle);
K[1] = P[1][0] / (P[0][0] + R_angle);
Car_State.Pitch_Angle += K[0] * angle_err;
gyro_err += K[1] * angle_err;
Car_State.Pitch_Gyro = gyro_rate - gyro_err;
P[0][0] -= K[0] * P[0][0];
P[0][1] -= K[0] * P[0][1];
P[1][0] -= K[1] * P[0][0];
P[1][1] -= K[1] * P[0][1];
Car_State.Roll_Angle = atan2((float)accel_x, sqrt(pow((float)accel_y, 2) + pow((float)accel_z, 2))) * 180.0f / M_PI;
}
float PID_Calc(PID_TypeDef *pid, float set, float actual) {
pid->Set = set;
pid->Actual = actual;
pid->Err = pid->Set - pid->Actual;
pid->Integral += pid->Err * DT;
pid->Integral = (pid->Integral > pid->Integral_Max) ? pid->Integral_Max : pid->Integral;
pid->Integral = (pid->Integral < pid->Integral_Min) ? pid->Integral_Min : pid->Integral;
pid->Output = pid->Kp * pid->Err + pid->Ki * pid->Integral + pid->Kd * (pid->Err - pid->Err_Last) / DT;
pid->Output = (pid->Output > pid->Output_Max) ? pid->Output_Max : pid->Output;
pid->Output = (pid->Output < pid->Output_Min) ? pid->Output_Min : pid->Output;
pid->Err_Last = pid->Err;
return pid->Output;
}
void BLDC_Speed_Control(float target_speed1, float target_speed2) {
float pwm1, pwm2;
uint16_t pos1, pos2;
pos1 = AS5048A_Read_Angle(1);
pos2 = AS5048A_Read_Angle(2);
static uint16_t last_pos1 = 0, last_pos2 = 0;
uint16_t delta_pos1 = pos1 - last_pos1;
uint16_t delta_pos2 = pos2 - last_pos2;
last_pos1 = pos1;
last_pos2 = pos2;
Car_State.Motor1_Speed = (delta_pos1 / 4096.0f) * BLDC_POLE_PAIRS * (1/DT) * 60;
Car_State.Motor2_Speed = (delta_pos2 / 4096.0f) * BLDC_POLE_PAIRS * (1/DT) * 60;
pwm1 = PID_Calc(&PID_Speed, target_speed1, Car_State.Motor1_Speed);
pwm2 = PID_Calc(&PID_Speed, target_speed2, Car_State.Motor2_Speed);
BLDC_Driver_Set_PWM(1, (uint16_t)pwm1);
BLDC_Driver_Set_PWM(2, (uint16_t)pwm2);
BLDC_Driver_Commutate(1, pos1);
BLDC_Driver_Commutate(2, pos2);
}
void Balance_Steer_Control(void) {
float angle_pid_out, gyro_pid_out, steer_pid_out;
float target_speed1, target_speed2;
Fault_Diagnosis();
if (Car_State.WorkMode == MODE_FAULT) {
BLDC_Driver_Stop(1);
BLDC_Driver_Stop(2);
return;
}
Attitude_Solve_Kalman();
angle_pid_out = PID_Calc(&PID_Angle, 0.0f + Car_State.Speed_Cmd/20.0f, Car_State.Pitch_Angle);
gyro_pid_out = PID_Calc(&PID_Gyro, angle_pid_out, Car_State.Pitch_Gyro);
steer_pid_out = PID_Calc(&PID_Steer, Car_State.Steer_Cmd, Car_State.Roll_Angle);
target_speed1 = gyro_pid_out + steer_pid_out;
target_speed2 = gyro_pid_out - steer_pid_out;
target_speed1 = (target_speed1 > 3000) ? 3000 : (target_speed1 < -3000) ? -3000 : target_speed1;
target_speed2 = (target_speed2 > 3000) ? 3000 : (target_speed2 < -3000) ? -3000 : target_speed2;
BLDC_Speed_Control(target_speed1, target_speed2);
Car_State.Battery_Voltage = ADC_Read_Battery() * 3.3f / 4096.0f * 4;
Car_State.Motor_Current = ADC_Read_Current() * 3.3f / 4096.0f / 0.1f;
}
void Fault_Diagnosis(void) {
if (fabs(Car_State.Pitch_Angle) > MAX_TILT_ANGLE) {
Car_State.FaultType = FAULT_OVER_TILT;
Car_State.WorkMode = MODE_FAULT;
return;
}
if (Car_State.Battery_Voltage < LOW_VOLTAGE) {
Car_State.FaultType = FAULT_LOW_VOLTAGE;
Car_State.WorkMode = MODE_FAULT;
return;
}
if (Car_State.Motor_Current > OVER_CURRENT) {
Car_State.FaultType = FAULT_OVER_CURRENT;
Car_State.WorkMode = MODE_FAULT;
return;
}
if (abs(BLDC_Driver_Get_PWM(1)) > 500 && Car_State.Motor1_Speed < 100) {
Car_State.FaultType = FAULT_MOTOR_ABNORMAL;
Car_State.WorkMode = MODE_FAULT;
return;
}
Car_State.FaultType = FAULT_NONE;
Car_State.WorkMode = MODE_BALANCE;
}
void Cmd_Parse(uint8_t *buf, uint16_t len) {
if (len < 7) return;
if (buf[0] == 'S') {
int16_t speed = (buf[1]-'0')*100 + (buf[2]-'0')*10 + (buf[3]-'0');
if (buf[1] == '1') speed = -speed;
Car_State.Speed_Cmd = (speed > 100) ? 100 : (speed < -100) ? -100 : speed;
}
if (buf[4] == 'T') {
int16_t steer = (buf[5]-'0')*10 + (buf[6]-'0');
if (buf[5] == '1') steer = -steer;
Car_State.Steer_Cmd = (steer > 50) ? 50 : (steer < -50) ? -50 : steer;
}
memset(buf, 0, len);
}
void State_Report(void) {
char report_buf[64];
sprintf(report_buf, "%.1f,%.1f,%.0f,%.0f,%.1f,%d\r\n", Car_State.Pitch_Angle, Car_State.Pitch_Gyro, Car_State.Motor1_Speed, Car_State.Motor2_Speed, Car_State.Battery_Voltage, Car_State.FaultType);
USART_SendString(USART1, report_buf);
Bluetooth_SendString(report_buf);
}
void BalanceCar_Full_Init(void) {
HAL_Init();
SystemClock_Config();
MPU6050_Init();
AS5048A_Init();
BLDC_Driver_Init();
USART1_Init(BAUDRATE_UART);
Bluetooth_Init(BAUDRATE_BLE);
ADC_Init();
HAL_Delay(1000);
Car_State.WorkMode = MODE_STANDBY;
Car_State.FaultType = FAULT_NONE;
}
#include "bldc_driver.h"
const uint8_t commutation_table[6][3] = {
{1, 0, 0},
{1, 1, 0},
{0, 1, 0},
{0, 1, 1},
{0, 0, 1},
{1, 0, 1}
};
void BLDC_Driver_Set_PWM(uint8_t motor_id, uint16_t pwm) {
if (motor_id == 1) {
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, pwm);
} else if (motor_id == 2) {
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_4, pwm);
}
}
void BLDC_Driver_Commutate(uint8_t motor_id, uint16_t angle) {
uint8_t sector = angle / 682;
GPIO_TypeDef *gpio_port = GPIOC;
if (motor_id == 1) {
HAL_GPIO_WritePin(gpio_port, GPIO_PIN_0, commutation_table[sector][0] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(gpio_port, GPIO_PIN_1, commutation_table[sector][1] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(gpio_port, GPIO_PIN_2, commutation_table[sector][2] ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
}
void BLDC_Driver_Stop(uint8_t motor_id) {
BLDC_Driver_Set_PWM(motor_id, 0);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
}