- IPS200 display menu with real-time status and parameter settings - Camera image processing and line tracking - IMU attitude solution and PID control - WiFi SPI oscilloscope data transmission
348 lines
9.9 KiB
C
348 lines
9.9 KiB
C
/*********************************************************************************************************************
|
|
* TC264 智能车 - 四轮差速速度/转向PID控制模块 (pid_control.c)
|
|
*
|
|
* 适用: 四轮小车, 两个电机各驱动一侧两个轮子 (差速转向)
|
|
* 传感器: 2个编码器 + 1个陀螺仪(IMU) + 摄像头
|
|
*
|
|
* 控制结构:
|
|
* 前向运动:
|
|
* 目标速度 → 左/右速度环(内环) → 左/右PWM基础值
|
|
*
|
|
* 并行 (转向):
|
|
* 转向环 → PWM差速值 (陀螺仪 yaw rate 作为 D 项阻尼)
|
|
*
|
|
* 最终:
|
|
* 左轮PWM = 左速度环输出 - 转向环输出
|
|
* 右轮PWM = 右速度环输出 + 转向环输出
|
|
*
|
|
* 调用周期:
|
|
* 速度环: 100ms (CCU61_CH0 中断)
|
|
* 转向环: 帧周期 (cpu0_main 主循环, 或 5ms ISR 中更新 gyro D 项)
|
|
*********************************************************************************************************************/
|
|
|
|
#include "zf_common_headfile.h"
|
|
#include "pid_control.h"
|
|
|
|
/* ==================== PID 句柄 ==================== */
|
|
pid_t g_pid_speed_left;
|
|
pid_t g_pid_speed_right;
|
|
pid_t g_pid_steering;
|
|
|
|
/* ==================== 控制使能 ==================== */
|
|
bool g_control_enabled = FALSE;
|
|
|
|
/* ==================== 级联变量 ==================== */
|
|
|
|
/* 目标速度 (Base Spd → 速度环输入), 单位: 编码器脉冲/100ms */
|
|
float g_target_speed = 0.0f;
|
|
|
|
/* 最终下发到左右电机的 PWM 命令 (含死区补偿和转向差速) */
|
|
float g_motor_pwm_left_cmd = 0.0f;
|
|
float g_motor_pwm_right_cmd = 0.0f;
|
|
|
|
/* 陀螺仪 z 轴角速度 (°/s), 在 5ms ISR 中更新, 供转向环 D 项使用 */
|
|
float g_gyro_z_dps = 0.0f;
|
|
|
|
/* 转向差速分量 (转向环输出), 单位: PWM占空比 (叠加到左右轮) */
|
|
static float g_steering_offset = 0.0f;
|
|
|
|
static int8 g_speed_target_dir_last = 0;
|
|
|
|
/* ==================== 电机引脚 ==================== */
|
|
/*
|
|
* 左电机: MOTOR_PWM1 = ATOM3_CH5_P11_10, MOTOR_DIR1 = P11_9
|
|
* 驱动左侧两个轮子
|
|
* 右电机: MOTOR_PWM2 = ATOM2_CH7_P11_12, MOTOR_DIR2 = P11_11
|
|
* 驱动右侧两个轮子
|
|
*
|
|
* 方向极性 (需实测确认, 以下为默认约定):
|
|
* 左电机: DIR1=1 前进, DIR1=0 后退
|
|
* 右电机: DIR2=1 后退, DIR2=0 前进 (对向安装, 极性与左相反)
|
|
*/
|
|
|
|
void motor_set_left(int16 pwm)
|
|
{
|
|
int16 abs_pwm;
|
|
|
|
if(pwm > PWM_DUTY_MAX_MOTOR) pwm = PWM_DUTY_MAX_MOTOR;
|
|
if(pwm < -PWM_DUTY_MAX_MOTOR) pwm = -PWM_DUTY_MAX_MOTOR;
|
|
|
|
abs_pwm = (pwm >= 0) ? pwm : -pwm;
|
|
|
|
if(pwm > 0)
|
|
{
|
|
gpio_set_level(MOTOR_DIR2, 1); /* 前进 */
|
|
pwm_set_duty(MOTOR_PWM2, (uint32)abs_pwm);
|
|
}
|
|
else if(pwm < 0)
|
|
{
|
|
gpio_set_level(MOTOR_DIR2, 0); /* 后退 */
|
|
pwm_set_duty(MOTOR_PWM2, (uint32)abs_pwm);
|
|
}
|
|
else
|
|
{
|
|
pwm_set_duty(MOTOR_PWM2, 0); /* 停止 */
|
|
}
|
|
}
|
|
|
|
void motor_set_right(int16 pwm)
|
|
{
|
|
int16 abs_pwm;
|
|
|
|
if(pwm > PWM_DUTY_MAX_MOTOR) pwm = PWM_DUTY_MAX_MOTOR;
|
|
if(pwm < -PWM_DUTY_MAX_MOTOR) pwm = -PWM_DUTY_MAX_MOTOR;
|
|
|
|
abs_pwm = (pwm >= 0) ? pwm : -pwm;
|
|
|
|
if(pwm > 0)
|
|
{
|
|
gpio_set_level(MOTOR_DIR1, 1); /* 前进 */
|
|
pwm_set_duty(MOTOR_PWM1, (uint32)abs_pwm);
|
|
}
|
|
else if(pwm < 0)
|
|
{
|
|
gpio_set_level(MOTOR_DIR1, 0); /* 后退 */
|
|
pwm_set_duty(MOTOR_PWM1, (uint32)abs_pwm);
|
|
}
|
|
else
|
|
{
|
|
pwm_set_duty(MOTOR_PWM1, 0); /* 停止 */
|
|
}
|
|
}
|
|
|
|
static int16 motor_pwm_clamp_int32(int32 pwm)
|
|
{
|
|
if(pwm > PWM_DUTY_MAX_MOTOR) pwm = PWM_DUTY_MAX_MOTOR;
|
|
if(pwm < -PWM_DUTY_MAX_MOTOR) pwm = -PWM_DUTY_MAX_MOTOR;
|
|
|
|
return (int16)pwm;
|
|
}
|
|
|
|
static int8 speed_target_dir(float target_speed)
|
|
{
|
|
if(target_speed > 1.0f) return 1;
|
|
if(target_speed < -1.0f) return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int16 speed_pwm_add_deadband(int16 pid_output, int8 target_dir)
|
|
{
|
|
int32 pwm_cmd;
|
|
|
|
if(target_dir > 0)
|
|
{
|
|
pwm_cmd = (int32)MOTOR_PWM_DEADBAND + (int32)pid_output;
|
|
if(pwm_cmd < 0) pwm_cmd = 0;
|
|
}
|
|
else if(target_dir < 0)
|
|
{
|
|
pwm_cmd = -(int32)MOTOR_PWM_DEADBAND + (int32)pid_output;
|
|
if(pwm_cmd > 0) pwm_cmd = 0;
|
|
}
|
|
else
|
|
{
|
|
pwm_cmd = 0;
|
|
}
|
|
|
|
return motor_pwm_clamp_int32(pwm_cmd);
|
|
}
|
|
|
|
/* ==================== PID 核心 ==================== */
|
|
|
|
void pid_init(pid_t *p, float kp, float ki, float kd,
|
|
float out_max, float out_min, float integral_limit, uint8 mode)
|
|
{
|
|
p->kp = kp;
|
|
p->ki = ki;
|
|
p->kd = kd;
|
|
p->out_max = out_max;
|
|
p->out_min = out_min;
|
|
p->integral_limit = integral_limit;
|
|
p->mode = mode;
|
|
p->setpoint = 0.0f;
|
|
p->feedback = 0.0f;
|
|
p->error = 0.0f;
|
|
p->integral = 0.0f;
|
|
p->prev_error = 0.0f;
|
|
p->prev_error2 = 0.0f;
|
|
p->derivative = 0.0f;
|
|
p->output = 0.0f;
|
|
}
|
|
|
|
float pid_compute(pid_t *p)
|
|
{
|
|
p->error = p->setpoint - p->feedback;
|
|
|
|
/* 积分分离 */
|
|
if(p->integral_limit > 0.0f)
|
|
{
|
|
float abs_err = (p->error >= 0.0f) ? p->error : -p->error;
|
|
if(abs_err > p->integral_limit)
|
|
{
|
|
p->integral = 0.0f;
|
|
}
|
|
}
|
|
|
|
/* 积分累加 + 抗饱和 (仅在 ki != 0 时累加, 避免无效累积) */
|
|
if(p->ki != 0.0f)
|
|
{
|
|
if((p->output < p->out_max) && (p->output > p->out_min))
|
|
{
|
|
p->integral += p->error;
|
|
}
|
|
}
|
|
|
|
/* 微分先行 (仅在 kd != 0 时计算, 避免无效更新) */
|
|
if(p->kd != 0.0f)
|
|
{
|
|
p->derivative = p->feedback - p->prev_error;
|
|
p->prev_error = p->feedback;
|
|
}
|
|
|
|
/* 位置式 PID */
|
|
p->output = p->kp * p->error
|
|
+ p->ki * p->integral
|
|
- p->kd * p->derivative;
|
|
|
|
/* 输出限幅 */
|
|
if(p->output > p->out_max) p->output = p->out_max;
|
|
if(p->output < p->out_min) p->output = p->out_min;
|
|
|
|
return p->output;
|
|
}
|
|
|
|
/* 增量式 PID:
|
|
* Δu = kp*(e(k)-e(k-1)) + ki*e(k) + kd*(e(k)-2*e(k-1)+e(k-2))
|
|
* u(k) = clamp(u(k-1) + Δu)
|
|
* 天然抗积分饱和, 输出平滑 */
|
|
float pid_compute_inc(pid_t *p)
|
|
{
|
|
float delta;
|
|
|
|
p->error = p->setpoint - p->feedback;
|
|
|
|
/* 增量式不需要显式积分, ki 项天然隐含积分效果 */
|
|
delta = p->kp * (p->error - p->prev_error)
|
|
+ p->ki * p->error
|
|
+ p->kd * (p->error - 2.0f * p->prev_error + p->prev_error2);
|
|
|
|
/* 滚动历史误差 */
|
|
p->prev_error2 = p->prev_error;
|
|
p->prev_error = p->error;
|
|
|
|
p->output += delta;
|
|
|
|
if(p->output > p->out_max) p->output = p->out_max;
|
|
if(p->output < p->out_min) p->output = p->out_min;
|
|
|
|
return p->output;
|
|
}
|
|
|
|
void pid_reset(pid_t *p)
|
|
{
|
|
p->integral = 0.0f;
|
|
p->prev_error = 0.0f;
|
|
p->prev_error2 = 0.0f;
|
|
|
|
/* 增量式复位时清零累积输出, 避免突变 */
|
|
if(p->mode == 1)
|
|
{
|
|
p->output = 0.0f;
|
|
}
|
|
}
|
|
|
|
/* ==================== 速度环 (内环, 100ms) ==================== */
|
|
/*
|
|
* 输入: enc1_count, enc2_count — 过去100ms编码器脉冲增量
|
|
* 左右速度独立闭环, enc1 默认对应左侧电机, enc2 默认对应右侧电机
|
|
* 输出: 直接驱动左右电机 PWM
|
|
*/
|
|
void cascade_speed_loop(int16 enc1_count, int16 enc2_count)
|
|
{
|
|
float current_speed_left;
|
|
float current_speed_right;
|
|
int16 pwm_left;
|
|
int16 pwm_right;
|
|
int16 motor_pwm_left;
|
|
int16 motor_pwm_right;
|
|
int8 target_dir;
|
|
|
|
if(!g_control_enabled) return;
|
|
|
|
target_dir = speed_target_dir(g_target_speed);
|
|
if(target_dir == 0)
|
|
{
|
|
pid_reset(&g_pid_speed_left);
|
|
pid_reset(&g_pid_speed_right);
|
|
g_speed_target_dir_last = 0;
|
|
g_motor_pwm_left_cmd = 0.0f;
|
|
g_motor_pwm_right_cmd = 0.0f;
|
|
motor_set_left(0);
|
|
motor_set_right(0);
|
|
return;
|
|
}
|
|
|
|
if(target_dir != g_speed_target_dir_last)
|
|
{
|
|
pid_reset(&g_pid_speed_left);
|
|
pid_reset(&g_pid_speed_right);
|
|
g_speed_target_dir_last = target_dir;
|
|
}
|
|
|
|
current_speed_left = (float)enc1_count;
|
|
current_speed_right = (float)enc2_count;
|
|
|
|
g_pid_speed_left.feedback = current_speed_left;
|
|
g_pid_speed_left.setpoint = g_target_speed;
|
|
pid_compute_inc(&g_pid_speed_left);
|
|
|
|
g_pid_speed_right.feedback = current_speed_right;
|
|
g_pid_speed_right.setpoint = g_target_speed;
|
|
pid_compute_inc(&g_pid_speed_right);
|
|
|
|
pwm_left = speed_pwm_add_deadband((int16)g_pid_speed_left.output, target_dir);
|
|
pwm_right = speed_pwm_add_deadband((int16)g_pid_speed_right.output, target_dir);
|
|
|
|
motor_pwm_left = motor_pwm_clamp_int32((int32)((float)pwm_left - g_steering_offset));
|
|
motor_pwm_right = motor_pwm_clamp_int32((int32)((float)pwm_right + g_steering_offset));
|
|
g_motor_pwm_left_cmd = (float)motor_pwm_left;
|
|
g_motor_pwm_right_cmd = (float)motor_pwm_right;
|
|
|
|
/* 叠加转向差速 → 左右电机 */
|
|
motor_set_left(motor_pwm_left);
|
|
motor_set_right(motor_pwm_right);
|
|
}
|
|
|
|
/* ==================== 转向环 (并行, 帧周期) ==================== */
|
|
/*
|
|
* 输入: line_deviation — 摄像头中线偏差 (像素), 正=偏右需右转
|
|
* gyro_z — IMU z轴角速度 (°/s), 用于 D 项阻尼
|
|
* 输出: g_steering_offset — PWM差速值
|
|
*
|
|
* PD控制: P项根据中线偏差产生转向力, D项用gyro_z抑制过冲
|
|
* 不加 I 项: 转向不需要消除静差 (弯道需要持续差速)
|
|
*/
|
|
void cascade_steering_loop(float line_deviation, float gyro_z)
|
|
{
|
|
if(!g_control_enabled) return;
|
|
|
|
/* 转向 PD:
|
|
* P项: 中线偏差 → 差速
|
|
* D项: gyro_z 反馈 → 阻尼 (车正在右转时 gyro_z>0, 需要抑制)
|
|
*/
|
|
g_pid_steering.feedback = line_deviation;
|
|
g_pid_steering.setpoint = 0.0f; /* 目标: 中线居中 */
|
|
pid_compute(&g_pid_steering);
|
|
|
|
/* 叠加 gyro D 项 (在 PID 输出基础上减去角速度反馈) */
|
|
g_steering_offset = g_pid_steering.output
|
|
- g_pid_steering.kd * gyro_z;
|
|
|
|
/* 限幅 */
|
|
if(g_steering_offset > g_pid_steering.out_max)
|
|
g_steering_offset = g_pid_steering.out_max;
|
|
if(g_steering_offset < g_pid_steering.out_min)
|
|
g_steering_offset = g_pid_steering.out_min;
|
|
}
|