2026-03-08 18:17:14 +08:00
|
|
|
|
#include "ladrc.h"
|
2026-03-18 22:03:16 +08:00
|
|
|
|
#include "f4_can_app.h"
|
2026-03-08 18:17:14 +08:00
|
|
|
|
|
2026-03-14 17:17:17 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 这个文件实现两层内容:
|
|
|
|
|
|
* 1) 单个电机的 LADRC 控制器。
|
|
|
|
|
|
* 2) 四轮底盘的调度层(读取编码器 -> 滤波 -> LADRC -> PWM 输出)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 额外升级点:
|
|
|
|
|
|
* - 提供目标 RPM / 控制输出 getter,供上层故障诊断与状态上报使用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define LADRC_CONTROL_DT_S 0.01f
|
|
|
|
|
|
#define LADRC_RPM_FILTER_ALPHA 0.30f
|
|
|
|
|
|
|
|
|
|
|
|
#define LADRC_DEFAULT_WC 25.0f
|
|
|
|
|
|
#define LADRC_DEFAULT_WO 60.0f
|
|
|
|
|
|
#define LADRC_DEFAULT_B0 0.20f
|
|
|
|
|
|
#define LADRC_DEFAULT_OUT_MAX 1000.0f
|
|
|
|
|
|
|
|
|
|
|
|
/* ================== 内部辅助函数 ================== */
|
|
|
|
|
|
static float LADRC_Abs(float x);
|
|
|
|
|
|
static float LADRC_Clamp(float x, float min_value, float max_value);
|
2026-03-18 22:03:16 +08:00
|
|
|
|
static void LADRC_ResetStates(LADRC_TypeDef *ladrc);
|
|
|
|
|
|
|
|
|
|
|
|
static float s_filtered_rpm[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
2026-03-14 17:17:17 +08:00
|
|
|
|
|
|
|
|
|
|
static float LADRC_Abs(float x)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (x >= 0.0f) ? x : -x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static float LADRC_Clamp(float x, float min_value, float max_value)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x > max_value)
|
|
|
|
|
|
{
|
|
|
|
|
|
return max_value;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (x < min_value)
|
|
|
|
|
|
{
|
|
|
|
|
|
return min_value;
|
|
|
|
|
|
}
|
|
|
|
|
|
return x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 22:03:16 +08:00
|
|
|
|
static void LADRC_ResetStates(LADRC_TypeDef *ladrc)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ladrc == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ladrc->v = 0.0f;
|
|
|
|
|
|
ladrc->y = 0.0f;
|
|
|
|
|
|
ladrc->z1 = 0.0f;
|
|
|
|
|
|
ladrc->z2 = 0.0f;
|
|
|
|
|
|
ladrc->u = 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-08 18:17:14 +08:00
|
|
|
|
/**
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* @brief 初始化单个 LADRC 控制器。
|
|
|
|
|
|
* @note 顺手做基础参数保护,避免 b0/h/out_max 为 0 或负值时出问题。
|
2026-03-08 18:17:14 +08:00
|
|
|
|
*/
|
2026-03-14 17:17:17 +08:00
|
|
|
|
void LADRC_Init(LADRC_TypeDef *ladrc, float wc, float wo, float b0, float h, float max)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ladrc == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (h <= 0.0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
h = LADRC_CONTROL_DT_S;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (LADRC_Abs(b0) < 1e-6f)
|
|
|
|
|
|
{
|
|
|
|
|
|
b0 = LADRC_DEFAULT_B0;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (wc < 0.0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
wc = -wc;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (wo < 0.0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
wo = -wo;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (max <= 0.0f)
|
|
|
|
|
|
{
|
|
|
|
|
|
max = LADRC_DEFAULT_OUT_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ladrc->v = 0.0f; /* 目标值 */
|
|
|
|
|
|
ladrc->y = 0.0f; /* 测量值 */
|
|
|
|
|
|
ladrc->z1 = 0.0f; /* 状态观测量 */
|
|
|
|
|
|
ladrc->z2 = 0.0f; /* 总扰动观测量 */
|
|
|
|
|
|
ladrc->u = 0.0f; /* 上一拍控制输出 */
|
|
|
|
|
|
|
2026-03-08 18:17:14 +08:00
|
|
|
|
ladrc->wc = wc;
|
|
|
|
|
|
ladrc->wo = wo;
|
|
|
|
|
|
ladrc->b0 = b0;
|
|
|
|
|
|
ladrc->h = h;
|
|
|
|
|
|
ladrc->out_max = max;
|
2026-03-14 17:17:17 +08:00
|
|
|
|
|
2026-03-08 18:17:14 +08:00
|
|
|
|
ladrc->beta1 = 2.0f * wo;
|
|
|
|
|
|
ladrc->beta2 = wo * wo;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* @brief 单个 LADRC 周期运算。
|
|
|
|
|
|
* @param ladrc 控制器实例
|
|
|
|
|
|
* @param actual_val 当前测量速度(RPM)
|
|
|
|
|
|
* @retval 限幅后的控制输出(通常可直接映射到 PWM)
|
2026-03-08 18:17:14 +08:00
|
|
|
|
*/
|
2026-03-14 17:17:17 +08:00
|
|
|
|
float LADRC_Calc(LADRC_TypeDef *ladrc, float actual_val)
|
|
|
|
|
|
{
|
|
|
|
|
|
float e;
|
|
|
|
|
|
float u0;
|
|
|
|
|
|
float out;
|
|
|
|
|
|
|
|
|
|
|
|
if (ladrc == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-08 18:17:14 +08:00
|
|
|
|
ladrc->y = actual_val;
|
2026-03-14 17:17:17 +08:00
|
|
|
|
|
|
|
|
|
|
/* 第 1 部分:LESO(线性扩张状态观测器) */
|
|
|
|
|
|
e = ladrc->y - ladrc->z1;
|
2026-03-08 18:17:14 +08:00
|
|
|
|
ladrc->z1 += (ladrc->z2 + ladrc->b0 * ladrc->u + ladrc->beta1 * e) * ladrc->h;
|
|
|
|
|
|
ladrc->z2 += (ladrc->beta2 * e) * ladrc->h;
|
2026-03-14 17:17:17 +08:00
|
|
|
|
|
|
|
|
|
|
/* 第 2 部分:LSEF + 扰动补偿 */
|
|
|
|
|
|
u0 = ladrc->wc * (ladrc->v - ladrc->z1);
|
|
|
|
|
|
out = (u0 - ladrc->z2) / ladrc->b0;
|
|
|
|
|
|
|
|
|
|
|
|
/* 第 3 部分:输出限幅,保护电机和驱动 */
|
|
|
|
|
|
out = LADRC_Clamp(out, -ladrc->out_max, ladrc->out_max);
|
|
|
|
|
|
|
|
|
|
|
|
ladrc->u = out;
|
2026-03-08 18:17:14 +08:00
|
|
|
|
return out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* =====================================================================
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* 针对四轮底盘的 LADRC 调度层
|
2026-03-08 18:17:14 +08:00
|
|
|
|
* ===================================================================== */
|
|
|
|
|
|
|
|
|
|
|
|
LADRC_TypeDef ladrc_motors[4];
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* @brief 一键初始化底层硬件和四个 LADRC 控制器。
|
2026-03-08 18:17:14 +08:00
|
|
|
|
*/
|
|
|
|
|
|
void FourWheel_LADRC_Init(void)
|
|
|
|
|
|
{
|
2026-03-14 17:17:17 +08:00
|
|
|
|
int i;
|
|
|
|
|
|
|
2026-03-08 18:17:14 +08:00
|
|
|
|
Motor_Init();
|
|
|
|
|
|
|
2026-03-14 17:17:17 +08:00
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
LADRC_Init(&ladrc_motors[i],
|
|
|
|
|
|
LADRC_DEFAULT_WC,
|
|
|
|
|
|
LADRC_DEFAULT_WO,
|
|
|
|
|
|
LADRC_DEFAULT_B0,
|
|
|
|
|
|
LADRC_CONTROL_DT_S,
|
|
|
|
|
|
LADRC_DEFAULT_OUT_MAX);
|
2026-03-18 22:03:16 +08:00
|
|
|
|
s_filtered_rpm[i] = 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FourWheel_LADRC_ResetAll(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
LADRC_ResetStates(&ladrc_motors[i]);
|
|
|
|
|
|
s_filtered_rpm[i] = 0.0f;
|
2026-03-08 18:17:14 +08:00
|
|
|
|
}
|
2026-03-18 22:03:16 +08:00
|
|
|
|
|
|
|
|
|
|
Motor_Brake_All();
|
2026-03-08 18:17:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* @brief 设置四个轮子的目标 RPM。
|
2026-03-08 18:17:14 +08:00
|
|
|
|
*/
|
|
|
|
|
|
void FourWheel_Set_Target_RPM(float fl_rpm, float rl_rpm, float fr_rpm, float rr_rpm)
|
|
|
|
|
|
{
|
|
|
|
|
|
ladrc_motors[MOTOR_FL].v = fl_rpm;
|
|
|
|
|
|
ladrc_motors[MOTOR_RL].v = rl_rpm;
|
|
|
|
|
|
ladrc_motors[MOTOR_FR].v = fr_rpm;
|
|
|
|
|
|
ladrc_motors[MOTOR_RR].v = rr_rpm;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-14 17:17:17 +08:00
|
|
|
|
* @brief 四轮 LADRC 大循环,建议固定 10ms 调用一次。
|
|
|
|
|
|
* @note 调度顺序:
|
|
|
|
|
|
* 1) 更新编码器测速
|
|
|
|
|
|
* 2) 一阶低通滤波,减小测速毛刺
|
|
|
|
|
|
* 3) 每个轮子独立做 LADRC
|
|
|
|
|
|
* 4) 输出到 H 桥
|
2026-03-08 18:17:14 +08:00
|
|
|
|
*/
|
|
|
|
|
|
void FourWheel_LADRC_Control_Loop(void)
|
|
|
|
|
|
{
|
2026-03-14 17:17:17 +08:00
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
Motor_Update_RPM(LADRC_CONTROL_DT_S);
|
2026-03-08 18:17:14 +08:00
|
|
|
|
|
2026-03-18 22:03:16 +08:00
|
|
|
|
if (g_robot_ctrl.state != SYSTEM_OPERATIONAL)
|
|
|
|
|
|
{
|
|
|
|
|
|
FourWheel_LADRC_ResetAll();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-14 17:17:17 +08:00
|
|
|
|
for (i = 0; i < 4; ++i)
|
2026-03-08 18:17:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
float raw_rpm = Get_Motor_RPM((Motor_ID_t)i);
|
2026-03-14 17:17:17 +08:00
|
|
|
|
float pwm_out;
|
2026-03-08 18:17:14 +08:00
|
|
|
|
|
2026-03-18 22:03:16 +08:00
|
|
|
|
s_filtered_rpm[i] = (1.0f - LADRC_RPM_FILTER_ALPHA) * s_filtered_rpm[i]
|
|
|
|
|
|
+ LADRC_RPM_FILTER_ALPHA * raw_rpm;
|
2026-03-08 18:17:14 +08:00
|
|
|
|
|
2026-03-18 22:03:16 +08:00
|
|
|
|
pwm_out = LADRC_Calc(&ladrc_motors[i], s_filtered_rpm[i]);
|
2026-03-08 18:17:14 +08:00
|
|
|
|
Set_Motor_Output((Motor_ID_t)i, (int16_t)pwm_out);
|
|
|
|
|
|
}
|
2026-03-14 17:17:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 读取指定轮子的“当前目标 RPM”。
|
|
|
|
|
|
* @note 该接口主要供上层做状态上报与故障诊断使用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
float FourWheel_Get_Target_RPM(Motor_ID_t id)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((id < MOTOR_FL) || (id > MOTOR_RR))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ladrc_motors[id].v;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 读取指定轮子的“当前控制输出”。
|
|
|
|
|
|
* @note 该值尚未经过电机左右镜像翻转,但足够用于判断是否长期顶满。
|
|
|
|
|
|
*/
|
|
|
|
|
|
float FourWheel_Get_Control_Output(Motor_ID_t id)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((id < MOTOR_FL) || (id > MOTOR_RR))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ladrc_motors[id].u;
|
|
|
|
|
|
}
|