260 lines
6.2 KiB
C
260 lines
6.2 KiB
C
|
||
/*
|
||
底盘任务
|
||
*/
|
||
|
||
/* Includes ----------------------------------------------------------------- */
|
||
#include "chassis.h"
|
||
#include "bsp/can.h"
|
||
#include "bsp/time.h"
|
||
#include "component/filter.h"
|
||
#include "component/pid.h"
|
||
#include "device/motor_rm.h"
|
||
#include <math.h>
|
||
#include "module/cmd.h"
|
||
#include "stdlib.h"
|
||
|
||
/* Private typedef ---------------------------------------------------------- */
|
||
|
||
static int8_t Chassis_SetMode(Chassis_t *c, Chassis_mode_t mode, uint32_t now)
|
||
{
|
||
if (!c)
|
||
return CHASSIS_ERR_NULL;
|
||
if (mode == c->mode)
|
||
return CHASSIS_OK;
|
||
|
||
// 重置 PID 和滤波器
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
PID_Reset(&c->pid.motor[i]);
|
||
LowPassFilter2p_Reset(&c->filter.in[i], 0.0f);
|
||
LowPassFilter2p_Reset(&c->filter.out[i], 0.0f);
|
||
}
|
||
|
||
c->mode = mode;
|
||
return CHASSIS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 底盘初始化
|
||
* @param c 底盘结构体指针
|
||
* @param param 底盘参数结构体指针
|
||
* @param target_freq 控制频率(Hz)
|
||
* @return CHASSIS_OK:成功 CHASSIS_ERR_NULL:空指针 CHASSIS_ERR_TYPE:不支持的底盘类型
|
||
*/
|
||
int8_t Chassis_Init(Chassis_t *c, Chassis_Params_t *param,
|
||
float target_freq)
|
||
{
|
||
if (!c)
|
||
return CHASSIS_ERR_NULL;
|
||
|
||
// 初始化 CAN 通信
|
||
BSP_CAN_Init();
|
||
c->param = param;
|
||
c->mode = STOP;
|
||
// 根据底盘类型配置轮组数量与混控器模式
|
||
Mixer_Mode_t mixer_mode;
|
||
switch (param->type)
|
||
{
|
||
case CHASSIS_TYPE_MECANUM: // 麦克纳姆轮
|
||
c->num_wheel = 4;
|
||
mixer_mode = MIXER_MECANUM;
|
||
break;
|
||
case CHASSIS_TYPE_PARLFIX4:
|
||
c->num_wheel = 4;
|
||
mixer_mode = MIXER_PARLFIX4;
|
||
break;
|
||
case CHASSIS_TYPE_PARLFIX2:
|
||
c->num_wheel = 2;
|
||
mixer_mode = MIXER_PARLFIX2;
|
||
break;
|
||
case CHASSIS_TYPE_OMNI_CROSS:
|
||
c->num_wheel = 4;
|
||
mixer_mode = MIXER_OMNICROSS;
|
||
break;
|
||
case CHASSIS_TYPE_OMNI_PLUS: // 全向轮(十字布局)
|
||
c->num_wheel = 4;
|
||
mixer_mode = MIXER_OMNIPLUS;
|
||
break;
|
||
case CHASSIS_TYPE_SINGLE:
|
||
c->num_wheel = 1;
|
||
mixer_mode = MIXER_SINGLE;
|
||
break;
|
||
default:
|
||
return CHASSIS_ERR_TYPE;
|
||
}
|
||
// 初始化时间戳
|
||
c->last_wakeup = 0;
|
||
c->dt = 0.0f;
|
||
// 初始化 PID 和滤波器
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
PID_Init(&c->pid.motor[i], KPID_MODE_NO_D, target_freq, ¶m->pid.motor_pid_param);
|
||
LowPassFilter2p_Init(&c->filter.in[i], target_freq, param->low_pass_cutoff_freq.in);
|
||
LowPassFilter2p_Init(&c->filter.out[i], target_freq, param->low_pass_cutoff_freq.out);
|
||
// 清零电机反馈
|
||
c->feedback.motor[i].rotor_speed = 0;
|
||
c->feedback.motor[i].torque_current = 0;
|
||
c->feedback.motor[i].rotor_abs_angle = 0;
|
||
c->feedback.motor[i].temp = 0;
|
||
}
|
||
// 初始化跟随 PID 和混控器
|
||
PID_Init(&c->pid.follow, KPID_MODE_CALC_D, target_freq, ¶m->pid.follow_pid_param);
|
||
Mixer_Init(&c->mixer, mixer_mode);
|
||
// 清零运动向量与输出
|
||
c->move_vec.vx = c->move_vec.vy = c->move_vec.wz = 0.0f;
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
c->out.motor[i] = 0.0f;
|
||
}
|
||
// 注册电机
|
||
for (int i = 0; i < c->num_wheel; i++)
|
||
{
|
||
MOTOR_RM_Register(&(c->param->motor_param[i]));
|
||
}
|
||
return CHASSIS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 更新底盘反馈(电机状态)
|
||
* @param c 底盘结构体指针
|
||
* @return CHASSIS_OK:成功 CHASSIS_ERR_NULL:空指针
|
||
*/
|
||
int8_t Chassis_UpdateFeedback(Chassis_t *c)
|
||
{
|
||
|
||
// 更新所有电机反馈
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
MOTOR_RM_Update(&(c->param->motor_param[i]));
|
||
MOTOR_RM_t *rm_motor = MOTOR_RM_GetMotor(&(c->param->motor_param[i]));
|
||
c->motors[i] = rm_motor;
|
||
// MOTOR_RM_t *rm = c->motors[i];
|
||
if (rm_motor != NULL)
|
||
{
|
||
c->feedback.motor[i] = rm_motor->feedback;
|
||
}
|
||
else
|
||
{
|
||
return CHASSIS_ERR_NULL;
|
||
}
|
||
}
|
||
return CHASSIS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 底盘电机控制
|
||
* @param c 底盘结构体指针
|
||
* @param c_cmd 控制命令
|
||
* @param now 当前时间戳(ms)
|
||
* @return CHASSIS_OK:成功 CHASSIS_ERR_NULL:空指针
|
||
*/
|
||
int8_t Chassis_Control(Chassis_t *c, const Chassis_CMD_t *c_cmd, uint32_t now)
|
||
{
|
||
if (!c || !c_cmd)
|
||
return CHASSIS_ERR_NULL;
|
||
// 计算控制周期
|
||
c->dt = (float)(now - c->last_wakeup) / 1000.0f;
|
||
c->last_wakeup = now;
|
||
if (!isfinite(c->dt) || c->dt <= 0.0f)
|
||
{
|
||
c->dt = 0.001f;
|
||
}
|
||
if (c->dt < 0.0005f)
|
||
c->dt = 0.0005f;
|
||
if (c->dt > 0.050f)
|
||
c->dt = 0.050f;
|
||
// 设置模式
|
||
Chassis_SetMode(c, c_cmd->mode, now);
|
||
// 根据模式计算运动向量
|
||
switch (c->mode)
|
||
{
|
||
case STOP:
|
||
c->move_vec.vx = c->move_vec.vy = 0.0f;
|
||
c->move_vec.wz = 0.0f;
|
||
break;
|
||
case RC:
|
||
c->move_vec.vx = c_cmd->Vx;
|
||
c->move_vec.vy = c_cmd->Vy;
|
||
c->move_vec.wz = c_cmd->Vw;
|
||
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
Mixer_Apply(&c->mixer, &c->move_vec, c->setpoint.motor_rpm, c->num_wheel, 500.0f);
|
||
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
float rf = c->setpoint.motor_rpm[i]; /// 目标转速
|
||
float fb = LowPassFilter2p_Apply(&c->filter.in[i], (float)c->feedback.motor[i].rotor_speed);
|
||
float out_current;
|
||
switch (c->mode)
|
||
{
|
||
case RC:
|
||
out_current = c->setpoint.motor_rpm[i] / 1000.0f;
|
||
break;
|
||
case STOP:
|
||
out_current = 0.0f;
|
||
break;
|
||
default:
|
||
out_current = 0.0f;
|
||
break;
|
||
}
|
||
|
||
(void)rf;
|
||
(void)fb;
|
||
|
||
// 低通滤波和限幅
|
||
c->out.motor[i] = LowPassFilter2p_Apply(&c->filter.out[i], out_current);
|
||
// Clip(&c->out.motor[i], -c->param->limit.max_current, c->param->limit.max_current);
|
||
}
|
||
|
||
return CHASSIS_OK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief 电机输出
|
||
* @param c 底盘结构体指针
|
||
*/
|
||
void Chassis_Output(Chassis_t *c)
|
||
{
|
||
if (!c)
|
||
return; // 空指针检查
|
||
|
||
// 1. 设置每个电机的输出值
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
MOTOR_RM_t *rm = c->motors[i];
|
||
if (!rm)
|
||
continue;
|
||
MOTOR_RM_SetOutput(&rm->param, c->out.motor[i]); // 将计算好的电流值写入电机结构体
|
||
}
|
||
|
||
// 2. 触发CAN发送(只调用一次)
|
||
MOTOR_RM_t *rm = c->motors[0];
|
||
if (rm)
|
||
{
|
||
MOTOR_RM_Ctrl(&rm->param); // 通过CAN总线发送控制帧
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 重置底盘输出
|
||
* @param c 底盘结构体指针
|
||
*/
|
||
void Chassis_ResetOutput(Chassis_t *c)
|
||
{
|
||
if (!c)
|
||
return;
|
||
for (uint8_t i = 0; i < c->num_wheel; i++)
|
||
{
|
||
MOTOR_RM_t *m = c->motors[i];
|
||
if (m)
|
||
{
|
||
MOTOR_RM_Relax(&(m->param));
|
||
}
|
||
}
|
||
} |