god-yuan-hero/User/module/cmd/cmd.c
2026-01-06 09:55:54 +08:00

378 lines
11 KiB
C
Raw 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.

/*
* CMD 模块 V2 - 主控制模块实现
*/
#include "cmd.h"
#include "bsp/time.h"
#include <stdint.h>
#include <string.h>
/* ========================================================================== */
/* 命令构建函数 */
/* ========================================================================== */
/* 从RC输入生成底盘命令 */
static void CMD_RC_BuildChassisCmd(CMD_t *ctx) {
CMD_RCModeMap_t *map = &ctx->config->rc_mode_map;
/* 根据左拨杆位置选择模式 */
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
ctx->output.chassis.cmd.mode = map->sw_left_up;
break;
case CMD_SW_MID:
ctx->output.chassis.cmd.mode = map->sw_left_mid;
break;
case CMD_SW_DOWN:
ctx->output.chassis.cmd.mode = map->sw_left_down;
break;
default:
ctx->output.chassis.cmd.mode = CHASSIS_MODE_RELAX;
break;
}
/* 摇杆控制移动 */
ctx->output.chassis.cmd.ctrl_vec.vx = ctx->input.rc.joy_right.x;
ctx->output.chassis.cmd.ctrl_vec.vy = ctx->input.rc.joy_right.y;
}
/* 从RC输入生成云台命令 */
static void CMD_RC_BuildGimbalCmd(CMD_t *ctx) {
CMD_RCModeMap_t *map = &ctx->config->rc_mode_map;
/* 根据拨杆选择云台模式 */
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
ctx->output.gimbal.cmd.mode = map->gimbal_sw_up;
break;
case CMD_SW_MID:
ctx->output.gimbal.cmd.mode = map->gimbal_sw_mid;
break;
case CMD_SW_DOWN:
ctx->output.gimbal.cmd.mode = map->gimbal_sw_down;
break;
default:
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX;
break;
}
/* 左摇杆控制云台 */
ctx->output.gimbal.cmd.delta_yaw = -ctx->input.rc.joy_left.x * 2.0f;
ctx->output.gimbal.cmd.delta_pit = -ctx->input.rc.joy_left.y * 1.5f;
}
/* 从RC输入生成射击命令 */
static void CMD_RC_BuildShootCmd(CMD_t *ctx) {
if (ctx->input.online[CMD_SRC_RC]) {
ctx->output.shoot.cmd.mode = SHOOT_MODE_SINGLE;
} else {
ctx->output.shoot.cmd.mode = SHOOT_MODE_SAFE;
}
/* 根据右拨杆控制射击 */
switch (ctx->input.rc.sw[1]) {
case CMD_SW_DOWN:
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = true;
break;
case CMD_SW_MID:
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = false;
break;
case CMD_SW_UP:
default:
ctx->output.shoot.cmd.ready = false;
ctx->output.shoot.cmd.firecmd = false;
break;
}
}
/* 从PC输入生成射击命令 */
static void CMD_RC_BuildTrackCmd(CMD_t *ctx) {
CMD_RCModeMap_t *map = &ctx->config->rc_mode_map;
if (!ctx->input.online[CMD_SRC_RC]) {
ctx->output.track.cmd.enable = false;
ctx->output.track.cmd.vel = 0.0f;
ctx->output.track.cmd.omega = 0.0f;
return;
}
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
ctx->output.track.cmd.enable = map->track_sw_up;
break;
case CMD_SW_MID:
ctx->output.track.cmd.enable = map->track_sw_mid;
break;
case CMD_SW_DOWN:
ctx->output.track.cmd.enable = map->track_sw_down;
break;
default:
ctx->output.track.cmd.enable = false;
break;
}
ctx->output.track.cmd.enable = ctx->input.online[CMD_SRC_RC];
ctx->output.track.cmd.vel = ctx->input.rc.joy_right.y * 2.0f;
if(fabsf(ctx->input.rc.joy_right.y * 2.0f) > 1.0f)
ctx->output.track.cmd.vel = ctx->output.track.cmd.vel > 0 ? 1.0f
: -1.0f;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_TRACK);
}
/* 从PC输入生成底盘命令 */
static void CMD_PC_BuildChassisCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.chassis.cmd.mode = CHASSIS_MODE_RELAX;
return;
}
ctx->output.chassis.cmd.mode = CHASSIS_MODE_FOLLOW_GIMBAL;
/* WASD控制移动 */
ctx->output.chassis.cmd.ctrl_vec.vx = 0.0f;
ctx->output.chassis.cmd.ctrl_vec.vy = 0.0f;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_CHASSIS);
}
/* 从PC输入生成云台命令 */
static void CMD_PC_BuildGimbalCmd(CMD_t *ctx) {
CMD_Sensitivity_t *sens = &ctx->config->sensitivity;
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX;
return;
}
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_ABSOLUTE;
/* 鼠标控制云台 */
ctx->output.gimbal.cmd.delta_yaw = (float)-ctx->input.pc.mouse.x * ctx->timer.dt * sens->mouse_sens;
ctx->output.gimbal.cmd.delta_pit = (float)ctx->input.pc.mouse.y * ctx->timer.dt * sens->mouse_sens * 1.5f;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_GIMBAL);
}
/* 从PC输入生成射击命令 */
static void CMD_PC_BuildShootCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.shoot.cmd.mode = SHOOT_MODE_SAFE;
return;
}
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = ctx->input.pc.mouse.l_click;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_SHOOT);
}
/* 从PC输入生成履带命令 */
static void CMD_PC_BuildTrackCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.track.cmd.enable = false;
ctx->output.track.cmd.vel = 0.0f;
ctx->output.track.cmd.omega = 0.0f;
return;
}
ctx->output.track.cmd.enable = true;
/* 可根据需要添加PC对履带的控制例如键盘按键 */
ctx->output.track.cmd.vel = 0.0f;
ctx->output.track.cmd.omega = 0.0f;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_TRACK);
}
/* 从NUC/AI输入生成云台命令 */
static void CMD_NUC_BuildGimbalCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_NUC]) {
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX;
return;
}
/* 使用AI提供的云台控制数据 */
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_ABSOLUTE;
ctx->output.gimbal.cmd.delta_yaw = ctx->input.nuc.gimbal.setpoint.yaw;
ctx->output.gimbal.cmd.delta_pit = ctx->input.nuc.gimbal.setpoint.pit;
}
/* 从NUC/AI输入生成射击命令 */
static void CMD_NUC_BuildShootCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_NUC]) {
ctx->output.shoot.cmd.mode = SHOOT_MODE_SAFE;
return;
}
/* 根据AI模式决定射击行为 */
switch (ctx->input.nuc.mode) {
case 1:
ctx->output.shoot.cmd.ready = false;
ctx->output.shoot.cmd.firecmd = false;
break;
case 2:
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = false;
break;
case 3:
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = true;
break;
}
}
/* 离线安全模式 */
static void CMD_SetOfflineMode(CMD_t *ctx) {
ctx->output.chassis.cmd.mode = CHASSIS_MODE_RELAX;
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX;
ctx->output.shoot.cmd.mode = SHOOT_MODE_SAFE;
ctx->output.track.cmd.enable = false;
}
/* ========================================================================== */
/* 公开API实现 */
/* ========================================================================== */
int8_t CMD_Init(CMD_t *ctx, CMD_Config_t *config) {
if (ctx == NULL || config == NULL) {
return CMD_ERR_NULL;
}
memset(ctx, 0, sizeof(CMD_t));
ctx->config = config;
/* 初始化适配器 */
CMD_Adapter_InitAll();
/* 初始化行为处理器 */
CMD_Behavior_Init();
return CMD_OK;
}
int8_t CMD_UpdateInput(CMD_t *ctx) {
if (ctx == NULL) {
return CMD_ERR_NULL;
}
/* 保存上一帧输入 */
memcpy(&ctx->last_input, &ctx->input, sizeof(ctx->input));
/* 更新所有输入源 */
for (int i = 0; i < CMD_SRC_NUM; i++) {
CMD_Adapter_GetInput((CMD_InputSource_t)i, &ctx->input);
}
return CMD_OK;
}
typedef void (*CMD_BuildCommandFunc)(CMD_t *cmd);
typedef struct {
CMD_InputSource_t source;
CMD_BuildCommandFunc chassisFunc;
CMD_BuildCommandFunc gimbalFunc;
CMD_BuildCommandFunc shootFunc;
CMD_BuildCommandFunc trackFunc;
} CMD_SourceHandler_t;
CMD_SourceHandler_t sourceHandlers[CMD_SRC_NUM] = {
{CMD_SRC_RC, CMD_RC_BuildChassisCmd, CMD_RC_BuildGimbalCmd, CMD_RC_BuildShootCmd, CMD_RC_BuildTrackCmd},
{CMD_SRC_PC, CMD_PC_BuildChassisCmd, CMD_PC_BuildGimbalCmd, CMD_PC_BuildShootCmd, CMD_PC_BuildTrackCmd},
{CMD_SRC_NUC, NULL, CMD_NUC_BuildGimbalCmd, CMD_NUC_BuildShootCmd, NULL},
{CMD_SRC_REF, NULL, NULL, NULL, NULL},
};
int8_t CMD_Arbitrate(CMD_t *ctx) {
if (ctx == NULL) {
return CMD_ERR_NULL;
}
/* 自动仲裁:优先级 PC > RC > NUC */
// CMD_InputSource_t candidates[] = {CMD_SRC_RC, CMD_SRC_PC, CMD_SRC_NUC};
CMD_InputSource_t candidates[] = {CMD_SRC_RC, CMD_SRC_PC};
const int num_candidates = sizeof(candidates) / sizeof(candidates[0]);
/* 如果当前输入源仍然在线且有效,保持使用 */
if (ctx->active_source < CMD_SRC_NUM &&
ctx->active_source != CMD_SRC_REF &&
ctx->input.online[ctx->active_source]) {
goto seize;
}
/* 否则选择第一个可用的控制输入源 */
for (int i = 0; i < num_candidates; i++) {
CMD_InputSource_t src = candidates[i];
if (ctx->input.online[src]) {
ctx->active_source = src;
break;
}else {
ctx->active_source = CMD_SRC_NUM;
continue;
}
}
/* 优先级抢占逻辑 */
seize:
/* 重置输入源 */
ctx->output.chassis.source = ctx->active_source;
ctx->output.gimbal.source = ctx->active_source;
ctx->output.shoot.source = ctx->active_source;
ctx->output.track.source = ctx->active_source;
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_NONE);
if (ctx->input.online[CMD_SRC_NUC]) {
if (ctx->active_source==CMD_SRC_RC) {//保护其余模式下安全
if (ctx->input.rc.sw[0] == CMD_SW_DOWN) {
ctx->output.gimbal.source = CMD_SRC_NUC;
ctx->output.shoot.source = CMD_SRC_NUC;
}
}
}
return CMD_OK;
}
int8_t CMD_GenerateCommands(CMD_t *ctx) {
if (ctx == NULL) {
return CMD_ERR_NULL;
}
/* 更新时间 */
uint64_t now_us = BSP_TIME_Get_us();
ctx->timer.now = now_us / 1000000.0f;
ctx->timer.dt = (now_us - ctx->timer.last_us) / 1000000.0f;
ctx->timer.last_us = now_us;
/* 没有有效输入源 */
if (ctx->active_source >= CMD_SRC_NUM) {
CMD_SetOfflineMode(ctx);
return CMD_ERR_NO_INPUT;
}
if (sourceHandlers[ctx->output.gimbal.source].gimbalFunc != NULL) {
sourceHandlers[ctx->output.gimbal.source].gimbalFunc(ctx);
}
if (sourceHandlers[ctx->output.chassis.source].chassisFunc != NULL) {
sourceHandlers[ctx->output.chassis.source].chassisFunc(ctx);
}
if (sourceHandlers[ctx->output.shoot.source].shootFunc != NULL) {
sourceHandlers[ctx->output.shoot.source].shootFunc(ctx);
}
if (sourceHandlers[ctx->output.track.source].trackFunc != NULL) {
sourceHandlers[ctx->output.track.source].trackFunc(ctx);
}
return CMD_OK;
}
int8_t CMD_Update(CMD_t *ctx) {
int8_t ret;
ret = CMD_UpdateInput(ctx);
if (ret != CMD_OK) return ret;
CMD_Arbitrate(ctx);
ret = CMD_GenerateCommands(ctx);
return ret;
}