From 11ee01e9f935fd20b9d8cde76e8fea9ec554d2c3 Mon Sep 17 00:00:00 2001 From: "zzzhkgs@gmail.com" Date: Thu, 12 Mar 2026 18:39:41 +0800 Subject: [PATCH] modified: MDK-ARM/.vscode/keil-assistant.log modified: User/task/ai.c modified: User/task/atti_esti.c --- MDK-ARM/.vscode/keil-assistant.log | 8 ++ User/task/ai.c | 200 +++++++++++++++++++++++++---- User/task/atti_esti.c | 7 + 3 files changed, 188 insertions(+), 27 deletions(-) diff --git a/MDK-ARM/.vscode/keil-assistant.log b/MDK-ARM/.vscode/keil-assistant.log index f375d3f..37ed132 100644 --- a/MDK-ARM/.vscode/keil-assistant.log +++ b/MDK-ARM/.vscode/keil-assistant.log @@ -4,3 +4,11 @@ [info] Log at : 2026/3/8|18:16:24|GMT+0800 +[info] Log at : 2026/3/9|19:40:56|GMT+0800 + +[info] Log at : 2026/3/10|14:01:43|GMT+0800 + +[info] Log at : 2026/3/11|12:22:50|GMT+0800 + +[info] Log at : 2026/3/12|15:59:31|GMT+0800 + diff --git a/User/task/ai.c b/User/task/ai.c index 0a330f7..942bcac 100644 --- a/User/task/ai.c +++ b/User/task/ai.c @@ -1,44 +1,190 @@ /* ai Task - + 功能: + 1. 接收姿态任务 (atti_esti) 发送的四元数。 + 2. 直接读取全局云台(gimbal)与发射机构(shoot)状态,打包发送给上位机。 + 3. 解析上位机指令并转化为系统的云台与发射控制信号。 */ /* Includes ----------------------------------------------------------------- */ #include "task/user_task.h" /* USER INCLUDE BEGIN */ - +#include +#include +#include "bsp/uart.h" +#include "component/crc16.h" +#include "module/gimbal.h" /* 引用云台结构体获取反馈 */ +#include "module/shoot.h" /* 引用发射机构结构体获取反馈 */ /* USER INCLUDE END */ /* Private typedef ---------------------------------------------------------- */ -/* Private define ----------------------------------------------------------- */ -/* Private macro ------------------------------------------------------------ */ +#define ID_MCU (0xC4) // MCU 数据包 (发送) +#define ID_REF (0xA8) // 裁判系统数据包 (发送) +#define ID_AI (0xB5) // AI 控制数据包 (接收) + +// MCU 数据结构(MCU -> 上位机) +typedef struct __attribute__((packed)) { + uint8_t mode; // 0: 空闲, 1: 自瞄, 2: 小符, 3: 大符 + float q[4]; // 四元数 wxyz 顺序 + float yaw; // 偏航角 + float yaw_vel; // 偏航角速度 + float pitch; // 俯仰角 + float pitch_vel; // 俯仰角速度 + float bullet_speed; // 弹速 + uint16_t bullet_count; // 子弹累计发送次数 +} DataMCU_t; + +typedef struct __attribute__((packed)) { + uint8_t id; // 数据包 ID: 0xC4 + DataMCU_t data; + uint16_t crc16; +} PackageMCU_t; + +// AI 控制数据结构(上位机 -> MCU) +typedef struct __attribute__((packed)) { + uint8_t mode; // 0: 不控制, 1: 控制云台但不开火, 2: 控制云台且开火 + float yaw; // 目标偏航角/偏移量 + float yaw_vel; // 偏航角速度 + float yaw_acc; // 偏航角加速度 + float pitch; // 目标俯仰角/偏移量 + float pitch_vel; // 俯仰角速度 + float pitch_acc; // 俯仰角加速度 + float vx; // X 方向速度 + float vy; // Y 方向速度 + float wz; // Z 方向角速度 + uint8_t reserved; // 预留字段 +} DataAI_t; + +typedef struct __attribute__((packed)) { + uint8_t id; // 数据包 ID: 0xB5 + DataAI_t data; + uint16_t crc16; +} PackageAI_t; + /* Private variables -------------------------------------------------------- */ -/* USER STRUCT BEGIN */ +static uint8_t ai_rx_buf[sizeof(PackageAI_t)]; +static PackageAI_t ai_rx_data; -/* USER STRUCT END */ +/* 声明外部全局变量,用于直接获取高频反馈数据 */ +extern Gimbal_t gimbal; +extern Shoot_t shoot; + +/* USER FUNCTION BEGIN */ + +/** + * @brief AI串口接收完成回调函数 + * @note 验证包头ID与CRC16校验,校验通过后更新控制指令,并重新开启DMA接收 + */ +static void AI_RxCpltCallback(void) { + if (ai_rx_buf[0] == ID_AI) { + /* 校验 CRC,确保数据包完整 */ + if (CRC16_Verify(ai_rx_buf, sizeof(PackageAI_t))) { + memcpy(&ai_rx_data, ai_rx_buf, sizeof(PackageAI_t)); + + Gimbal_CMD_t ai_gimbal_cmd; + Shoot_CMD_t ai_shoot_cmd; + + /* 1. 解析指令并生成系统控制信号 */ + switch (ai_rx_data.data.mode) { + case 0: /* 不控制 */ + ai_gimbal_cmd.mode = GIMBAL_MODE_RELAX; + // ai_shoot_cmd.mode = SHOOT_MODE_STOP; /* 需根据实际枚举名称修改 */ + break; + case 1: /* 控制云台但不开火 */ + ai_gimbal_cmd.mode = GIMBAL_MODE_ABSOLUTE; + // ai_shoot_cmd.mode = SHOOT_MODE_READY; /* 摩擦轮开启,不拨弹 */ + break; + case 2: /* 控制云台且开火 */ + ai_gimbal_cmd.mode = GIMBAL_MODE_ABSOLUTE; + // ai_shoot_cmd.mode = SHOOT_MODE_FIRE; /* 摩擦轮开启,拨弹 */ + break; + default: + ai_gimbal_cmd.mode = GIMBAL_MODE_RELAX; + break; + } + + /* 云台位姿控制信号赋值 (假设协议中发下来的 yaw/pitch 直接作为速度增量) */ + ai_gimbal_cmd.delta_yaw = ai_rx_data.data.yaw; + ai_gimbal_cmd.delta_pit = ai_rx_data.data.pitch; + + /* 2. 将命令压入控制队列(非阻塞) */ + osMessageQueuePut(msgq.gimbal.cmd, &ai_gimbal_cmd, 0, 0); + + // 注意:若 ctrl_shoot.c 中使用的是 task_runtime.msgq.shoot.shoot_cmd, + // 则替换为该全局结构体的队列句柄。此处采用常规 msgq 定义示范: + osMessageQueuePut(msgq.shoot.shoot_cmd, &ai_shoot_cmd, 0, 0); + } + } + /* 重新开启DMA定长接收 */ + BSP_UART_Receive(BSP_UART_AI, ai_rx_buf, sizeof(PackageAI_t), true); +} + +/** + * @brief 向上位机发送MCU状态数据 + */ +static void AI_Send_MCU_Data(void) { + PackageMCU_t tx_pkg; + quat_t current_quat; + + tx_pkg.id = ID_MCU; + memset(&tx_pkg.data, 0, sizeof(DataMCU_t)); + + /* 1. 获取四元数 (从消息队列读取) */ + if (osMessageQueueGet(msgq.ai.quat, ¤t_quat, NULL, 0) == osOK) { + tx_pkg.data.q[0] = current_quat.q0; + tx_pkg.data.q[1] = current_quat.q1; + tx_pkg.data.q[2] = current_quat.q2; + tx_pkg.data.q[3] = current_quat.q3; + } + + /* 2. 获取云台状态 (直接读取全局 gimbal 结构体) */ + tx_pkg.data.yaw = gimbal.feedback.imu.eulr.yaw; + tx_pkg.data.yaw_vel = gimbal.feedback.imu.gyro.z; + tx_pkg.data.pitch = gimbal.feedback.imu.eulr.rol; /* 电控中 Pitch 对应 IMU 的 Rol */ + tx_pkg.data.pitch_vel = gimbal.feedback.imu.gyro.y; + + /* 3. 获取发射机构与模式状态 (直接读取全局 shoot 结构体) */ + /* 模式回传,取值为实际枚举映射 */ + tx_pkg.data.mode = (uint8_t)shoot.mode; + + /* 弹速反馈:由于硬件可能无法直接测弹速,暂用摩擦轮目标转速或估算转速代替。 + 若接入了裁判系统,应替换为裁判系统下发的真实弹速。 */ + tx_pkg.data.bullet_speed = shoot.target_variable.target_rpm; + + /* 弹量统计:如有独立计数变量,可进行赋值 */ + // tx_pkg.data.bullet_count = shoot.feedback.xxxx; + + /* 4. 计算 CRC16(扣除末尾 2 字节) */ + tx_pkg.crc16 = CRC16_Calc((const uint8_t *)&tx_pkg, sizeof(PackageMCU_t) - 2, CRC16_INIT); + + /* 5. 通过 DMA 发送至上位机 */ + BSP_UART_Transmit(BSP_UART_AI, (uint8_t *)&tx_pkg, sizeof(PackageMCU_t), true); +} + +/* USER FUNCTION END */ -/* 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); + uint32_t tick = osKernelGetTickCount(); - /* 计算任务运行到指定频率需要等待的tick数 */ - const uint32_t delay_tick = osKernelGetTickFreq() / AI_FREQ; - - osDelay(AI_INIT_DELAY); /* 延时一段时间再开启任务 */ - - uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */ - /* USER CODE INIT BEGIN */ - - /* USER CODE INIT END */ - - while (1) { - tick += delay_tick; /* 计算下一个唤醒时刻 */ - /* USER CODE BEGIN */ - //测试 - /* USER CODE END */ - osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */ - } + /* USER CODE INIT BEGIN */ + /* 注册串口接收完成回调并启动第一次DMA接收 */ + BSP_UART_RegisterCallback(BSP_UART_AI, BSP_UART_RX_CPLT_CB, AI_RxCpltCallback); + BSP_UART_Receive(BSP_UART_AI, ai_rx_buf, sizeof(PackageAI_t), true); + /* USER CODE INIT END */ + while (1) { + tick += delay_tick; + + /* USER CODE BEGIN */ + /* 定期向上位机发送包含姿态、云台、射击反馈的 MCU 状态包 */ + AI_Send_MCU_Data(); + /* USER CODE END */ + + osDelayUntil(tick); + } } \ No newline at end of file diff --git a/User/task/atti_esti.c b/User/task/atti_esti.c index b6a880c..18434bc 100644 --- a/User/task/atti_esti.c +++ b/User/task/atti_esti.c @@ -83,7 +83,14 @@ void Task_atti_esti(void *argument) { /* 根据解析出来的四元数计算欧拉角 */ AHRS_GetEulr(&eulr_to_send, &gimbal_ahrs); osKernelUnlock(); + // 将四元数发送至 AI 任务的消息队列 + // &gimbal_ahrs.quat: 四元数结构体地址 + // 0: 消息优先级 + // 0: 不等待,如果队列满了直接跳过(保证实时性) + osMessageQueuePut(msgq.ai.quat, &gimbal_ahrs.quat, 0, 0); + // 如果 AI 任务还需要欧拉角,也可以发送欧拉角队列 + osMessageQueuePut(msgq.ai.eulr, &eulr_to_send, 0, 0); BSP_PWM_SetComp(BSP_PWM_IMU_HEAT_PWM, PID_Calc(&imu_temp_ctrl_pid, 40.5f, bmi088.temp, 0.0f, 0.0f));