diff --git a/User/device/ai.c b/User/device/ai.c index ad97b28..903f552 100644 --- a/User/device/ai.c +++ b/User/device/ai.c @@ -2,7 +2,12 @@ #include "device/device.h" #include "bsp/uart.h" #include "component/crc16.h" -#include "device/referee.h" +#include "device/referee.h" + +/* ---- 接收缓冲 ---- */ +static uint8_t ai_rx_buf[64]; /* DMA 接收缓冲,略大于 PackageAI_t */ +static AI_result_t ai_parsed; /* 解析后的结果 */ +static volatile uint8_t ai_data_ready = 0; static int8_t Package_BuildAndVerify(uint8_t *raw, uint16_t len, uint8_t id) { uint16_t *crc = NULL; @@ -22,105 +27,119 @@ static int8_t Package_BuildAndVerify(uint8_t *raw, uint16_t len, uint8_t id) { return DEVICE_OK; } -static int8_t Package_Check(const uint8_t *raw, uint16_t len, uint8_t expected_id) { - if (raw == NULL || len <= sizeof(uint16_t)) { - return DEVICE_ERR; +/* IDLE LINE 回调 —— 上位机发完一帧后总线空闲触发 */ +static void AI_IdleLineCallback(void) { + UART_HandleTypeDef *huart = BSP_UART_GetHandle(BSP_UART_1); + + /* 停止DMA,计算实际收到的字节数 */ + HAL_UART_DMAStop(huart); + uint16_t rx_len = sizeof(ai_rx_buf) - __HAL_DMA_GET_COUNTER(huart->hdmarx); + + /* 长度匹配才解析 */ + if (rx_len == sizeof(PackageAI_t)) { + PackageAI_t *pkt = (PackageAI_t *)ai_rx_buf; + + if (pkt->id == ID_AI) { + ai_parsed.mode = pkt->data.mode; + ai_parsed.gimbal_t.setpoint.yaw = pkt->data.yaw; + ai_parsed.gimbal_t.vel.yaw = pkt->data.yaw_vel; + ai_parsed.gimbal_t.accl.yaw = pkt->data.yaw_acc; + ai_parsed.gimbal_t.setpoint.pit = pkt->data.pitch; + ai_parsed.gimbal_t.vel.pit = pkt->data.pitch_vel; + ai_parsed.gimbal_t.accl.pit = pkt->data.pitch_acc; + ai_parsed.chassis_t.Vx = pkt->data.vx; + ai_parsed.chassis_t.Vy = pkt->data.vy; + ai_parsed.chassis_t.Vw = pkt->data.wz; + ai_data_ready = 1; + } } - if (raw[0] != expected_id) { - return DEVICE_ERR; - } + /* 重启DMA接收,等待下一帧 */ + BSP_UART_Receive(BSP_UART_1, ai_rx_buf, sizeof(ai_rx_buf), true); +} - if (CRC16_Verify(raw, len) != true) { - return DEVICE_ERR; - } +/** + * @brief 初始化AI通信,注册IDLE回调并启动DMA接收 + */ +int8_t AI_Init(void) { + UART_HandleTypeDef *huart = BSP_UART_GetHandle(BSP_UART_1); + /* 注册IDLE LINE回调 */ + BSP_UART_RegisterCallback(BSP_UART_1, BSP_UART_IDLE_LINE_CB, + (void (*)(void))AI_IdleLineCallback); + + /* 使能IDLE中断 */ + __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); + + /* 启动第一次DMA接收 */ + BSP_UART_Receive(BSP_UART_1, ai_rx_buf, sizeof(ai_rx_buf), true); return DEVICE_OK; } - -int8_t AI_StartReceiving(PackageAI_t *ai) { - if (BSP_UART_Receive(BSP_UART_1,(uint8_t *)ai,sizeof(*ai), true)==HAL_OK) { - return DEVICE_OK;} - return DEVICE_ERR; +/** + * @brief 获取最新的AI数据(非阻塞,task循环中调用) + * @return DEVICE_OK=有新数据已拷贝, DEVICE_ERR=无新数据 + */ +int8_t AI_GetLatest(AI_result_t *result) { + if (result == NULL) { + return DEVICE_ERR; + } + if (!ai_data_ready) { + return DEVICE_ERR; + } + *result = ai_parsed; + ai_data_ready = 0; + return DEVICE_OK; } -int8_t AI_Get_NUC(PackageAI_t *ai,AI_result_t* result) { - if (ai == NULL || result == NULL) { - return DEVICE_ERR; - } +/* ---- 以下发送函数不变 ---- */ - if (Package_Check((const uint8_t *)ai, sizeof(*ai), ID_AI) != DEVICE_OK) { - return DEVICE_ERR; - } +int8_t MCU_Send(PackageMCU_t *mcu, Gimbal_feedback_t *motor, AHRS_Quaternion_t *quat) { + (void)quat; + if (mcu == NULL || motor == NULL) { + return DEVICE_ERR; + } - result->mode=ai->data.mode; - result->gimbal_t.setpoint.yaw=ai->data.yaw; - result->gimbal_t.vel.yaw=ai->data.yaw_vel; - result->gimbal_t.accl.yaw=ai->data.yaw_acc; - result->gimbal_t.setpoint.pit=ai->data.pitch; - result->gimbal_t.vel.pit=ai->data.pitch_vel; - result->gimbal_t.accl.pit=ai->data.pitch_acc; + mcu->data.mode = 1; + mcu->data.q[0] = motor->imu.quat.q0; + mcu->data.q[1] = motor->imu.quat.q1; + mcu->data.q[2] = motor->imu.quat.q2; + mcu->data.q[3] = motor->imu.quat.q3; + mcu->data.yaw = motor->imu.eulr.yaw; + mcu->data.yaw_vel = motor->imu.gyro.z; + mcu->data.pitch = motor->imu.eulr.rol; + mcu->data.pitch_vel = motor->imu.gyro.x; + mcu->data.bullet_count = 0; + mcu->data.bullet_speed = 22; - result->chassis_t.Vx=ai->data.vx; - result->chassis_t.Vy=ai->data.vy; - result->chassis_t.Vw=ai->data.wz; - - return DEVICE_OK; + return Package_BuildAndVerify((uint8_t *)mcu, sizeof(*mcu), ID_MCU); } +int8_t REF_Send(PackageReferee_t *referee, Referee_RobotStatus_t *robot_status, Referee_GameStatus_t *game_status) { + if (referee == NULL || robot_status == NULL || game_status == NULL) { + return DEVICE_ERR; + } -int8_t MCU_Send(PackageMCU_t* mcu,Gimbal_feedback_t* motor,AHRS_Quaternion_t* quat ){ - (void)quat; - if (mcu == NULL || motor == NULL) { - return DEVICE_ERR; - } + referee->data.remain_hp = robot_status->current_HP; + referee->data.game_progress = game_status->game_progress & 0x0F; + referee->data.stage_remain_time = game_status->stage_remain_time; - mcu->data.mode=1; - mcu->data.q[0]=motor->imu.quat.q0; - mcu->data.q[1]=motor->imu.quat.q1; - mcu->data.q[2]=motor->imu.quat.q2; - mcu->data.q[3]=motor->imu.quat.q3; - mcu->data.yaw=motor->imu.eulr.yaw; - mcu->data.yaw_vel=motor->imu.gyro.z; - mcu->data.pitch=motor->imu.eulr.rol; - mcu->data.pitch_vel=motor->imu.gyro.x; - mcu->data.bullet_count=0; - mcu->data.bullet_speed=22; - - return Package_BuildAndVerify((uint8_t *)mcu, sizeof(*mcu), ID_MCU); -} - -int8_t REF_Send(PackageReferee_t *referee,Referee_RobotStatus_t* robot_status,Referee_GameStatus_t *game_status) { - if (referee == NULL || robot_status == NULL || game_status == NULL) { - return DEVICE_ERR; - } - - referee->data.remain_hp=robot_status->current_HP; - referee->data.game_progress = game_status->game_progress & 0x0F; - referee->data.stage_remain_time= game_status->stage_remain_time; - - return Package_BuildAndVerify((uint8_t *)referee, sizeof(*referee), ID_REF); + return Package_BuildAndVerify((uint8_t *)referee, sizeof(*referee), ID_REF); } int8_t REF_StartSend(PackageReferee_t *referee) { - if (referee == NULL) { - return DEVICE_ERR; - } + if (referee == NULL) { + return DEVICE_ERR; + } - if(BSP_UART_Transmit(BSP_UART_1, (uint8_t *)referee, sizeof(*referee), true)==HAL_OK) - return DEVICE_OK; - return DEVICE_ERR; + if (BSP_UART_Transmit(BSP_UART_1, (uint8_t *)referee, sizeof(*referee), true) == HAL_OK) + return DEVICE_OK; + return DEVICE_ERR; } - int8_t MCU_StartSend(PackageMCU_t *mcu) { - if(BSP_UART_Transmit(BSP_UART_1, (uint8_t *)mcu, sizeof(*mcu), true)==HAL_OK) - return DEVICE_OK; - return DEVICE_ERR; - + if (BSP_UART_Transmit(BSP_UART_1, (uint8_t *)mcu, sizeof(*mcu), true) == HAL_OK) + return DEVICE_OK; + return DEVICE_ERR; } - - - \ No newline at end of file diff --git a/User/device/ai.h b/User/device/ai.h index 938c713..b5493ed 100644 --- a/User/device/ai.h +++ b/User/device/ai.h @@ -15,7 +15,7 @@ extern "C" { // { // uint8_t head[2]; // uint8_t mode; // 0: 空闲, 1: 自瞄, 2: 小符, 3: 大符 -// float q[4]; // wxyz顺序 /q4,q0,q1,q2/ +// float q[4]; // wxyz顺序 /q4,q0,q1,q2/ // float yaw; // float yaw_vel; // float pitch; @@ -39,31 +39,31 @@ extern "C" { // }; // typedef struct __attribute__((packed)) { -// uint8_t mode; -// struct{ -// // Gimbal_CMD_t g_cmd; -// struct{ -// float yaw; -// float pit; -// }setpoint; - - -// struct{ -// float pit; -// float yaw; -// }accl; -// struct{ -// float pit; -// float yaw; -// }vel; -// }gimbal_t; - +// uint8_t mode; +// struct{ +// // Gimbal_CMD_t g_cmd; +// struct{ +// float yaw; +// float pit; +// }setpoint; + + +// struct{ +// float pit; +// float yaw; +// }accl; +// struct{ +// float pit; +// float yaw; +// }vel; +// }gimbal_t; + // }AI_cmd_t; // typedef struct __attribute__((packed)) { -// struct GimbalToVision TX; -// struct VisionToGimbal RX; +// struct GimbalToVision TX; +// struct VisionToGimbal RX; // }AI_t; @@ -134,40 +134,41 @@ typedef struct __attribute__((packed)) } PackageAI_t; typedef struct __attribute__((packed)) { - uint8_t mode; // 0: 不控制, 1: 控制云台但不开火, 2: 控制云台且开火 - struct{ -// Gimbal_CMD_t g_cmd; - struct{ - float yaw; // 目标偏航角 - float pit; // 目标俯仰角 - }setpoint; - - struct{ - float pit; // 俯仰角加速度 - float yaw; // 偏航角加速度 - }accl; - struct{ - float pit; // 俯仰角速度 - float yaw; // 偏航角速度 - }vel; - }gimbal_t; + uint8_t mode; // 0: 不控制, 1: 控制云台但不开火, 2: 控制云台且开火 + struct{ +// Gimbal_CMD_t g_cmd; + struct{ + float yaw; // 目标偏航角 + float pit; // 目标俯仰角 + }setpoint; + + struct{ + float pit; // 俯仰角加速度 + float yaw; // 偏航角加速度 + }accl; + struct{ + float pit; // 俯仰角速度 + float yaw; // 偏航角速度 + }vel; + }gimbal_t; struct{ float Vx; // X 方向速度 float Vy; // Y 方向速度 float Vw; // Z 方向角速度 }chassis_t; - + uint8_t reserved; // 预留字段 }AI_result_t; //接收到的所有数据,来自NUC +int8_t AI_Init(void); +int8_t AI_GetLatest(AI_result_t *result); int8_t MCU_Send(PackageMCU_t* mcu,Gimbal_feedback_t* motor,AHRS_Quaternion_t* quat); int8_t REF_Send(PackageReferee_t *referee,Referee_RobotStatus_t* robot_status,Referee_GameStatus_t *game_status); int8_t REF_StartSend(PackageReferee_t *referee); int8_t MCU_StartSend(PackageMCU_t *mcu); -int8_t AI_Get_NUC(PackageAI_t *ai,AI_result_t* result); -int8_t AI_StartReceiving(PackageAI_t *ai); #ifdef __cplusplus } #endif + diff --git a/User/module/chassis.c b/User/module/chassis.c index 618d71e..55a6141 100644 --- a/User/module/chassis.c +++ b/User/module/chassis.c @@ -448,28 +448,8 @@ int8_t Chassis_Control(Chassis_t *c, Chassis_CMD_t *c_cmd,uint32_t now) // 跟随云台模式 c->move_vec.Vx =-c_cmd->Vy; c->move_vec.Vy =-c_cmd->Vx; - if (fabsf(c->move_vec.Vx) < CHASSIS_FOLLOW_TRANS_DEADBAND) c->move_vec.Vx = 0.0f; - if (fabsf(c->move_vec.Vy) < CHASSIS_FOLLOW_TRANS_DEADBAND) c->move_vec.Vy = 0.0f; - { - const float follow_target = c->mech_zero_4310; - const float follow_err = CircleError(follow_target, - c->motorfeedback.gimbal_yaw_encoder.rotor_abs_angle, - M_2PI); - if (fabsf(follow_err) < CHASSIS_FOLLOW_LOCK_DEADBAND_RAD) { - c->move_vec.Vw = 0.0f; - PID_ResetIntegral(&c->pid.chassis_follow_gimbal_pid); - } else { - float follow_vw = PID_Calc(&c->pid.chassis_follow_gimbal_pid, - follow_target, - c->motorfeedback.gimbal_yaw_encoder.rotor_abs_angle, - 0.0f, - c->dt); - if (fabsf(follow_vw) < CHASSIS_FOLLOW_VW_DEADBAND) { - follow_vw = 0.0f; - } - c->move_vec.Vw = follow_vw; - } - } + c->move_vec.Vw =PID_Calc(&c->pid.chassis_follow_gimbal_pid, 4.51735544f ,c->motorfeedback.gimbal_yaw_encoder.rotor_abs_angle, 0.0f, c->dt); + break; @@ -561,6 +541,7 @@ void Chassis_Setoutput(Chassis_t *c) } // MOTOR_RM_SetOutput(&(c->param->motor_3508_param[0]), 1.0f); MOTOR_RM_Ctrl(&(c->param->motor_3508_param[0])); + MOTOR_RM_Ctrl(&(c->param->motor_3508_param[1])); MOTOR_RM_Ctrl(&(c->param->motor_6020_param[0])); // MOTOR_RM_Ctrl(&(c->param->motor_6020_param[3])); diff --git a/User/module/cmd.c b/User/module/cmd.c index deb7c98..df69bff 100644 --- a/User/module/cmd.c +++ b/User/module/cmd.c @@ -3,6 +3,7 @@ */ #include "module/cmd.h" #include "bsp/time.h" +#include "chassis.h" #include #include @@ -35,8 +36,8 @@ static void CMD_RC_BuildChassisCmd(CMD_t *ctx) { } /* 左摇杆控制移动 */ - ctx->output.chassis.cmd.Vx = ctx->input.rc.joy_left.x; - ctx->output.chassis.cmd.Vy = ctx->input.rc.joy_left.y; + ctx->output.chassis.cmd.Vx = ctx->input.rc.joy_right.x; + ctx->output.chassis.cmd.Vy = ctx->input.rc.joy_right.y; } /* 从RC输入生成云台命令 */ @@ -60,9 +61,9 @@ static void CMD_RC_BuildGimbalCmd(CMD_t *ctx) { } /* 右摇杆控制云台 */ - ctx->output.gimbal.cmd.delta_yaw_6020 = -ctx->input.rc.joy_right.x * 2.0f; - ctx->output.gimbal.cmd.delta_yaw_4310 = -ctx->input.rc.joy_right.x * 1.2f; - ctx->output.gimbal.cmd.delta_pitch_4310 = -ctx->input.rc.joy_right.y * 1.5f; + ctx->output.gimbal.cmd.delta_yaw_6020 = -ctx->input.rc.joy_left.x * 2.0f; + ctx->output.gimbal.cmd.delta_yaw_4310 = -ctx->input.rc.joy_left.x * 1.2f; + ctx->output.gimbal.cmd.delta_pitch_4310 = -ctx->input.rc.joy_left.y * 1.5f; } /* 从RC输入生成射击命令 */ @@ -107,18 +108,18 @@ static void CMD_RC_BuildShootCmd(CMD_t *ctx) { } switch (ctx->input.rc.sw[1]) { case CMD_SW_UP: - ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_RC; - ctx->output.gimbal.cmd.ctrl_mode = GIMBAL_MODE_REMOTE; + ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_RC; + ctx->output.gimbal.cmd.ctrl_mode = GIMBAL_MODE_REMOTE; ctx->output.shoot.cmd.control_mode = SHOOT_REMOTE; break; case CMD_SW_MID: - ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_DAOHANG; - ctx->output.gimbal.cmd.mode = GIMBAL_MODE_DAOHANG; + ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_DAOHANG; + ctx->output.gimbal.cmd.mode = GIMBAL_MODE_DAOHANG; ctx->output.shoot.cmd.control_mode = SHOOT_REMOTE; break; case CMD_SW_DOWN: - ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_RELAX; - ctx->output.gimbal.cmd.ctrl_mode = GIMBAL_MODE_AI; + ctx->output.chassis.cmd.ctrl_mode = CHASSIS_MODE_FOLLOW_GIMBAL; + ctx->output.gimbal.cmd.ctrl_mode = GIMBAL_MODE_SCAN; ctx->output.shoot.cmd.control_mode = SHOOT_AI; break; default: @@ -126,6 +127,12 @@ static void CMD_RC_BuildShootCmd(CMD_t *ctx) { ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX; ctx->output.shoot.cmd.control_mode = SHOOT_REMOTE; break; + } + + /* sw[5] (F拨杆): 下=纯AI自瞄(无扫描), 上/中=保持B拨杆的设定 */ + if (ctx->input.rc.sw[5] == CMD_SW_DOWN) { + ctx->output.gimbal.cmd.ctrl_mode = GIMBAL_MODE_AI; + ctx->output.shoot.cmd.control_mode = SHOOT_AI; } } diff --git a/User/module/config.c b/User/module/config.c index 0e97a96..e1420f8 100644 --- a/User/module/config.c +++ b/User/module/config.c @@ -17,7 +17,7 @@ Config_Param_t config = { .chassis = { .motor_3508_param[0] = {BSP_CAN_1, 0x201, MOTOR_M3508, false, false}, - .motor_3508_param[1] = {BSP_CAN_1, 0x202, MOTOR_M3508, false, false}, + .motor_3508_param[1] = {BSP_CAN_2, 0x204, MOTOR_M3508, false, false}, .motor_3508_param[2] = {BSP_CAN_1, 0x203, MOTOR_M3508, false, false}, .motor_3508_param[3] = {BSP_CAN_1, 0x204, MOTOR_M3508, false, false}, @@ -247,7 +247,7 @@ Config_Param_t config = { .pid.daohang_6020_motor_angle={ .k = 1.5f, - .p = 1.7f, + .p = 1.2f, .i = 0.0f, .d = 0.0f, .i_limit = 1.0f, @@ -257,7 +257,7 @@ Config_Param_t config = { }, .pid.daohang_4310_motor_angle={ .k = 2.0f, - .p = 5.0f, + .p = 2.0f, .i = 1.0f, .d = 0.2f, .i_limit = 1.0f, diff --git a/User/module/gimbal.c b/User/module/gimbal.c index 7934062..bcb7a87 100644 --- a/User/module/gimbal.c +++ b/User/module/gimbal.c @@ -56,20 +56,11 @@ static float g_scan_pitch_dir = 1.0f; static float g_scan_yaw_dir = 1.0f; static int8_t g_scan_yaw_edge_latch = 0; /* -1: min边界锁存, 1: max边界锁存 */ -#define GIMBAL_SCAN_SMALL_YAW_SPEED (0.40f) -#define GIMBAL_SCAN_BIG_YAW_PERIOD_SEC (5.0f) -#define GIMBAL_SCAN_PITCH_SPEED (0.16f) -#define GIMBAL_SCAN_PITCH_AMPLITUDE (0.18f) -#define GIMBAL_SCAN_YAW_EDGE_MARGIN (0.16f) -#define GIMBAL_SCAN_YAW_EDGE_HYST (0.22f) -#define GIMBAL_SCAN_YAW_EDGE_RELEASE (0.28f) -#define GIMBAL_SCAN_PITCH_EDGE_MARGIN (0.12f) -#define GIMBAL_SCAN_EDGE_SLOW_BAND (0.26f) -#define GIMBAL_REMOTE_BIG_YAW_SPEED_GAIN (1.6f) -#define GIMBAL_REMOTE_SMALL_YAW_GAIN (16.0f) -#define GIMBAL_REMOTE_EDGE_ASSIST_BAND (0.20f) -#define GIMBAL_YAW6020_INPUT_DEADBAND (0.0005f) -#define GIMBAL_CONTROL_DT_MAX (0.02f) +#define GIMBAL_SCAN_SMALL_YAW_SPEED (4.0f) /* 扫描模式小YAW旋转速度 (rad/s) */ +#define GIMBAL_SCAN_PITCH_SPEED (3.0f) /* 扫描模式pitch上下扫描速度 (rad/s) */ +#define GIMBAL_SCAN_PITCH_EDGE_MARGIN (0.03f) /* pitch接近限位多远开始反转 (rad) */ + + @@ -232,23 +223,23 @@ int8_t Gimbal_UpdateIMU(Gimbal_t *gimbal, const Gimbal_IMU_t *imu){ return GIMBAL_OK; } -// int8_t Gimbal_Control_mode(Gimbal_t *g,Gimbal_CMD_t *g_cmd){ -// if (g == NULL || g_cmd == NULL) { +// int8_t Gimbal_Control_mode(Gimbal_t *g,Gimbal_CMD_t *g_cmd){ +// if (g == NULL || g_cmd == NULL) { // return -1; // } -// switch(g_cmd->ctrl_mode){ -// case GIMBAL_MODE_REMOTE: -// g->setpoint.eulr.yaw=g_cmd->delta_yaw_6020; -// g->setpoint.eulr.pit=g_cmd->delta_pitch_4310; -// break; -// case GIMBAL_MODE_AI: -// g->setpoint.eulr.yaw=g_cmd->set_yaw; -// g->setpoint.eulr.pit=g_cmd->set_pitch; -// break; -// default: -// break; -// } -// return GIMBAL_OK; +// switch(g_cmd->ctrl_mode){ +// case GIMBAL_MODE_REMOTE: +// g->setpoint.eulr.yaw=g_cmd->delta_yaw_6020; +// g->setpoint.eulr.pit=g_cmd->delta_pitch_4310; +// break; +// case GIMBAL_MODE_AI: +// g->setpoint.eulr.yaw=g_cmd->set_yaw; +// g->setpoint.eulr.pit=g_cmd->set_pitch; +// break; +// default: +// break; +// } +// return GIMBAL_OK; // }; /** @@ -276,171 +267,40 @@ int8_t Gimbal_UpdateIMU(Gimbal_t *gimbal, const Gimbal_IMU_t *imu){ uint64_t now_us = BSP_TIME_Get_us(); g->dt = (now_us - g->last_wakeup) / 1000000.0f; g->last_wakeup = now_us; - if (g->dt < 0.0f || g->dt > GIMBAL_CONTROL_DT_MAX) { - g->dt = 0.002f; - } + } Gimbal_SetMode(g, g_cmd->mode); - if (g_cmd->ctrl_mode == GIMBAL_MODE_SCAN) { - float yaw_speed_scale = 1.0f; - float big_yaw_speed = 0.0f; - float pitch_speed_scale = 1.0f; - float yaw_abs = g->feedback.motor.yaw_6020_motor_feedback.rotor_abs_angle; - float pitch_abs = g->feedback.motor.pitch_4310_motor_feedback.rotor_abs_angle; + /*处理小yaw轴控制命令*/ +float delta_yaw_6020 = 5.0f*g_cmd->delta_yaw_6020 * g->dt; - if (!g_scan_active) { - float yaw_mid = 0.5f * (g->limit.yaw_6020.max + g->limit.yaw_6020.min); - g_scan_active = 1; - g_scan_pitch_center = g->feedback.imu.eulr.rol; - g_scan_pitch_dir = 1.0f; - g_scan_yaw_dir = (yaw_abs >= yaw_mid) ? -1.0f : 1.0f; - g_scan_yaw_edge_latch = 0; - g->setpoint.yaw_4310 = g->feedback.motor.yaw_4310_motor_feedback.rotor_abs_angle; - } +if(g->param->travel.yaw_6020 > 0) // 有限位才处理 +{ +/* 计算当前电机角度与IMU角度的偏差 */ +float motor_imu_offset_6020 = g->feedback.motor.yaw_6020_motor_feedback.rotor_abs_angle - g->feedback.imu.eulr.yaw; +/* 处理跨越±π的情况 */ +if (motor_imu_offset_6020 > M_PI) motor_imu_offset_6020 -= M_2PI; +if (motor_imu_offset_6020 < -M_PI) motor_imu_offset_6020 += M_2PI; +/*计算限位距离*/ +const float delta_max_6020 = CircleError(g->limit.yaw_6020.max, + (g->setpoint.eulr.yaw + motor_imu_offset_6020 + delta_yaw_6020), M_2PI); +const float delta_min_6020 = CircleError(g->limit.yaw_6020.min, + (g->setpoint.eulr.yaw + motor_imu_offset_6020 + delta_yaw_6020), M_2PI); - if (GIMBAL_SCAN_BIG_YAW_PERIOD_SEC > 0.0f) { - /* 连续旋转:按周期计算角速度,5秒转一圈即 2PI/5 */ - big_yaw_speed = M_2PI / GIMBAL_SCAN_BIG_YAW_PERIOD_SEC; - } - - if (g_scan_yaw_edge_latch == 1) { - g_scan_yaw_dir = -1.0f; - if (yaw_abs < g->limit.yaw_6020.max - GIMBAL_SCAN_YAW_EDGE_RELEASE) { - g_scan_yaw_edge_latch = 0; - } - } else if (g_scan_yaw_edge_latch == -1) { - g_scan_yaw_dir = 1.0f; - if (yaw_abs > g->limit.yaw_6020.min + GIMBAL_SCAN_YAW_EDGE_RELEASE) { - g_scan_yaw_edge_latch = 0; - } - } else if (yaw_abs >= g->limit.yaw_6020.max - GIMBAL_SCAN_YAW_EDGE_MARGIN) { - g_scan_yaw_dir = -1.0f; - g_scan_yaw_edge_latch = 1; - } else if (yaw_abs <= g->limit.yaw_6020.min + GIMBAL_SCAN_YAW_EDGE_MARGIN) { - g_scan_yaw_dir = 1.0f; - g_scan_yaw_edge_latch = -1; - } else if (g_scan_yaw_dir > 0.0f && yaw_abs > g->limit.yaw_6020.max - GIMBAL_SCAN_YAW_EDGE_HYST) { - g_scan_yaw_dir = -1.0f; - } else if (g_scan_yaw_dir < 0.0f && yaw_abs < g->limit.yaw_6020.min + GIMBAL_SCAN_YAW_EDGE_HYST) { - g_scan_yaw_dir = 1.0f; - } - - if (pitch_abs >= g->limit.pitch_4310.max - GIMBAL_SCAN_PITCH_EDGE_MARGIN) { - g_scan_pitch_dir = -1.0f; - } else if (pitch_abs <= g->limit.pitch_4310.min + GIMBAL_SCAN_PITCH_EDGE_MARGIN) { - g_scan_pitch_dir = 1.0f; - } - - { - float yaw_dist_to_edge_max = g->limit.yaw_6020.max - yaw_abs; - float yaw_dist_to_edge_min = yaw_abs - g->limit.yaw_6020.min; - float yaw_min_dist = (yaw_dist_to_edge_max < yaw_dist_to_edge_min) ? yaw_dist_to_edge_max : yaw_dist_to_edge_min; - if (yaw_min_dist < GIMBAL_SCAN_EDGE_SLOW_BAND) { - if (yaw_min_dist < 0.0f) yaw_min_dist = 0.0f; - yaw_speed_scale = (yaw_min_dist / GIMBAL_SCAN_EDGE_SLOW_BAND); - } - } - - { - float pitch_dist_to_edge_max = g->limit.pitch_4310.max - pitch_abs; - float pitch_dist_to_edge_min = pitch_abs - g->limit.pitch_4310.min; - float pitch_min_dist = (pitch_dist_to_edge_max < pitch_dist_to_edge_min) ? pitch_dist_to_edge_max : pitch_dist_to_edge_min; - if (pitch_min_dist < GIMBAL_SCAN_EDGE_SLOW_BAND) { - if (pitch_min_dist < 0.0f) pitch_min_dist = 0.0f; - pitch_speed_scale = 0.25f + 0.75f * (pitch_min_dist / GIMBAL_SCAN_EDGE_SLOW_BAND); - } - } - - if (g->feedback.imu.eulr.rol >= g_scan_pitch_center + GIMBAL_SCAN_PITCH_AMPLITUDE) { - g_scan_pitch_dir = -1.0f; - } else if (g->feedback.imu.eulr.rol <= g_scan_pitch_center - GIMBAL_SCAN_PITCH_AMPLITUDE) { - g_scan_pitch_dir = 1.0f; - } - - g_cmd->delta_yaw_6020 = g_scan_yaw_dir * GIMBAL_SCAN_SMALL_YAW_SPEED * yaw_speed_scale; - g_cmd->delta_pitch_4310 = g_scan_pitch_dir * GIMBAL_SCAN_PITCH_SPEED * pitch_speed_scale; - if (g->param->travel.yaw_4310 > 0.0f) { - g->setpoint.yaw_4310 += big_yaw_speed * g->dt; - if (g->setpoint.yaw_4310 > g->limit.yaw_4310.max) g->setpoint.yaw_4310 = g->limit.yaw_4310.max; - if (g->setpoint.yaw_4310 < g->limit.yaw_4310.min) g->setpoint.yaw_4310 = g->limit.yaw_4310.min; - } else { - CircleAdd(&(g->setpoint.yaw_4310), big_yaw_speed * g->dt, M_2PI); - } - g_cmd->yaw_vel = 0.0f; - g_cmd->yaw_accl = 0.0f; - g_cmd->pit_vel = 0.0f; - g_cmd->pit_accl = 0.0f; - } else { - g_scan_active = 0; - g_scan_yaw_dir = 1.0f; - g_scan_yaw_edge_latch = 0; - } - -/*处理小yaw轴控制命令*/ -float yaw_6020_gain = 10.0f; -if (g_cmd->ctrl_mode == GIMBAL_MODE_REMOTE) { - yaw_6020_gain = GIMBAL_REMOTE_SMALL_YAW_GAIN; + if(delta_yaw_6020 > delta_max_6020) delta_yaw_6020 = delta_max_6020; + if(delta_yaw_6020 < delta_min_6020) delta_yaw_6020 = delta_min_6020; } -float delta_yaw_6020 = yaw_6020_gain * g_cmd->delta_yaw_6020 * g->dt; -float yaw_abs_now = g->feedback.motor.yaw_6020_motor_feedback.rotor_abs_angle; -float yaw_abs_target = yaw_abs_now; - -/* 摇杆无输入时抑制微小噪声,避免setpoint慢漂 */ -if (fabsf(delta_yaw_6020) < GIMBAL_YAW6020_INPUT_DEADBAND) { - delta_yaw_6020 = 0.0f; -} - -if (g->param->travel.yaw_6020 > 0.0f) { - /* 边界方向判定:到边界时只允许朝边界内侧运动 */ - if (yaw_abs_now >= g->limit.yaw_6020.max - GIMBAL_SCAN_YAW_EDGE_MARGIN && delta_yaw_6020 > 0.0f) { - delta_yaw_6020 = 0.0f; - } - if (yaw_abs_now <= g->limit.yaw_6020.min + GIMBAL_SCAN_YAW_EDGE_MARGIN && delta_yaw_6020 < 0.0f) { - delta_yaw_6020 = 0.0f; - } - - yaw_abs_target = yaw_abs_now + delta_yaw_6020; - if (yaw_abs_target > g->limit.yaw_6020.max) yaw_abs_target = g->limit.yaw_6020.max; - if (yaw_abs_target < g->limit.yaw_6020.min) yaw_abs_target = g->limit.yaw_6020.min; - - /* 直接用允许的电机角增量映射到欧拉角目标,避免CircleAdd累积导致回弹 */ - g->setpoint.eulr.yaw = g->feedback.imu.eulr.yaw + (yaw_abs_target - yaw_abs_now); -} else { CircleAdd(&(g->setpoint.eulr.yaw), delta_yaw_6020, M_2PI); -} - // g->setpoint.eulr.yaw = g->setpoint.NUC_Yaw; /*处理大yaw控制命令,软件限位 - 使用电机绝对角度*/ /*获得小YAW的中点位置,如果小YAW大于中点的一定范围,大YAW开始运动, 跟随小YAW向相同方向运动,将遥控数据进行分配*/ - if (g_cmd->ctrl_mode == GIMBAL_MODE_REMOTE) { - float delta_yaw_4310 = -GIMBAL_REMOTE_BIG_YAW_SPEED_GAIN * g_cmd->delta_yaw_4310 * g->dt; - if (g->param->travel.yaw_6020 > 0.0f) { - float yaw_abs_now = g->feedback.motor.yaw_6020_motor_feedback.rotor_abs_angle; - float dist_to_max = g->limit.yaw_6020.max - yaw_abs_now; - float dist_to_min = yaw_abs_now - g->limit.yaw_6020.min; - float edge_assist = 1.0f; - if (g_cmd->delta_yaw_6020 > 0.0f && dist_to_max < GIMBAL_REMOTE_EDGE_ASSIST_BAND) { - edge_assist += (GIMBAL_REMOTE_EDGE_ASSIST_BAND - dist_to_max) / GIMBAL_REMOTE_EDGE_ASSIST_BAND; - } else if (g_cmd->delta_yaw_6020 < 0.0f && dist_to_min < GIMBAL_REMOTE_EDGE_ASSIST_BAND) { - edge_assist += (GIMBAL_REMOTE_EDGE_ASSIST_BAND - dist_to_min) / GIMBAL_REMOTE_EDGE_ASSIST_BAND; - } - if (edge_assist < 1.0f) edge_assist = 1.0f; - if (edge_assist > 2.0f) edge_assist = 2.0f; - delta_yaw_4310 *= edge_assist; - } - if (g->param->travel.yaw_4310 > 0.0f) { - g->setpoint.yaw_4310 += delta_yaw_4310; - if (g->setpoint.yaw_4310 > g->limit.yaw_4310.max) g->setpoint.yaw_4310 = g->limit.yaw_4310.max; - if (g->setpoint.yaw_4310 < g->limit.yaw_4310.min) g->setpoint.yaw_4310 = g->limit.yaw_4310.min; - } else { - CircleAdd(&(g->setpoint.yaw_4310), delta_yaw_4310, M_2PI); - } - } + /*小YAW中点*/ + float small_yaw_center_offset = g->param->mech_zero.yaw_6020 + g->param->travel.yaw_6020* 0.5f ; + g->setpoint.yaw_4310=small_yaw_center_offset; /*处理大pitch控制命令*/ float delta_pitch_4310 = 8.0f*g_cmd->delta_pitch_4310*g->dt; @@ -479,8 +339,28 @@ switch (g_cmd->ctrl_mode) { break; case GIMBAL_MODE_SCAN: - g->setpoint.eulr.yaw = g->setpoint.eulr.yaw; - g->setpoint.eulr.pit = g->setpoint.eulr.pit; +{ + /* 扫描模式:小YAW持续旋转带动大YAW转动,pitch在限位间来回扫描 */ + + /* --- 小YAW持续旋转 --- */ + float scan_yaw_delta = GIMBAL_SCAN_SMALL_YAW_SPEED * g->dt; + CircleAdd(&(g->setpoint.eulr.yaw), scan_yaw_delta, M_2PI); + + /* --- Pitch增量式来回扫描 --- */ + float pitch_pos = g->feedback.motor.pitch_4310_motor_feedback.rotor_abs_angle; + + /* 到达下限位(1.83)附近,反转向上 */ + if (pitch_pos >= (g->limit.pitch_4310.max - GIMBAL_SCAN_PITCH_EDGE_MARGIN)) { + g_scan_pitch_dir = -1.0f; + } + /* 到达上限位(0.97)附近,反转向下 */ + if (pitch_pos <= 1.20f) { + g_scan_pitch_dir = 1.0f; + } + + float scan_pitch_delta = GIMBAL_SCAN_PITCH_SPEED * g_scan_pitch_dir * g->dt; + CircleAdd(&(g->setpoint.eulr.pit), scan_pitch_delta, M_2PI); +} break; case GIMBAL_MODE_AI: @@ -603,3 +483,4 @@ void Gimbal_Output(Gimbal_t *g) + diff --git a/User/module/gimbal.h b/User/module/gimbal.h index 16a6ed9..ada187c 100644 --- a/User/module/gimbal.h +++ b/User/module/gimbal.h @@ -125,6 +125,7 @@ typedef struct { float delta_pitch_4310; float delta_yaw_6020; GIMBAL_Ctrl_mode_t ctrl_mode; + uint8_t scan_enable; /* 1=AI无目标时扫描, 0=AI无目标时保持当前姿态 */ float set_yaw; /*自瞄YAW目标值*/ float set_pitch;/*自瞄PITCH目标值*/ float yaw_vel; /*自瞄YAW角速度*/ diff --git a/User/task/ai.c b/User/task/ai.c index 2a64c14..c2d47c0 100644 --- a/User/task/ai.c +++ b/User/task/ai.c @@ -1,6 +1,5 @@ /* ai Task - */ /* Includes ----------------------------------------------------------------- */ @@ -10,60 +9,55 @@ #include "module/gimbal.h" /* USER INCLUDE END */ -/* Private typedef ---------------------------------------------------------- */ -/* Private define ----------------------------------------------------------- */ -/* Private macro ------------------------------------------------------------ */ /* Private variables -------------------------------------------------------- */ -/* USER STRUCT BEGIN */ -PackageAI_t ai; PackageMCU_t mcu; PackageReferee_t ref_pkg; AI_result_t ai_result; Referee_ForAI_t ref_for_ai; AHRS_Quaternion_t quat; Gimbal_feedback_t gimbal_motor; -PackageMCU_t shoot_mcu_package; /* 新增的 ai 数据包 主要是给自瞄发送射击数量*/ -/* USER STRUCT END */ +PackageMCU_t shoot_mcu_package; -/* Private function --------------------------------------------------------- */ /* Exported functions ------------------------------------------------------- */ void Task_ai(void *argument) { - (void)argument; /* 未使用argument,消除警告 */ + (void)argument; - - /* 计算任务运行到指定频率需要等待的tick数 */ const uint32_t delay_tick = osKernelGetTickFreq() / AI_FREQ; + osDelay(AI_INIT_DELAY); - osDelay(AI_INIT_DELAY); /* 延时一段时间再开启任务 */ + /* 初始化AI通信:注册IDLE回调 + 启动DMA接收 */ + AI_Init(); - uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */ - /* USER CODE INIT BEGIN */ + uint32_t tick = osKernelGetTickCount(); - /* USER CODE INIT END */ - while (1) { - tick += delay_tick; /* 计算下一个唤醒时刻 */ - /* USER CODE BEGIN */ - osMessageQueueGet(task_runtime.msgq.gimbal.ai.quat, &quat, NULL, 0); - osMessageQueueGet(task_runtime.msgq.gimbal.ai.feedback, &gimbal_motor, NULL, 0); - osMessageQueueGet(task_runtime.msgq.shoot.ai.s_cmd_ai_bool_count, &shoot_mcu_package, NULL, 0); - osMessageQueueGet(task_runtime.msgq.referee.ai, &ref_for_ai, NULL, 0); - MCU_Send(&mcu,&gimbal_motor,&quat); - mcu.data.bullet_count=shoot_mcu_package.data.bullet_count; /* 从 shoot 任务获取射击数量并放入 mcu 数据包中 */ - MCU_StartSend(&mcu); + tick += delay_tick; - if (REF_Send(&ref_pkg, &ref_for_ai.robot_status, &ref_for_ai.game_status) == DEVICE_OK) { + /* 获取其他任务传来的数据 */ + osMessageQueueGet(task_runtime.msgq.gimbal.ai.quat, &quat, NULL, 0); + osMessageQueueGet(task_runtime.msgq.gimbal.ai.feedback, &gimbal_motor, NULL, 0); + osMessageQueueGet(task_runtime.msgq.shoot.ai.s_cmd_ai_bool_count, &shoot_mcu_package, NULL, 0); + osMessageQueueGet(task_runtime.msgq.referee.ai, &ref_for_ai, NULL, 0); + + /* 发送MCU数据给上位机 */ + MCU_Send(&mcu, &gimbal_motor, &quat); + mcu.data.bullet_count = shoot_mcu_package.data.bullet_count; + MCU_StartSend(&mcu); + + /* 发送裁判系统数据给上位机 */ + if (REF_Send(&ref_pkg, &ref_for_ai.robot_status, &ref_for_ai.game_status) == 0) { REF_StartSend(&ref_pkg); } - - AI_StartReceiving(&ai); - AI_Get_NUC(&ai,&ai_result); + + /* 非阻塞获取上位机最新数据(IDLE回调已在中断中解析) */ + AI_GetLatest(&ai_result); + + /* 分发AI结果给各任务 */ osMessageQueueReset(task_runtime.msgq.gimbal.ai.g_cmd); - osMessageQueuePut(task_runtime.msgq.gimbal.ai.g_cmd, &ai_result, 0, 0); + osMessageQueuePut(task_runtime.msgq.gimbal.ai.g_cmd, &ai_result, 0, 0); osMessageQueuePut(task_runtime.msgq.shoot.ai.s_cmd, &ai_result, 0, 0); - osMessageQueuePut(task_runtime.msgq.navi.c_cmd, &ai_result, 0, 0); /* 将 ai_result 中的射击命令布尔值发送给 shoot 任务 */ - /* USER CODE END */ - osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */ + osMessageQueuePut(task_runtime.msgq.navi.c_cmd, &ai_result, 0, 0); + + osDelayUntil(tick); } - -} \ No newline at end of file +} diff --git a/User/task/chassis.c b/User/task/chassis.c index c20b325..322d1a5 100644 --- a/User/task/chassis.c +++ b/User/task/chassis.c @@ -49,7 +49,7 @@ chassis_init(&chassis,&Config_GetRobotParam()->chassis,CHASSIS_FREQ); osMessageQueueGet(task_runtime.msgq.navi.c_cmd, &c_cmd_ai_result, NULL, 0); - osMessageQueueGet(task_runtime.msgq.gimbal.yaw6020,&chassis.motorfeedback.gimbal_yaw_encoder,NULL,0); + osMessageQueueGet(task_runtime.msgq.gimbal.yaw4310,&chassis.motorfeedback.gimbal_yaw_encoder,NULL,0); /*接受cmd任务数据*/ if(osMessageQueueGet(task_runtime.msgq.chassis.cmd, &cmd_chassis, NULL, 0)==osOK) { diff --git a/User/task/gimbal.c b/User/task/gimbal.c index 750f78b..d4b8051 100644 --- a/User/task/gimbal.c +++ b/User/task/gimbal.c @@ -48,16 +48,16 @@ void Task_gimbal(void *argument) { Gimbal_UpdateIMU(&gimbal, &gimbal_imu); osMessageQueueGet(task_runtime.msgq.gimbal.ai.g_cmd,&ai_gimbal_result_cmd,NULL, 0); - /* ai指令 */ - // if(osMessageQueueGet(task_runtime.msgq.gimbal.ai.g_cmd, &ai_gimbal_result_cmd, NULL, 0)==osOK){ - // if(ai_gimbal_result_cmd.mode==0){ + /* ai指令 */ + // if(osMessageQueueGet(task_runtime.msgq.gimbal.ai.g_cmd, &ai_gimbal_result_cmd, NULL, 0)==osOK){ + // if(ai_gimbal_result_cmd.mode==0){ // final_gimbal_cmd.set_pitch =gimbal_imu.eulr.rol; - // final_gimbal_cmd.set_yaw=gimbal_imu.eulr.yaw; - // } - // if(ai_gimbal_cmd.mode==2){ + // final_gimbal_cmd.set_yaw=gimbal_imu.eulr.yaw; + // } + // if(ai_gimbal_cmd.mode==2){ // final_gimbal_cmd.set_pitch=ai_gimbal_cmd.gimbal_t.setpoint.pit; // final_gimbal_cmd.set_yaw=ai_gimbal_cmd.gimbal_t.setpoint.yaw; - // } + // } // } @@ -66,9 +66,15 @@ osMessageQueueGet(task_runtime.msgq.gimbal.cmd, &cmd_gimbal, NULL, 0); if (cmd_gimbal.ctrl_mode == GIMBAL_MODE_AI) { if (ai_gimbal_result_cmd.mode == 0) { + /* AI无目标 */ cmd_gimbal.mode = GIMBAL_MODE_ABSOLUTE; - cmd_gimbal.ctrl_mode = GIMBAL_MODE_SCAN; + if (cmd_gimbal.scan_enable) { + cmd_gimbal.ctrl_mode = GIMBAL_MODE_SCAN; /* 有扫描:回退到扫描模式 */ + } else { + cmd_gimbal.ctrl_mode = GIMBAL_MODE_REMOTE; /* 无扫描:保持当前姿态不动 */ + } } else { + /* AI有目标,填充自瞄数据 */ cmd_gimbal.mode = GIMBAL_MODE_ABSOLUTE; cmd_gimbal.ctrl_mode = GIMBAL_MODE_AI; cmd_gimbal.set_pitch = ai_gimbal_result_cmd.gimbal_t.setpoint.pit; @@ -82,9 +88,9 @@ if (cmd_gimbal.ctrl_mode == GIMBAL_MODE_AI) { Gimbal_UpdateFeedback(&gimbal); -osMessageQueueReset(task_runtime.msgq.gimbal.yaw6020); +osMessageQueueReset(task_runtime.msgq.gimbal.yaw4310); /* 底盘跟随统一使用大YAW反馈,避免跟随锁到小YAW */ -osMessageQueuePut(task_runtime.msgq.gimbal.yaw6020, &gimbal.feedback.motor.yaw_4310_motor_feedback, 0, 0); +osMessageQueuePut(task_runtime.msgq.gimbal.yaw4310, &gimbal.feedback.motor.yaw_4310_motor_feedback, 0, 0); osMessageQueueReset(task_runtime.msgq.gimbal.ai.feedback); osMessageQueuePut(task_runtime.msgq.gimbal.ai.feedback, &gimbal.feedback, 0, 0); @@ -99,4 +105,4 @@ Gimbal_Output(&gimbal); osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */ } -} \ No newline at end of file +} diff --git a/User/task/init.c b/User/task/init.c index b9d23e3..d6091be 100644 --- a/User/task/init.c +++ b/User/task/init.c @@ -60,6 +60,7 @@ void Task_Init(void *argument) { task_runtime.msgq.gimbal.imu = osMessageQueueNew(2u, sizeof(Gimbal_IMU_t), NULL); task_runtime.msgq.gimbal.cmd = osMessageQueueNew(2u, sizeof(Gimbal_CMD_t), NULL); task_runtime.msgq.gimbal.yaw6020 = osMessageQueueNew(2u, sizeof(MOTOR_Feedback_t), NULL); + task_runtime.msgq.gimbal.yaw4310 = osMessageQueueNew(2u, sizeof(MOTOR_Feedback_t), NULL); task_runtime.msgq.shoot.cmd = osMessageQueueNew(2u, sizeof(Shoot_CMD_t),NULL); task_runtime.msgq.gimbal.ai.feedback = osMessageQueueNew(2u, sizeof(Gimbal_feedback_t),NULL); task_runtime.msgq.gimbal.ai.g_cmd = osMessageQueueNew(2u, sizeof(AI_result_t),NULL); diff --git a/User/task/user_task.h b/User/task/user_task.h index 4495933..24a3696 100644 --- a/User/task/user_task.h +++ b/User/task/user_task.h @@ -87,6 +87,7 @@ typedef struct { osMessageQueueId_t imu; osMessageQueueId_t cmd; osMessageQueueId_t yaw6020; /* 新增的 yaw_6020 消息队列 主要是给底盘传6020位置的反馈*/ + osMessageQueueId_t yaw4310; /* 大YAW 4310反馈,给底盘跟随用 */ struct{ osMessageQueueId_t quat; osMessageQueueId_t feedback;