添加跳跃标志

This commit is contained in:
Robofish 2026-01-14 10:50:31 +08:00
parent 5abd099f6a
commit 8330656915
8 changed files with 305 additions and 58 deletions

View File

@ -62,6 +62,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/component/error_detect.c
User/component/filter.c
User/component/freertos_cli.c
User/component/limiter.c
User/component/lqr.c
User/component/pid.c
User/component/user_math.c

View File

@ -19,6 +19,9 @@ filter:
freertos_cli:
dependencies: []
enabled: true
limiter:
dependencies: []
enabled: true
pid:
dependencies:
- component/filter

167
User/component/limiter.c Normal file
View File

@ -0,0 +1,167 @@
/*
*/
#include "limiter.h"
#include "user_math.h"
#include <math.h>
#include <stddef.h>
#define POWER_BUFF_THRESHOLD 20
#define CHASSIS_POWER_CHECK_FREQ 10
#define CHASSIS_POWER_FACTOR_PASS 0.9f
#define CHASSIS_POWER_FACTOR_NO_PASS 1.5f
#define CHASSIS_MOTOR_CIRCUMFERENCE 0.12f
/**
* @brief power_limit
*
* @param power_limit
* @param motor_out
* @param speed
* @param len
* @return int8_t 0
*/
int8_t PowerLimit_ChassicOutput(float power_limit, float *motor_out,
float *speed, uint32_t len) {
/* power_limit小于0时不进行限制 */
if (motor_out == NULL || speed == NULL || power_limit < 0) return -1;
float sum_motor_out = 0.0f;
for (uint32_t i = 0; i < len; i++) {
/* 总功率计算 P=F(由转矩电流表示)*V(由转速表示) */
sum_motor_out +=
fabsf(motor_out[i]) * fabsf(speed[i]) * CHASSIS_MOTOR_CIRCUMFERENCE;
}
/* 保持每个电机输出值缩小时比例不变 */
if (sum_motor_out > power_limit) {
for (uint32_t i = 0; i < len; i++) {
motor_out[i] *= power_limit / sum_motor_out;
}
}
return 0;
}
/**
* @brief
*
* @param power_in
* @param power_limit
* @param power_buffer
* @return float
*/
float PowerLimit_CapInput(float power_in, float power_limit,
float power_buffer) {
float target_power = 0.0f;
/* 计算下一个检测周期的剩余缓冲能量 */
float heat_buff = power_buffer - (float)(power_in - power_limit) /
(float)CHASSIS_POWER_CHECK_FREQ;
if (heat_buff < POWER_BUFF_THRESHOLD) { /* 功率限制 */
target_power = power_limit * CHASSIS_POWER_FACTOR_PASS;
} else {
target_power = power_limit * CHASSIS_POWER_FACTOR_NO_PASS;
}
return target_power;
}
/**
* @brief 使
*
* @param power_limit
* @param power_buffer
* @return float
*/
float PowerLimit_TargetPower(float power_limit, float power_buffer) {
float target_power = 0.0f;
/* 根据剩余缓冲能量计算输出功率 */
target_power = power_limit * (power_buffer - 10.0f) / 20.0f;
if (target_power < 0.0f) target_power = 0.0f;
return target_power;
}
/**
* @brief
*
* @param heat
* @param heat_limit
* @param cooling_rate
* @param heat_increase
* @param shoot_freq
* @return float
*/
float HeatLimit_ShootFreq(float heat, float heat_limit, float cooling_rate,
float heat_increase, bool is_big) {
float heat_percent = heat / heat_limit;
float stable_freq = cooling_rate / heat_increase;
if (is_big)
return stable_freq;
else
return (heat_percent > 0.7f) ? stable_freq : 3.0f * stable_freq;
}
/**
* @brief IMU坐标系之间的偏差±π
* @param motor_angle
* @param imu_angle IMU角度
* @return -π ~ π
*/
static float CalcMotorImuOffset(float motor_angle, float imu_angle) {
float offset = motor_angle - imu_angle;
if (offset > M_PI) offset -= M_2PI;
if (offset < -M_PI) offset += M_2PI;
return offset;
}
/**
* @brief - IMU坐标系偏差
*/
void CircleAngleLimit(float *setpoint, float motor_angle, float imu_angle,
float limit_max, float limit_min, float range) {
if (setpoint == NULL) return;
/* 计算电机与IMU坐标系偏差 */
float motor_imu_offset = CalcMotorImuOffset(motor_angle, imu_angle);
/* 将IMU setpoint转换为电机角度后进行限位检查 */
float motor_target = *setpoint + motor_imu_offset;
/* 检查是否超过最大限位 */
if (CircleError(motor_target, limit_max, range) > 0) {
*setpoint = limit_max - motor_imu_offset;
}
/* 检查是否低于最小限位 */
if (CircleError(motor_target, limit_min, range) < 0) {
*setpoint = limit_min - motor_imu_offset;
}
}
/**
* @brief - IMU坐标系偏差
*/
void CircleAngleDeltaLimit(float *delta, float setpoint, float motor_angle,
float imu_angle, float limit_max, float limit_min,
float range) {
if (delta == NULL) return;
/* 计算电机与IMU坐标系偏差 */
float motor_imu_offset = CalcMotorImuOffset(motor_angle, imu_angle);
/* 计算应用增量后在电机坐标系下的目标角度 */
float motor_target_after_delta = setpoint + motor_imu_offset + *delta;
/* 计算到限位边界的距离 */
float delta_max = CircleError(limit_max, motor_target_after_delta, range);
float delta_min = CircleError(limit_min, motor_target_after_delta, range);
/* 限制增量 */
if (*delta > delta_max) *delta = delta_max;
if (*delta < delta_min) *delta = delta_min;
}

