modified: MDK-ARM/DevC.uvoptx

modified:   MDK-ARM/DevC/DevC.axf
	modified:   MDK-ARM/DevC/DevC.hex
	modified:   User/module/config.c
	modified:   User/module/config.h
	modified:   User/module/gimbal.c
	modified:   User/module/gimbal.h
	modified:   User/module/shoot.c
	modified:   User/task/ai.c
	modified:   User/task/atti_esti.c
	modified:   User/task/ctrl_gimbal.c
	modified:   User/task/ctrl_shoot.c
	modified:   User/task/rc.c
	modified:   User/task/user_task.h
This commit is contained in:
zzzhkgs@gmail.com 2026-03-12 20:52:24 +08:00
parent 11ee01e9f9
commit 3ec16b82bc
14 changed files with 5730 additions and 5512 deletions

View File

@ -887,7 +887,7 @@
<GroupNumber>7</GroupNumber>
<FileNumber>55</FileNumber>
<FileType>1</FileType>
<tvExp>1</tvExp>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c</PathWithFileName>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,8 @@
/* Private variables -------------------------------------------------------- */
/* Exported variables ------------------------------------------------------- */
/* 全局电机调试使能标志位1正常输出0强制禁用 */
volatile uint8_t g_debug_motor_enable = 0;
/**
* @brief
* @note

View File

@ -12,6 +12,8 @@ extern "C" {
#include <stdbool.h>
#include "module/shoot.h"
#include "module/gimbal.h"
extern volatile uint8_t g_debug_motor_enable;
// #include "device/vision_bridge.h"
/**
* @brief

View File

@ -11,7 +11,7 @@
#include "component/pid.h"
#include "device/motor_dm.h"
#include "device/motor_rm.h"
#include "module/config.h"
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
@ -42,13 +42,7 @@ static int8_t Gimbal_SetMode(Gimbal_t *g, Gimbal_Mode_t mode) {
MOTOR_DM_Enable(&(g->param->pit_motor));
AHRS_ResetEulr(&(g->setpoint.eulr)); /* 切换模式后重置设定值 */
// if (g->mode == GIMBAL_MODE_RELAX) {
// if (mode == GIMBAL_MODE_ABSOLUTE) {
// g->setpoint.eulr.yaw = g->feedback.imu.eulr.yaw;
// } else if (mode == GIMBAL_MODE_RELATIVE) {
// g->setpoint.eulr.yaw = g->feedback.imu.eulr.yaw;
// }
// }
g->setpoint.eulr.pit = g->feedback.imu.eulr.rol;
g->setpoint.eulr.yaw = g->feedback.imu.eulr.yaw;
@ -159,8 +153,21 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) {
Gimbal_SetMode(g, g_cmd->mode);
/* 处理yaw控制命令软件限位 - 使用电机绝对角度 */
float delta_yaw = g_cmd->delta_yaw * g->dt * 1.5f;
/* --- 计算目标增量:兼容手动模式与火控托管模式 --- */
float delta_yaw = 0.0f;
float delta_pit = 0.0f;
if (g->mode == GIMBAL_MODE_AUTO_AIM && g_cmd->vision.target_found) {
/* 火控模式且目标已锁定:计算视觉目标角度与当前设定值的偏差 */
delta_yaw = CircleError(g_cmd->vision.yaw, g->setpoint.eulr.yaw, M_2PI);
delta_pit = CircleError(g_cmd->vision.pit, g->setpoint.eulr.pit, M_2PI);
} else {
/* 常规模式或火控模式下目标丢失:使用操作手遥控器增量 */
delta_yaw = g_cmd->delta_yaw * g->dt * 1.5f;
delta_pit = g_cmd->delta_pit * g->dt;
}
/* --- 处理yaw控制命令软件限位 - 使用电机绝对角度 --- */
if (g->param->travel.yaw > 0) {
/* 计算当前电机角度与IMU角度的偏差 */
float motor_imu_offset = g->feedback.motor.yaw.rotor_abs_angle - g->feedback.imu.eulr.yaw;
@ -190,8 +197,7 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) {
}
CircleAdd(&(g->setpoint.eulr.yaw), delta_yaw, M_2PI);
/* 处理pitch控制命令软件限位 - 使用电机绝对角度 */
float delta_pit = g_cmd->delta_pit * g->dt;
/* --- 处理pitch控制命令软件限位 - 使用电机绝对角度 --- */
if (g->param->travel.pit > 0) {
/* 计算当前电机角度与IMU角度的偏差 */
float motor_imu_offset = g->feedback.motor.pit.rotor_abs_angle - g->feedback.imu.eulr.rol;
@ -211,7 +217,7 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) {
}
CircleAdd(&(g->setpoint.eulr.pit), delta_pit, M_2PI);
/* 控制相关逻辑 */
/* --- 控制相关逻辑PID闭环计算 --- */
float yaw_omega_set_point, pit_omega_set_point;
switch (g->mode) {
case GIMBAL_MODE_RELAX:
@ -219,10 +225,11 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) {
g->out.pit = 0.0f;
break;
case GIMBAL_MODE_AUTO_AIM: /* 火控模式与绝对模式共用相同的空间系PID闭环 */
case GIMBAL_MODE_ABSOLUTE:
yaw_omega_set_point = PID_Calc(&(g->pid.yaw_angle), g->setpoint.eulr.yaw,
g->feedback.imu.eulr.yaw, 0.0f, g->dt);
g->out.yaw = PID_Calc(&(g->pid.pit_omega), yaw_omega_set_point,
g->out.yaw = PID_Calc(&(g->pid.yaw_omega), yaw_omega_set_point,
g->feedback.imu.gyro.z, 0.f, g->dt);
pit_omega_set_point = PID_Calc(&(g->pid.pit_angle), g->setpoint.eulr.pit,
@ -252,10 +259,21 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) {
* \param out CAN设备云台输出结构体
*/
void Gimbal_Output(Gimbal_t *g){
if (g_debug_motor_enable) {
/* 标志位为1正常工作时的输出逻辑 */
MOTOR_RM_SetOutput(&g->param->yaw_motor, g->out.yaw);
MOTOR_MIT_Output_t output = {0};
output.torque = g->out.pit * 3.0f; // 乘以减速比
output.kd = 0.3f;
MOTOR_RM_Ctrl(&g->param->yaw_motor);
MOTOR_DM_MITCtrl(&g->param->pit_motor, &output);
} else {
/* 标志位为0强制禁用逻辑避免断线报错 */
MOTOR_RM_SetOutput(&g->param->yaw_motor, 0.0f); // 大疆电机强制0电流
MOTOR_MIT_Output_t output = {0}; // 达妙MIT模式全零初始化 (位置/速度/扭矩/kp/kd 均为0)
MOTOR_RM_Ctrl(&g->param->yaw_motor);
MOTOR_DM_MITCtrl(&g->param->pit_motor, &output);
}
}

View File

@ -15,6 +15,8 @@ extern "C" {
#include "device/motor.h"
#include "device/motor_dm.h"
#include "device/motor_rm.h"
#include <stdint.h>
#include <stdbool.h>
/* Exported constants ------------------------------------------------------- */
#define GIMBAL_OK (0) /* 运行正常 */
@ -29,12 +31,21 @@ typedef enum {
GIMBAL_MODE_RELAX, /* 放松模式,电机不输出。一般情况云台初始化之后的模式 */
GIMBAL_MODE_ABSOLUTE, /* 绝对坐标系控制,控制在空间内的绝对姿态 */
GIMBAL_MODE_RELATIVE, /* 相对坐标系控制,控制相对于底盘的姿态 */
GIMBAL_MODE_AUTO_AIM, /* 火控系统托管模式,接收上位机或视觉算法指令 */
} Gimbal_Mode_t;
/* --- 新增:视觉/AI数据结构 --- */
typedef struct {
float yaw; /* 视觉解算出的空间目标绝对 Yaw 角度 */
float pit; /* 视觉解算出的空间目标绝对 Pitch 角度 */
uint8_t target_found; /* 视觉是否已锁定目标 (0: 未锁定, 1: 锁定) */
} Gimbal_Vision_t;
typedef struct {
Gimbal_Mode_t mode;
float delta_yaw;
float delta_pit;
Gimbal_Vision_t vision; /* 新增:包含视觉/AI控制数据 */
} Gimbal_CMD_t;
/* 软件限位 */
@ -76,6 +87,7 @@ typedef struct {
AHRS_Gyro_t gyro;
AHRS_Eulr_t eulr;
} Gimbal_IMU_t;
/* 云台反馈数据的结构体,包含反馈控制用的反馈数据 */
typedef struct {
Gimbal_IMU_t imu;
@ -90,6 +102,7 @@ typedef struct {
float yaw; /* yaw轴电机输出 */
float pit; /* pitch轴电机输出 */
} Gimbal_Output_t;
/*
*
*
@ -155,13 +168,12 @@ int8_t Gimbal_Init(Gimbal_t *g, const Gimbal_Params_t *param,
int8_t Gimbal_UpdateFeedback(Gimbal_t *gimbal);
int8_t Gimbal_UpdateIMU(Gimbal_t *gimbal, const Gimbal_IMU_t *imu);
/**
* \brief
*
* \param g
* \param fb
* \param g_cmd
* \param dt_sec
*
* \return
*/
@ -170,11 +182,10 @@ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd);
/**
* \brief
*
* \param s
* \param out CAN设备云台输出结构体
* \param g
*/
void Gimbal_Output(Gimbal_t *g);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -6,7 +6,7 @@
#include <math.h>
#include "bsp/time.h"
#include "device/motor_rm.h"
#include "module/config.h"
static bool last_firecmd;
static inline void ScaleSumTo1(float *a, float *b) {
@ -289,6 +289,13 @@ int8_t Shoot_Control(Shoot_t *s, Shoot_CMD_t *cmd)
break;
}
}
if (!g_debug_motor_enable) {
for(int i=0;i<SHOOT_FRIC_NUM;i++) {
MOTOR_RM_SetOutput(&s->param->fric_motor_param[i], 0.0f); // 摩擦轮强制 0 电流
}
MOTOR_RM_SetOutput(&s->param->trig_motor_param, 0.0f); // 拨弹电机强制 0 电流
}
MOTOR_RM_Ctrl(&s->param->fric_motor_param[0]);
last_firecmd = cmd->firecmd;
return 0;

View File

@ -21,7 +21,7 @@
#define ID_MCU (0xC4) // MCU 数据包 (发送)
#define ID_REF (0xA8) // 裁判系统数据包 (发送)
#define ID_AI (0xB5) // AI 控制数据包 (接收)
extern uint8_t AI_mode; // AI 模式变量供其他模块查询当前AI控制模式
// MCU 数据结构MCU -> 上位机)
typedef struct __attribute__((packed)) {
uint8_t mode; // 0: 空闲, 1: 自瞄, 2: 小符, 3: 大符
@ -81,38 +81,41 @@ static void AI_RxCpltCallback(void) {
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;
/* --- 修复 2务必初始化局部变量 --- */
Gimbal_Vision_t vision_data;
memset(&vision_data, 0, sizeof(Gimbal_Vision_t));
/* 1. 解析指令并生成系统控制信号 */
Shoot_CMD_t ai_shoot_cmd;
memset(&ai_shoot_cmd, 0, sizeof(Shoot_CMD_t)); // 防止野指针走火
/* --- 修复 1 & 3正确赋值视觉结构体 --- */
switch (ai_rx_data.data.mode) {
case 0: /* 不控制 */
ai_gimbal_cmd.mode = GIMBAL_MODE_RELAX;
// ai_shoot_cmd.mode = SHOOT_MODE_STOP; /* 需根据实际枚举名称修改 */
vision_data.target_found = 0;
// ai_shoot_cmd.mode = SHOOT_MODE_STOP;
break;
case 1: /* 控制云台但不开火 */
ai_gimbal_cmd.mode = GIMBAL_MODE_ABSOLUTE;
// ai_shoot_cmd.mode = SHOOT_MODE_READY; /* 摩擦轮开启,不拨弹 */
vision_data.target_found = 1;
vision_data.yaw = ai_rx_data.data.yaw;
vision_data.pit = ai_rx_data.data.pitch;
// ai_shoot_cmd.mode = SHOOT_MODE_READY;
break;
case 2: /* 控制云台且开火 */
ai_gimbal_cmd.mode = GIMBAL_MODE_ABSOLUTE;
// ai_shoot_cmd.mode = SHOOT_MODE_FIRE; /* 摩擦轮开启,拨弹 */
vision_data.target_found = 1;
vision_data.yaw = ai_rx_data.data.yaw;
vision_data.pit = ai_rx_data.data.pitch;
// ai_shoot_cmd.mode = SHOOT_MODE_FIRE;
break;
default:
ai_gimbal_cmd.mode = GIMBAL_MODE_RELAX;
vision_data.target_found = 0;
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);
/* 将视觉数据发送到专属队列,不与遥控器命令冲突 */
osMessageQueuePut(task_runtime.msgq.vision.data, &vision_data, 0, 0);
// 注意:若 ctrl_shoot.c 中使用的是 task_runtime.msgq.shoot.shoot_cmd
// 则替换为该全局结构体的队列句柄。此处采用常规 msgq 定义示范:
osMessageQueuePut(msgq.shoot.shoot_cmd, &ai_shoot_cmd, 0, 0);
/* 将发射指令发送到发射队列 (确保枚举名称与shoot.h对应后解开注释) */
// osMessageQueuePut(task_runtime.msgq.shoot.shoot_cmd, &ai_shoot_cmd, 0, 0);
}
}
/* 重新开启DMA定长接收 */
@ -124,13 +127,13 @@ static void AI_RxCpltCallback(void) {
*/
static void AI_Send_MCU_Data(void) {
PackageMCU_t tx_pkg;
quat_t current_quat;
AHRS_Quaternion_t current_quat;
tx_pkg.id = ID_MCU;
memset(&tx_pkg.data, 0, sizeof(DataMCU_t));
/* 1. 获取四元数 (从消息队列读取) */
if (osMessageQueueGet(msgq.ai.quat, &current_quat, NULL, 0) == osOK) {
if (osMessageQueueGet(task_runtime.msgq.ai.quat, &current_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;
@ -145,15 +148,15 @@ static void AI_Send_MCU_Data(void) {
/* 3. 获取发射机构与模式状态 (直接读取全局 shoot 结构体) */
/* 模式回传,取值为实际枚举映射 */
tx_pkg.data.mode = (uint8_t)shoot.mode;
//tx_pkg.data.mode = (uint8_t)shoot.mode;
tx_pkg.data.mode = AI_mode; /* 直接回传当前AI模式供上位机显示 */
/* 弹速反馈:由于硬件可能无法直接测弹速,暂用摩擦轮目标转速或估算转速代替。
*/
tx_pkg.data.bullet_speed = shoot.target_variable.target_rpm;
/* 弹量统计:如有独立计数变量,可进行赋值 */
// tx_pkg.data.bullet_count = shoot.feedback.xxxx;
//tx_pkg.data.bullet_count = shoot.feedback.xxxx;
tx_pkg.data.bullet_count = -1;
/* 4. 计算 CRC16扣除末尾 2 字节) */
tx_pkg.crc16 = CRC16_Calc((const uint8_t *)&tx_pkg, sizeof(PackageMCU_t) - 2, CRC16_INIT);

View File

@ -87,10 +87,10 @@ void Task_atti_esti(void *argument) {
// &gimbal_ahrs.quat: 四元数结构体地址
// 0: 消息优先级
// 0: 不等待,如果队列满了直接跳过(保证实时性)
osMessageQueuePut(msgq.ai.quat, &gimbal_ahrs.quat, 0, 0);
osMessageQueuePut(task_runtime.msgq.ai.quat, &gimbal_ahrs.quat, 0, 0);
// 如果 AI 任务还需要欧拉角,也可以发送欧拉角队列
osMessageQueuePut(msgq.ai.eulr, &eulr_to_send, 0, 0);
osMessageQueuePut(task_runtime.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));

View File

@ -24,39 +24,57 @@ Gimbal_CMD_t gimbal_cmd;
/* Private function --------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
void Task_ctrl_gimbal(void *argument) {
(void)argument; /* 未使用argument消除警告 */
/* 计算任务运行到指定频率需要等待的tick数 */
(void)argument;
const uint32_t delay_tick = osKernelGetTickFreq() / CTRL_GIMBAL_FREQ;
osDelay(CTRL_GIMBAL_INIT_DELAY);
uint32_t tick = osKernelGetTickCount();
osDelay(CTRL_GIMBAL_INIT_DELAY); /* 延时一段时间再开启任务 */
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
/* USER CODE INIT BEGIN */
Gimbal_Init(&gimbal, &Config_GetRobotParam()->gimbal_param, CTRL_GIMBAL_FREQ);
/* USER CODE INIT END */
/* 缓存最近一次收到的有效视觉数据 */
static Gimbal_Vision_t last_vision_data = {0};
/* 新增:记录最后一次收到有效视觉数据的时间戳 */
static uint32_t last_vision_update_tick = 0;
/* 新增:视觉断线超时阈值,此处设置为 100ms */
const uint32_t vision_timeout_ticks = osKernelGetTickFreq() / 10;
while (1) {
tick += delay_tick; /* 计算下一个唤醒时刻 */
/* USER CODE BEGIN */
tick += delay_tick;
/* 1. 更新 IMU 数据 */
if (osMessageQueueGet(task_runtime.msgq.gimbal.imu, &gimbal_imu, NULL, 0) == osOK) {
Gimbal_UpdateIMU(&gimbal, &gimbal_imu);
}
/* 2. 更新基础控制指令(这会覆盖全局 gimbal_cmd丢失其内部的 vision 数据) */
osMessageQueueGet(task_runtime.msgq.gimbal.cmd, &gimbal_cmd, NULL, 0);
/* 3. 获取并缓存 AI 视觉信号,增加超时判断逻辑 */
Gimbal_Vision_t current_vision;
if (osMessageQueueGet(task_runtime.msgq.vision.data, &current_vision, NULL, 0) == osOK) {
last_vision_data = current_vision; /* 有新视觉包时更新缓存 */
last_vision_update_tick = osKernelGetTickCount(); /* 更新收到数据的时间戳 */
} else {
/* 未收到新数据时,检查是否超时断线 */
if ((osKernelGetTickCount() - last_vision_update_tick) > vision_timeout_ticks) {
last_vision_data.target_found = 0; /* 超时强制视为目标丢失 */
/* 将缓存的 yaw/pit 角度清零,避免残留脏数据干扰 */
last_vision_data.yaw = 0.0f;
last_vision_data.pit = 0.0f;
}
}
/* 4. 强制将缓存的视觉数据挂载到当前的控制指令中 */
gimbal_cmd.vision = last_vision_data;
/* 5. 执行云台计算与输出 */
Gimbal_UpdateFeedback(&gimbal);
// osMessageQueueReset(task_runtime.msgq.chassis_yaw);
osMessageQueuePut(task_runtime.msgq.chassis.yaw, &gimbal.feedback.motor.yaw, 0, 0);
Gimbal_Control(&gimbal, &gimbal_cmd);
Gimbal_Output(&gimbal);
/* USER CODE END */
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
osDelayUntil(tick);
}
}

View File

@ -17,6 +17,9 @@
/* USER STRUCT BEGIN */
Shoot_t shoot;
Shoot_CMD_t shoot_cmd;
extern Gimbal_CMD_t ai_gimbal_cmd;
extern Shoot_CMD_t ai_shoot_cmd;
extern uint8_t AI_mode; // AI 模式变量供其他模块查询当前AI控制模式
/* USER STRUCT END */
/* Private function --------------------------------------------------------- */

View File

@ -19,6 +19,7 @@
/* Private macro ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER STRUCT BEGIN */
uint8_t AI_mode; // AI 模式变量供其他模块查询当前AI控制模式
DR16_t dr16;
Shoot_CMD_t for_shoot;
Gimbal_CMD_t cmd_for_gimbal;
@ -59,21 +60,25 @@ void Task_rc(void *argument) {
cmd_for_gimbal.mode = GIMBAL_MODE_RELAX;
cmd_for_gimbal.delta_yaw = 0.0f;
cmd_for_gimbal.delta_pit = 0.0f;
AI_mode = 0; /* AI 模式:空闲 */
break;
case DR16_SW_MID:
cmd_for_gimbal.mode = GIMBAL_MODE_ABSOLUTE;
cmd_for_gimbal.delta_yaw = -dr16.data.ch_r_x * 5.0f;
cmd_for_gimbal.delta_pit = dr16.data.ch_r_y * 5.0f;
AI_mode = 0; /* AI 模式:空闲 */
break;
case DR16_SW_DOWN:
cmd_for_gimbal.mode = GIMBAL_MODE_ABSOLUTE;
cmd_for_gimbal.mode = GIMBAL_MODE_AUTO_AIM;
cmd_for_gimbal.delta_yaw = -dr16.data.ch_r_x * 5.0f;
cmd_for_gimbal.delta_pit = dr16.data.ch_r_y * 5.0f;
AI_mode = 1; /* AI 模式:自动瞄准 */
break;
default:
cmd_for_gimbal.mode = GIMBAL_MODE_RELAX;
cmd_for_gimbal.delta_yaw = 0.0f;
cmd_for_gimbal.delta_pit = 0.0f;
AI_mode = 0; /* AI 模式:空闲 */
break;
}

View File

@ -74,6 +74,10 @@ typedef struct {
osMessageQueueId_t eulr;
osMessageQueueId_t fire;
}ai;
/* --- 新增:视觉/AI数据消息队列 --- */
struct {
osMessageQueueId_t data;
}vision;
} msgq;
/* USER MESSAGE END */