CM_DOG/User/module/chassis.c

516 lines
25 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 底盘模组
*/
/* Includes ----------------------------------------------------------------- */
#include "chassis.h"
#include <stdlib.h>
#include "cmsis_os2.h"
#include "device/go.h"
#include "component/limiter.h"
#include "component/kinematics.h"
#include "component/path.h"
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
#define CHASSIS_DEFAULT_HEIGHT (0.22f) /* 底盘默认高度,单位:米 */
#define CHASSIS_MAX_SPEED (0.02f) /*调试用速度限幅*/
#define CHASSIS_MAX_TORQUE (1.0f) /*调试用力矩限幅*/
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
static Kinematics_JointCMD_t zero_torque = {
.T = 0.0f, /* 零力矩 */
.W = 0.0f, /* 零速度 */
.Pos = 0.0f, /* 零位置 */
.K_P = 0.0f, /* 零刚度系数 */
.K_W = 0.0f, /* 零速度系数 */
};
static Kinematics_JointCMD_t damping_torque = {
.T = 0.0f, /* 零力矩 */
.W = 0.0f, /* 阻尼速度 */
.Pos = 0.0f, /* 零位置 */
.K_P = 0.0f, /* 零刚度系数 */
.K_W = 0.1f, /* 阻尼速度系数 */
};
//位控模式
static Kinematics_JointCMD_t position_mode = {
.T = 0.0f, /* 零力矩 */
.W = 0.0f, /* 零速度 */
.Pos = 0.0f, /* 零位置 */
.K_P = 1.2f, /* 刚度系数 */
.K_W = 0.05f, /* 速度系数 */
};
// static uint8_t chassis_mode_states = 0;
// static uint8_t chassis_action_states = 0;
static uint8_t stage = 0;
static float T = 0.0f; /* 记录当前时间 */
/* Private function -------------------------------------------------------- */
/**
* \brief 设置底盘运行模式
*
* \param c 包含底盘数据的结构体
* \param mode 要设置的模式
*
* \return 函数运行结果
*/
static int8_t Chassis_SetMode(Chassis_t *c, CMD_ChassisCmd_t *c_cmd) {
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
if (c_cmd == NULL) return CHASSIS_ERR_NULL; /* 控制指令不能为空 */
if (c->mode == c_cmd->mode && c->action == c_cmd->action) {
return CHASSIS_OK; /* 如果当前模式和动作与要设置的相同,直接返回 */
}
stage = 0;
c->mode = c_cmd->mode;
c->action = c_cmd->action; /* 更新底盘模式和动作 */
c->time = 0.0f; /* 重置时间 */
c->eulr_setpoint.pit = 0;
c->eulr_setpoint.rol = 0.0f; /* 重置期望的俯仰角和翻滚角 */
c->eulr_setpoint.yaw = c->eulr_imu.yaw; /* 重置期望的偏航角为当前IMU偏航角 */
if (c->mode != CHASSIS_MODE_POSITION) {
c->height = CHASSIS_DEFAULT_HEIGHT; /* 重置底盘高度 */
}
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
PID_Reset(c->pid.motor_id + i);
LowPassFilter2p_Reset(c->filter.in + i, 0.0f);
LowPassFilter2p_Reset(c->filter.out + i, 0.0f);
}
PID_Reset(&c->pid.blance); /* 重置平衡PID */
return CHASSIS_OK;
}
/* Exported functions ------------------------------------------------------- */
/**
* \brief 初始化底盘
*
* \param c 包含底盘数据的结构体
* \param param 包含底盘参数的结构体指针
* \param target_freq 任务预期的运行频率
*
* \return 函数运行结果
*/
int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param,
float target_freq) {
if (c == NULL) return CHASSIS_ERR_NULL;
c->param = param; /* 初始化参数 */
c->mode = CHASSIS_MODE_RELAX; /* 设置上电后底盘默认模式 */
c->height = CHASSIS_DEFAULT_HEIGHT; /* 设置底盘默认高度为0.2米 */
c->pid.motor_id = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->pid.motor_id));
if (c->pid.motor_id == NULL) goto error;
c->filter.in = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->filter.in));
if (c->filter.in == NULL) goto error;
c->filter.out = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->filter.out));
if (c->filter.out == NULL) goto error;
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
PID_Init(c->pid.motor_id + i, KPID_MODE_NO_D, target_freq,
&(c->param->torque_pid_param));
LowPassFilter2p_Init(c->filter.in + i, target_freq,
c->param->low_pass_cutoff_freq.in);
LowPassFilter2p_Init(c->filter.out + i, target_freq,
c->param->low_pass_cutoff_freq.out);
}
PID_Init(&c->pid.blance, KPID_MODE_NO_D, target_freq,
&(c->param->blance_pid_param));
return CHASSIS_OK; /* 返回成功 */
error:
/* 动态内存分配错误时,释放已经分配的内存,返回错误值 */
BSP_Free(c->pid.motor_id);
BSP_Free(c->filter.in);
BSP_Free(c->filter.out);
return CHASSIS_ERR_NULL;
}
/**
* \brief 更新底盘的反馈信息
*
* \param c 包含底盘数据的结构体
* \param can CAN设备结构体
*
* \return 函数运行结果
*/
int8_t Chassis_UpdateFeedback(Chassis_t *c, const GO_ChassisFeedback_t *go){
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
Kinematics_RealFeedback(&c->feedback.id[i],&go->id[i],
c->param->mech_param.ratio.id[i],
c->param->mech_param.zero_point.id[i]);
}
}
int8_t Chassis_UpdateImu(Chassis_t *c, const AHRS_Eulr_t *eulr){
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
if (eulr == NULL) return CHASSIS_ERR_NULL; /* IMU数据不能为空 */
c->eulr_imu.yaw = eulr->yaw;
c->eulr_imu.pit = eulr->pit;
c->eulr_imu.rol = eulr->rol;
}
int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now){
/* 底盘数据和控制指令结构体不能为空 */
if (c == NULL) return CHASSIS_ERR_NULL;
if (c_cmd == NULL) return CHASSIS_ERR_NULL;
c->dt = (float)(now - c->lask_wakeup) / 1000.0f;
c->lask_wakeup = now;
c->time += c->dt; /* 更新用于生成动作的时间 */
// while (c->time > 1.0f) { c ->time -= 1.0f; } /* 保持时间在0-1秒之间 */
Chassis_SetMode(c, c_cmd); /* 设置底盘模式和动作 */
switch (c->mode) {
case CHASSIS_MODE_RELAX: /* 放松模式,电机不输出 */
c->output = (GO_ChassisCMD_t){0}; /* 清空输出 */
c->mode = CHASSIS_MODE_RELAX; /* 更新状态 */
c->action = CHASSIS_ACTION_NONE; /* 清除动作状态 */
c->time = 0.0f; /* 重置时间 */
break;
case CHASSIS_MODE_DAMP: /* 阻尼模式,电机闭环控制保持阻尼,用于安全模式放松 */
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
c->output.id[i] = damping_torque; /* 设置阻尼力矩 */
}
c->mode = CHASSIS_MODE_DAMP; /* 更新状态 */
c->action = CHASSIS_ACTION_NONE; /* 清除动作状态 */
c->time = 0.0f; /* 重置时间 */
break;
case CHASSIS_MODE_FOLLOW:{
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
c->output.id[i] = position_mode; /* 设置位置模式 */
}
c->delta_eulr.pit = PID_Calc(&c->pid.blance, 0.0f, c->eulr_imu.pit, 0.0f, c->dt);
c->eulr_setpoint.pit += c->delta_eulr.pit; /* 更新期望的俯仰角 */
//限制c->eulr_setpoint.pit在-0.1f到0.1f之间
if (c->eulr_setpoint.pit > 0.05f) {
c->eulr_setpoint.pit = 0.05f;
}
else if (c->eulr_setpoint.pit < -0.05f) {
c->eulr_setpoint.pit = -0.05f;
}
// c->delta_eulr.pit = 0;
/* 计算俯仰角变化量 */
switch (c->action) {
case CHASSIS_ACTION_NONE:{
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
c->output.id[i].Pos = c->feedback.id[i].Pos;}
c->mode = CHASSIS_MODE_POSITION;
c->action = CHASSIS_ACTION_NONE;
c->time = 0.0f; /* 重置时间 */
break;
}
case CHASSIS_ACTION_STAND:{
c->height = c->height + c_cmd->delta_hight * c->dt; /* 更新底盘高度 */
float pitch_correction = PID_Calc(&c->pid.blance, 0.0f, c->eulr_imu.pit, 0.0f, c->dt);
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
if (c->eulr_setpoint.pit > 0.0f) {
if (i == 0) { /* 左前腿 */
float target_pose[3] = {-0.015, -0.0861, -c->height - c->eulr_setpoint.pit};
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
} else if (i == 1) { /* 右前腿 */
float target_pose[3] = {-0.015, 0.0861, -c->height - c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
} else if (i == 2) { /* 左后腿 */
float target_pose[3] = {-0.015, -0.0861, -c->height + c->eulr_setpoint.pit}; /* 左前腿和右后腿 */
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
} else if (i == 3) { /* 右后腿 */
float target_pose[3] = {-0.015, 0.0861, -c->height + c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
}
} else {
if (i == 0) { /* 左前腿 */
float target_pose[3] = {-0.015, -0.0861, -c->height - c->eulr_setpoint.pit};
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
} else if (i == 1) { /* 右前腿 */
float target_pose[3] = {-0.015, 0.0861, -c->height - c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
} else if (i == 2) { /* 左后腿 */
float target_pose[3] = {-0.015, -0.0861, -c->height + c->eulr_setpoint.pit};
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
} else if (i == 3) { /* 右后腿 */
float target_pose[3] = {-0.015, 0.0861, -c->height + c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
}
}
}
// break;
if (c_cmd->ctrl_vec.vx == 0.0f && c_cmd->ctrl_vec.vy == 0.0f && c_cmd->ctrl_vec.wz == 0.0f) {
break;
}
} /* 站立动作 */
case CHASSIS_ACTION_TROT:{
c->height = c->height + c_cmd->delta_hight * c->dt;
float T = 0.6f; // 步态周期(s)
if (c->time > T) {
c->time -= T; // 保持时间在0-T之间
}
float stride_x = c_cmd->ctrl_vec.vx / 10.0f;
float stride_y = c_cmd->ctrl_vec.vy / 10.0f;
float swing_height = 0.12f; // 摆动腿抬高高度
// 对角腿分组0-3一组1-2一组
for (uint8_t leg = 0; leg < 4; leg++) {
float t_leg = fmodf(c->time + ((leg == 0 || leg == 3) ? 0.0f : T/2), T) / T; // 对角腿相差半周期
float base_y = (leg % 2 == 0) ? -0.0861f : 0.0861f;
float wz = (leg < 2) ? c_cmd->ctrl_vec.wz/5 : -c_cmd->ctrl_vec.wz/5; // 前两条腿正向,后两条腿反向
float target_pose[3];
if (t_leg < 0.5f) {
// 摆动相,贝塞尔插值
float t_bezier = t_leg / 0.5f;
float start[3] = {-stride_x -0.015, base_y - stride_y - wz, -c->height};
float mid1[3] = {-0.015 - stride_x/2, base_y, -c->height + swing_height};
float mid2[3] = {-0.015 + stride_x/2, base_y, -c->height + swing_height};
float end[3] = {stride_x -0.015, base_y + stride_y + wz, -c->height};
Path_Bezier3d(start, mid1, mid2, end, t_bezier, target_pose);
} else {
// 支撑相,首尾相连,起点为上一个摆动相终点,终点为下一个摆动相起点
float t_line = (t_leg - 0.5f) / 0.5f;
float start[3] = {stride_x-0.015, base_y + stride_y + wz, -c->height}; // 摆动相终点
float end[3] = {-stride_x-0.015, base_y - stride_y - wz, -c->height}; // 下一个摆动相起点
Path_straight3d(start, end, t_line, target_pose);
}
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[leg]);
c->output.id[leg * 3 + 0].Pos = angle_pose[0];
c->output.id[leg * 3 + 1].Pos = angle_pose[1];
c->output.id[leg * 3 + 2].Pos = angle_pose[2];
}
break;
}
}
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
/*PID计算力矩*/
c->output.id[i].T = PID_Calc(c->pid.motor_id, c->output.id[i].Pos, c->feedback.id[i].Pos, 0.0f, c->dt);
}
break;
}
case CHASSIS_MODE_POSITION:{
/*设置pd参数为位控参数获取遥控器控制命令*/
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
c->output.id[i] = position_mode;} /* 设置位置模式 */
c->height = c->height + c_cmd->delta_hight * c->dt; /* 更新底盘高度 */
c->eulr_setpoint.pit += c_cmd->delta_eulr.pit* c->dt; /* 更新期望的俯仰角 */
c->eulr_setpoint.rol = 0.0f; /* 期望的俯仰角和翻滚角 */
c->eulr_setpoint.yaw += c_cmd->delta_eulr.yaw * c->dt; /* 期望的偏航角 */
/*pitch轴软件限位*/
if (c->eulr_setpoint.pit > 0.2f) {
c->eulr_setpoint.pit = 0.2f; /* 限制俯仰角在-0.05到0.05之间 */
} else if (c->eulr_setpoint.pit < -0.2f) {
c->eulr_setpoint.pit = -0.2f; /* 限制俯仰角在-0.05到0.05之间 */
}
switch (c->action)
{
case CHASSIS_ACTION_NONE:{
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
c->output.id[i].Pos = c->feedback.id[i].Pos;}
c->mode = CHASSIS_MODE_POSITION;
c->action = CHASSIS_ACTION_NONE;
c->time = 0.0f; /* 重置时间 */
break;
}
case CHASSIS_ACTION_STAND:{
switch (stage) {
/* 重置时间记录当前位子 */
case 0:{
c->time = 0.0f; /* 重置时间 */
T = 0.2f; /* 设置站立动作的时间周期 */
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
c->leg_pos.start_pos.leg[i].x = c->feedback.id[i * 3].Pos; /* 四条腿基坐标x轴位置 */
c->leg_pos.start_pos.leg[i].y = c->feedback.id[i * 3 + 1].Pos;
c->leg_pos.start_pos.leg[i].z = c->feedback.id[i * 3 + 2].Pos;
c->leg_pos.end_pos.leg[i].x = -0.015f; /* 四条腿基坐标x轴位置 */
c->leg_pos.end_pos.leg[i].y = (i % 2 == 0) ? -c->param->mech_param.length.hip : c->param->mech_param.length.hip; /* 左前腿和右后腿y轴位置右前腿和左后腿y轴位置 */
c->leg_pos.end_pos.leg[i].z = -c->height; /* 四条腿基坐标z轴位置 */
float angle_pose[3];
Kinematics_InverseKinematics(&c->leg_pos.end_pos.leg[i].x, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->leg_pos.end_pos.leg[i].x = angle_pose[0]; /* 髋关节 */
c->leg_pos.end_pos.leg[i].y = angle_pose[1]; /* 大腿 */
c->leg_pos.end_pos.leg[i].z = angle_pose[2]; /* 小腿 */
/*设置四腿基坐标*/
c->foot_base[i].x = -0.015f; /* 四条腿基坐标x轴位置 */
c->foot_base[i].y = (i % 2 == 0) ? -c->param->mech_param.length.hip : c->param->mech_param.length.hip; /* 左前腿和右后腿y轴位置右前腿和左后腿y轴位置 */
c->foot_base[i].z = -c->height;
}
stage ++;
break;
}
case 1:{
/*在周期内对12个关节完成一次直线插补*/
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
float t = c->time / T; /* 计算当前时间在周期中的比例 */
c->output.id[i * 3].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].x, c->leg_pos.end_pos.leg[i].x, t); /* 髋关节 */
c->output.id[i * 3 + 1].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].y, c->leg_pos.end_pos.leg[i].y, t); /* 大腿 */
c->output.id[i * 3 + 2].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].z, c->leg_pos.end_pos.leg[i].z, t); /* 小腿 */
}
if (c->time >= T) {
stage ++;
}
break;
}
case 2:{
/*计算pit和rol的平衡pid*/
float pitch_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.pit, c->eulr_imu.pit, 0.0f, c->dt);
float roll_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.rol, c->eulr_imu.rol, 0.0f, c->dt);
/*计算为了补偿平衡的腿长*/
c->foot_base[0].z = -c->height - pitch_correction - roll_correction + c->delta_eulr.pit; /* 左前腿 */
c->foot_base[1].z = -c->height - pitch_correction + roll_correction + c->delta_eulr.pit; /* 右前腿 */
c->foot_base[2].z = -c->height + pitch_correction - roll_correction - c->delta_eulr.pit; /* 左后腿 */
c->foot_base[3].z = -c->height + pitch_correction + roll_correction - c->delta_eulr.pit; /* 右后腿 */
float target_pose[3];
float angle_pose[3];
/*将足端坐标转换为关节角度*/
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
target_pose[0] = c->foot_base[i].x; /* 髋关节 */
target_pose[1] = c->foot_base[i].y; /* 大腿 */
target_pose[2] = c->foot_base[i].z;
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
c->output.id[i * 3].Pos = angle_pose[0]; /* 髋关节 */
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 大腿 */
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 小腿 */
}
break;
}
}
if (c_cmd->ctrl_vec.vx == 0.0f && c_cmd->ctrl_vec.vy == 0.0f && c_cmd->ctrl_vec.wz == 0.0f){
break;
}
} /* 站立动作 */
case CHASSIS_ACTION_TROT:{
if( c->action == CHASSIS_ACTION_TROT){
float pitch_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.pit, c->eulr_imu.pit, 0.0f, c->dt);
float roll_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.rol, c->eulr_imu.rol, 0.0f, c->dt);
/*计算为了补偿平衡的腿长*/
c->foot_base[0].z = -c->height - pitch_correction - roll_correction; /* 左前腿 */
c->foot_base[1].z = -c->height - pitch_correction + roll_correction; /* 右前腿 */
c->foot_base[2].z = -c->height + pitch_correction - roll_correction; /* 左后腿 */
c->foot_base[3].z = -c->height + pitch_correction + roll_correction; /* 右后腿 */
}
T = 0.6f; /* 设置trot动作的时间周期 */
if (c->time > T) {
c->time -= T; // 保持时间在0-T之间
}
float stride_x = c_cmd->ctrl_vec.vx / 10.0f; /* 步幅x轴 */
float stride_y = c_cmd->ctrl_vec.vy / 10.0f; /* 步幅y轴 */
float swing_height = 0.12f; // 摆动腿默认抬高高度
// 对角腿分组0-3一组1-2一组
for (uint8_t leg = 0; leg < 4; leg++) {
float t_leg = fmodf(c->time + ((leg == 0 || leg == 3) ? 0.0f : T/2), T) / T; // 对角腿相差半周期
float wz = (leg < 2) ? c_cmd->ctrl_vec.wz/5 : -c_cmd->ctrl_vec.wz/5; // 前两条腿正向,后两条腿反向
float target_pose[3];
if (t_leg < 0.5f) {
// 摆动相,贝塞尔插值
float t_bezier = t_leg / 0.5f;
float start[3] = {c->foot_base[leg].x -stride_x, c->foot_base[leg].y - stride_y - wz, c->foot_base[leg].z};
float mid1[3] = {c->foot_base[leg].x - stride_x/2, c->foot_base[leg].y, c->foot_base[leg].z + swing_height};
float mid2[3] = {c->foot_base[leg].x + stride_x/2, c->foot_base[leg].y, c->foot_base[leg].z + swing_height};
float end[3] = {c->foot_base[leg].x +stride_x , c->foot_base[leg].y + stride_y + wz, c->foot_base[leg].z};
Path_Bezier3d(start, mid1, mid2, end, t_bezier, target_pose);
} else {
// 支撑相,首尾相连,起点为上一个摆动相终点,终点为下一个摆动相起点
float t_line = (t_leg - 0.5f) / 0.5f;
float start[3] = {stride_x+c->foot_base[leg].x, c->foot_base[leg].y + stride_y + wz, c->foot_base[leg].z}; // 摆动相终点
float end[3] = {-stride_x+c->foot_base[leg].x, c->foot_base[leg].y - stride_y - wz, c->foot_base[leg].z}; // 下一个摆动相起点
Path_straight3d(start, end, t_line, target_pose);
}
float angle_pose[3];
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[leg]);
c->output.id[leg * 3 + 0].Pos = angle_pose[0];
c->output.id[leg * 3 + 1].Pos = angle_pose[1];
c->output.id[leg * 3 + 2].Pos = angle_pose[2];
}
break;
} /* trot动作 */
}
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
/*PID计算力矩*/
c->output.id[i].T = PID_Calc(c->pid.motor_id, c->output.id[i].Pos, c->feedback.id[i].Pos, 0.0f, c->dt);
}
}
}
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
Limit_ChassicOutput(c->feedback.id[i].Pos, &c->output.id[i].Pos, &c->output.id[i].T,
c->param->mech_param.ratio.id[i] * CHASSIS_MAX_SPEED,
c->param->mech_param.limit.max.id[i],
c->param->mech_param.limit.min.id[i],
c->param->mech_param.ratio.id[i] * CHASSIS_MAX_TORQUE);
}
}
void Chassis_DumpOutput(const Chassis_t *c, GO_ChassisCMD_t *out){
if (c == NULL || out == NULL) return; /* 主结构体和输出结构体不能为空 */
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
Kinematics_RealOutput(&c->output.id[i], &out->id[i],
c->param->mech_param.ratio.id[i],
c->param->mech_param.zero_point.id[i]);
}
}