93
User/component/limiter.h Normal file
View File

@ -0,0 +1,93 @@
/*
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/**
* @brief power_limit
*
* @param power_limit
* @param motor_out
* @param speed
* @param len
* @return int8_t 0
*/
int8_t PowerLimit_ChassicOutput(float power_limit, float *motor_out,
float *speed, uint32_t len);
/**
* @brief
*
* @param power_in
* @param power_limit
* @param power_buffer
* @return float
*/
float PowerLimit_CapInput(float power_in, float power_limit,
float power_buffer);
/**
* @brief 使
*
* @param power_limit
* @param power_buffer
* @return float
*/
float PowerLimit_TargetPower(float power_limit, float power_buffer);
/**
* @brief
*
* @param heat
* @param heat_limit
* @param cooling_rate
* @param heat_increase
* @param shoot_freq
* @return float
*/
float HeatLimit_ShootFreq(float heat, float heat_limit, float cooling_rate,
float heat_increase, bool is_big);
/**
* @brief - IMU坐标系偏差
* @note IMU
*
* @param setpoint IMU坐标系
* @param motor_angle
* @param imu_angle IMU当前角度
* @param limit_max
* @param limit_min
* @param range M_2PI
*/
void CircleAngleLimit(float *setpoint, float motor_angle, float imu_angle,
float limit_max, float limit_min, float range);
/**
* @brief - IMU坐标系偏差
* @note
*
* @param delta
* @param setpoint IMU坐标系
* @param motor_angle
* @param imu_angle IMU当前角度
* @param limit_max
* @param limit_min
* @param range M_2PI
*/
void CircleAngleDeltaLimit(float *delta, float setpoint, float motor_angle,
float imu_angle, float limit_max, float limit_min,
float range);

View File

@ -56,6 +56,7 @@ typedef struct {
Chassis_Mode_t mode; /* 底盘模式 */
MoveVector_t move_vec; /* 底盘运动向量 */
float height; /* 目标高度 */
bool jump_trigger; /* 跳跃触发标志 */
} Chassis_CMD_t;
typedef struct {

View File

@ -8,6 +8,7 @@
#include "bsp/time.h"
#include <math.h>
#include "component/filter.h"
#include "component/limiter.h"
#include "component/pid.h"
#include "device/gimbal_imu.h"
#include "device/motor_dm.h"
@ -173,45 +174,23 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd, Gimbal_AI_t *g_ai) {
Gimbal_SetMode(g, g_cmd->mode);
/* 处理yaw控制命令软件限位 - 使用电机绝对角度 */
/* 处理yaw控制命令软件限位 */
float delta_yaw = g_cmd->delta_yaw * g->dt * 1.5f;
if (g->param->travel.yaw > 0) {
/* 计算当前电机角度与IMU角度的偏差 */
float motor_imu_offset = g->feedback.motor.yaw.rotor_abs_angle - g->feedback.imu.eulr.yaw;
/* 处理跨越±π的情况 */
if (motor_imu_offset > M_PI) motor_imu_offset -= M_2PI;
if (motor_imu_offset < -M_PI) motor_imu_offset += M_2PI;
/* 计算到限位边界的距离 */
const float delta_max = CircleError(g->limit.yaw.max,
(g->setpoint.eulr.yaw + motor_imu_offset + delta_yaw), M_2PI);
const float delta_min = CircleError(g->limit.yaw.min,
(g->setpoint.eulr.yaw + motor_imu_offset + delta_yaw), M_2PI);
/* 限制控制命令 */
if (delta_yaw > delta_max) delta_yaw = delta_max;
if (delta_yaw < delta_min) delta_yaw = delta_min;
CircleAngleDeltaLimit(&delta_yaw, g->setpoint.eulr.yaw,
g->feedback.motor.yaw.rotor_abs_angle,
g->feedback.imu.eulr.yaw,
g->limit.yaw.max, g->limit.yaw.min, M_2PI);
}
CircleAdd(&(g->setpoint.eulr.yaw), delta_yaw, M_2PI);
/* 处理pitch控制命令软件限位 - 使用电机绝对角度 */
/* 处理pitch控制命令软件限位 */
float delta_pit = g_cmd->delta_pit * g->dt;
if (g->param->travel.pit > 0) {
/* 计算当前电机角度与IMU角度的偏差 */
float motor_imu_offset = g->feedback.motor.pit.rotor_abs_angle - g->feedback.imu.eulr.rol;
/* 处理跨越±π的情况 */
if (motor_imu_offset > M_PI) motor_imu_offset -= M_2PI;
if (motor_imu_offset < -M_PI) motor_imu_offset += M_2PI;
/* 计算到限位边界的距离 */
const float delta_max = CircleError(g->limit.pit.max,
(g->setpoint.eulr.pit + motor_imu_offset + delta_pit), M_2PI);
const float delta_min = CircleError(g->limit.pit.min,
(g->setpoint.eulr.pit + motor_imu_offset + delta_pit), M_2PI);
/* 限制控制命令 */
if (delta_pit > delta_max) delta_pit = delta_max;
if (delta_pit < delta_min) delta_pit = delta_min;
CircleAngleDeltaLimit(&delta_pit, g->setpoint.eulr.pit,
g->feedback.motor.pit.rotor_abs_angle,
g->feedback.imu.eulr.rol,
g->limit.pit.max, g->limit.pit.min, M_2PI);
}
CircleAdd(&(g->setpoint.eulr.pit), delta_pit, M_2PI);
@ -227,35 +206,18 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd, Gimbal_AI_t *g_ai) {
g->setpoint.eulr.yaw = g_ai->yaw;
g->setpoint.eulr.pit = -g_ai->pit;
/* 限位处理 - 需要考虑电机和IMU之间的偏差 */
/* 限位处理 */
if (g->param->travel.yaw > 0) {
float yaw_motor_imu_offset = g->feedback.motor.yaw.rotor_abs_angle - g->feedback.imu.eulr.yaw;
if (yaw_motor_imu_offset > M_PI) yaw_motor_imu_offset -= M_2PI;
if (yaw_motor_imu_offset < -M_PI) yaw_motor_imu_offset += M_2PI;
/* 将IMU setpoint转换为电机角度后进行限位检查 */
float yaw_motor_target = g->setpoint.eulr.yaw + yaw_motor_imu_offset;
if (CircleError(yaw_motor_target, g->limit.yaw.max, M_2PI) > 0) {
g->setpoint.eulr.yaw = g->limit.yaw.max - yaw_motor_imu_offset;
}
if (CircleError(yaw_motor_target, g->limit.yaw.min, M_2PI) < 0) {
g->setpoint.eulr.yaw = g->limit.yaw.min - yaw_motor_imu_offset;
}
CircleAngleLimit(&g->setpoint.eulr.yaw,
g->feedback.motor.yaw.rotor_abs_angle,
g->feedback.imu.eulr.yaw,
g->limit.yaw.max, g->limit.yaw.min, M_2PI);
}
if (g->param->travel.pit > 0) {
float pit_motor_imu_offset = g->feedback.motor.pit.rotor_abs_angle - g->feedback.imu.eulr.rol;
if (pit_motor_imu_offset > M_PI) pit_motor_imu_offset -= M_2PI;
if (pit_motor_imu_offset < -M_PI) pit_motor_imu_offset += M_2PI;
/* 将IMU setpoint转换为电机角度后进行限位检查 */
float pit_motor_target = g->setpoint.eulr.pit + pit_motor_imu_offset;
if (CircleError(pit_motor_target, g->limit.pit.max, M_2PI) > 0) {
g->setpoint.eulr.pit = g->limit.pit.max - pit_motor_imu_offset;
}
if (CircleError(pit_motor_target, g->limit.pit.min, M_2PI) < 0) {
g->setpoint.eulr.pit = g->limit.pit.min - pit_motor_imu_offset;
}
CircleAngleLimit(&g->setpoint.eulr.pit,
g->feedback.motor.pit.rotor_abs_angle,
g->feedback.imu.eulr.rol,
g->limit.pit.max, g->limit.pit.min, M_2PI);
}
}
/* fallthrough - AI控制模式也需要执行PID计算 */

View File

@ -62,6 +62,12 @@ void Task_ctrl_chassis(void *argument) {
Chassis_UpdateFeedback(&chassis);
/* 检查跳跃触发 */
if (chassis_cmd.jump_trigger) {
Chassis_TriggerJump(&chassis);
chassis_cmd.jump_trigger = false; /* 清除触发标志 */
}
Chassis_Control(&chassis, &chassis_cmd);
/* USER CODE END */
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */

View File

@ -113,6 +113,20 @@ void Task_rc(void *argument) {
osMessageQueuePut(task_runtime.msgq.gimbal.cmd, &cmd_for_gimbal, 0, 0);
/************************* 底盘命令 **************************************/
/* 跳跃触发检测ch_res 从 -1.0f 松开回 0 时触发 */
static bool ch_res_was_min = false; /* 记录上次是否在最低位置 */
const float CH_RES_MIN_THRESHOLD = -0.9f; /* 判定为最低位置的阈值 */
const float CH_RES_RELEASE_THRESHOLD = -0.3f; /* 判定为松开的阈值 */
cmd_for_chassis.jump_trigger = false; /* 默认不触发 */
if (dr16.data.ch_res < CH_RES_MIN_THRESHOLD) {
ch_res_was_min = true; /* 记录已到达最低位置 */
} else if (ch_res_was_min && dr16.data.ch_res > CH_RES_RELEASE_THRESHOLD) {
/* 从最低位置松开,触发跳跃 */
cmd_for_chassis.jump_trigger = true;
ch_res_was_min = false; /* 重置状态 */
}
switch (dr16.data.sw_l) {
case DR16_SW_UP:
cmd_for_chassis.mode = CHASSIS_MODE_RELAX;