rm_balance/User/module/cmd/cmd.c
2026-03-14 12:41:14 +08:00

745 lines
24 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输入生成底盘命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_CHASSIS
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;
ctx->output.chassis.cmd.ctrl_vec.wz = ctx->input.rc.joy_left.x;
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_CHASSIS */
/* 从 RC 输入生成云台命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_GIMBAL
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_right.x * 5.0f;
ctx->output.gimbal.cmd.delta_pit = ctx->input.rc.joy_right.y * 5.0f;
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_GIMBAL */
/* 从 RC 输入生成射击命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_SHOOT
static void CMD_RC_BuildShootCmd(CMD_t *ctx) {
if (ctx->input.online[CMD_SRC_RC]) {
ctx->output.shoot.cmd.mode = SHOOT_MODE_BURST;
} 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;
}
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_SHOOT */
/* 从 RC 输入生成履带命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_TRACK
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);
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_TRACK */
/* 从PC输入生成底盘命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_CHASSIS
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);
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_CHASSIS */
/* 从PC输入生成云台命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_GIMBAL
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);
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_GIMBAL */
/* 从PC输入生成射击命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_SHOOT
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);
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_SHOOT */
/* 从PC输入生成履带命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_TRACK
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);
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_TRACK */
/* 从NUC/AI输入生成云台命令 */
#if CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_GIMBAL
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提供的云台控制数据 */
if (ctx->input.nuc.mode!=0) {
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_AI_CONTROL;
ctx->output.gimbal.cmd.setpoint_yaw = ctx->input.nuc.gimbal.setpoint.yaw;
ctx->output.gimbal.cmd.setpoint_pit = ctx->input.nuc.gimbal.setpoint.pit;
}
}
#endif /* CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_GIMBAL */
/* 从 NUC/AI 输入生成射击命令 */
#if CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_SHOOT
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 0:
ctx->output.shoot.cmd.ready = false;
ctx->output.shoot.cmd.firecmd = false;
break;
case 1:
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = false;
break;
case 2:
if (ctx->input.rc.sw[1]==CMD_SW_DOWN) {
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = true;
}else {
ctx->output.shoot.cmd.ready = true;
ctx->output.shoot.cmd.firecmd = false;
}
break;
}
}
#endif /* CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_SHOOT */
/* 从 RC 输入生成机械臂命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_ARM
static void CMD_RC_BuildArmCmd(CMD_t *ctx) {
/*
* 遥控器控制机械臂末端位姿 —— 笛卡尔增量方案
*
* 左拨杆 SW_L (sw[0]) —— 整体使能:
* UP → 失能(电机松弛/重力补偿)
* MID → 使能,笛卡尔增量控制
* DOWN → 使能(保留备用)
*
* 右拨杆 SW_R (sw[1]) —— 自由度分组:
*
* UP 【位置模式】3轴平移 + 偏航(最常用操作放第一层)
* 右摇杆X (RX) → X 平移(末端左右移动)
* 右摇杆Y (RY) → Y 平移(末端前后移动)
* 左摇杆Y (LY) → Z 平移(末端升降)
* 左摇杆X (LX) → Yaw 偏航旋转
*
* MID 【姿态模式】俯仰/横滚全姿态,同时保留 Z 和偏航可调
* 右摇杆X (RX) → Yaw 偏航旋转(持续可调,避免强制切换模式)
* 右摇杆Y (RY) → Pitch 俯仰旋转
* 左摇杆X (LX) → Roll 横滚旋转
* 左摇杆Y (LY) → Z 平移(升降保持可调,避免强制切换模式)
*
* DOWN → set_target_as_current目标位姿吸附当前实际位姿消除累积漂移
*
* 摇杆直觉映射总结:
* 右摇杆 = "末端去哪里"XY平移最自然
* 左Y = "臂的高低" Z升降推上=升高)
* 左X = 位置模式→偏航 / 姿态模式→横滚
*/
ctx->output.arm.cmd.set_target_as_current = false;
if (ctx->input.rc.sw[1] == CMD_SW_DOWN) {
ctx->output.arm.cmd.set_target_as_current = true;
}
switch (ctx->input.rc.sw[0]) {
case CMD_SW_MID:
case CMD_SW_DOWN:
ctx->output.arm.cmd.enable = true;
break;
default:
ctx->output.arm.cmd.enable = false;
goto end;
}
/* 遥控器模式使用笛卡尔位姿累积控制 */
ctx->output.arm.cmd.ctrl_type = ARM_CTRL_REMOTE_CARTESIAN;
/* set_target_as_current 置位时不叠加摇杆增量,由上层负责同步位姿基准 */
if (ctx->output.arm.cmd.set_target_as_current) return;
/* 输出摇杆速度命令m/s, rad/s——不含 dt由arm_main在正确坐标系下积分到世界系 target_pose */
float pos_scale = 0.3f; /* 末端线速度上限 (m/s) */
float rot_scale = 1.0f; /* 末端角速度上限 (rad/s) */
memset(&ctx->output.arm.cmd.joy_vel, 0, sizeof(ctx->output.arm.cmd.joy_vel));
switch (ctx->input.rc.sw[1]) {
case CMD_SW_UP:
/* 位置模式3轴平移 + 偏航 */
ctx->output.arm.cmd.joy_vel.x = ctx->input.rc.joy_right.x * pos_scale;
ctx->output.arm.cmd.joy_vel.y = ctx->input.rc.joy_right.y * pos_scale;
ctx->output.arm.cmd.joy_vel.z = ctx->input.rc.joy_left.y * pos_scale;
ctx->output.arm.cmd.joy_vel.yaw = ctx->input.rc.joy_left.x * rot_scale;
break;
case CMD_SW_MID:
/* 姿态模式:俯仰 + 横滚 + 偏航 + 升降全6自由度可达Z/Yaw持续可调 */
ctx->output.arm.cmd.joy_vel.yaw = ctx->input.rc.joy_right.x * rot_scale;
ctx->output.arm.cmd.joy_vel.pitch = ctx->input.rc.joy_right.y * rot_scale;
ctx->output.arm.cmd.joy_vel.roll = ctx->input.rc.joy_left.x * rot_scale;
ctx->output.arm.cmd.joy_vel.z = ctx->input.rc.joy_left.y * pos_scale;
break;
default:
break;
}
end:
return;
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_ARM */
/* 从 PC 输入生成机械臂命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_ARM
static void CMD_PC_BuildArmCmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.arm.cmd.enable = false;
return;
}
ctx->output.arm.cmd.enable = true;
ctx->output.arm.cmd.ctrl_type = ARM_CTRL_REMOTE_CARTESIAN;
/* 鼠标控制末端位坐 XY其他轴可根据需要扩展 */
float sens = ctx->config->sensitivity.mouse_sens * 0.001f;
ctx->output.arm.cmd.target_pose.x += (float)ctx->input.pc.mouse.x * sens * ctx->timer.dt;
ctx->output.arm.cmd.target_pose.y += (float)ctx->input.pc.mouse.y * sens * ctx->timer.dt;
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_ARM */
/* 从 RC 输入生成平衡底盘命令 */
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_BALANCE_CHASSIS
static void CMD_RC_BuildBalanceChassisCmd(CMD_t *ctx) {
CMD_RCModeMap_t *map = &ctx->config->rc_mode_map;
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
ctx->output.balance_chassis.cmd.mode = map->balance_sw_up;
break;
case CMD_SW_MID:
ctx->output.balance_chassis.cmd.mode = map->balance_sw_mid;
break;
case CMD_SW_DOWN:
ctx->output.balance_chassis.cmd.mode = map->balance_sw_down;
break;
default:
ctx->output.balance_chassis.cmd.mode = CHASSIS_MODE_RELAX;
break;
}
/* 左摩杆Y/X → 平移右摩杆X → 转向 */
ctx->output.balance_chassis.cmd.move_vec.vx = ctx->input.rc.joy_left.y;
ctx->output.balance_chassis.cmd.move_vec.vy = ctx->input.rc.joy_left.x;
ctx->output.balance_chassis.cmd.move_vec.wz = ctx->input.rc.joy_right.x;
/* 拨轮传递目标高度 */
ctx->output.balance_chassis.cmd.height = ctx->input.rc.dial;
ctx->output.balance_chassis.cmd.jump_trigger = false;
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_BALANCE_CHASSIS */
/* 从 PC 输入生成平衡底盘命令 */
#if CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_BALANCE_CHASSIS
static void CMD_PC_BuildBalanceChassisCmd(CMD_t *ctx) {
CMD_Sensitivity_t *sens = &ctx->config->sensitivity;
if (!ctx->input.online[CMD_SRC_PC]) {
ctx->output.balance_chassis.cmd.mode = CHASSIS_MODE_RELAX;
return;
}
ctx->output.balance_chassis.cmd.mode = CHASSIS_MODE_WHELL_LEG_BALANCE;
float vx = 0.0f, wz = 0.0f;
uint32_t kb = ctx->input.pc.keyboard.bitmap;
//案件的命令要放到behavior里
// if (kb & CMD_KEY_W) vx += sens->move_sens;
// if (kb & CMD_KEY_S) vx -= sens->move_sens;
// if (kb & CMD_KEY_A) wz += sens->move_sens;
// if (kb & CMD_KEY_D) wz -= sens->move_sens;
// if (kb & CMD_KEY_SHIFT) { vx *= sens->move_fast_mult; wz *= sens->move_fast_mult; }
// if (kb & CMD_KEY_CTRL) { vx *= sens->move_slow_mult; wz *= sens->move_slow_mult; }
ctx->output.balance_chassis.cmd.move_vec.vx = vx;
ctx->output.balance_chassis.cmd.move_vec.wz = wz;
ctx->output.balance_chassis.cmd.move_vec.vy = 0.0f;
}
#endif /* CMD_ENABLE_SRC_PC && CMD_ENABLE_MODULE_BALANCE_CHASSIS */
#if CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_REFUI
static void CMD_NUC_BuildRefUICmd(CMD_t *ctx) {
if (!ctx->input.online[CMD_SRC_NUC]) {
ctx->output.refui.cmd = UI_AUTO_AIM_STOP;
return;
}
ctx->output.refui.cmd = (ctx->input.nuc.mode != 0) ? UI_AUTO_AIM_START : UI_AUTO_AIM_STOP;
}
#endif /* CMD_ENABLE_SRC_NUC && CMD_ENABLE_MODULE_REFUI */
/* 离线安全模式 */
static void CMD_SetOfflineMode(CMD_t *ctx) {
#if CMD_ENABLE_MODULE_CHASSIS
ctx->output.chassis.cmd.mode = CHASSIS_MODE_RELAX;
#endif
#if CMD_ENABLE_MODULE_GIMBAL
ctx->output.gimbal.cmd.mode = GIMBAL_MODE_RELAX;
#endif
#if CMD_ENABLE_MODULE_SHOOT
ctx->output.shoot.cmd.mode = SHOOT_MODE_SAFE;
#endif
#if CMD_ENABLE_MODULE_TRACK
ctx->output.track.cmd.enable = false;
#endif
#if CMD_ENABLE_MODULE_BALANCE_CHASSIS
ctx->output.balance_chassis.cmd.mode = CHASSIS_MODE_RELAX;
#endif
#if CMD_ENABLE_MODULE_ARM
ctx->output.arm.cmd.enable = false;
#endif
#if CMD_ENABLE_MODULE_REFUI
ctx->output.refui.cmd = UI_NOTHING;
#endif
}
/* ========================================================================== */
/* 公开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_BuildCommandFunc armFunc;
CMD_BuildCommandFunc refuiFunc;
CMD_BuildCommandFunc balanceChassisFunc;
} CMD_SourceHandler_t;
/* Build-function conditional references */
#if CMD_ENABLE_MODULE_CHASSIS && CMD_ENABLE_SRC_RC
#define _FN_RC_CHASSIS CMD_RC_BuildChassisCmd
#else
#define _FN_RC_CHASSIS NULL
#endif
#if CMD_ENABLE_MODULE_GIMBAL && CMD_ENABLE_SRC_RC
#define _FN_RC_GIMBAL CMD_RC_BuildGimbalCmd
#else
#define _FN_RC_GIMBAL NULL
#endif
#if CMD_ENABLE_MODULE_SHOOT && CMD_ENABLE_SRC_RC
#define _FN_RC_SHOOT CMD_RC_BuildShootCmd
#else
#define _FN_RC_SHOOT NULL
#endif
#if CMD_ENABLE_MODULE_TRACK && CMD_ENABLE_SRC_RC
#define _FN_RC_TRACK CMD_RC_BuildTrackCmd
#else
#define _FN_RC_TRACK NULL
#endif
#if CMD_ENABLE_MODULE_CHASSIS && CMD_ENABLE_SRC_PC
#define _FN_PC_CHASSIS CMD_PC_BuildChassisCmd
#else
#define _FN_PC_CHASSIS NULL
#endif
#if CMD_ENABLE_MODULE_GIMBAL && CMD_ENABLE_SRC_PC
#define _FN_PC_GIMBAL CMD_PC_BuildGimbalCmd
#else
#define _FN_PC_GIMBAL NULL
#endif
#if CMD_ENABLE_MODULE_SHOOT && CMD_ENABLE_SRC_PC
#define _FN_PC_SHOOT CMD_PC_BuildShootCmd
#else
#define _FN_PC_SHOOT NULL
#endif
#if CMD_ENABLE_MODULE_TRACK && CMD_ENABLE_SRC_PC
#define _FN_PC_TRACK CMD_PC_BuildTrackCmd
#else
#define _FN_PC_TRACK NULL
#endif
#if CMD_ENABLE_MODULE_ARM && CMD_ENABLE_SRC_RC
#define _FN_RC_ARM CMD_RC_BuildArmCmd
#else
#define _FN_RC_ARM NULL
#endif
#if CMD_ENABLE_MODULE_ARM && CMD_ENABLE_SRC_PC
#define _FN_PC_ARM CMD_PC_BuildArmCmd
#else
#define _FN_PC_ARM NULL
#endif
#if CMD_ENABLE_MODULE_BALANCE_CHASSIS && CMD_ENABLE_SRC_RC
#define _FN_RC_BALANCE_CHASSIS CMD_RC_BuildBalanceChassisCmd
#else
#define _FN_RC_BALANCE_CHASSIS NULL
#endif
#if CMD_ENABLE_MODULE_BALANCE_CHASSIS && CMD_ENABLE_SRC_PC
#define _FN_PC_BALANCE_CHASSIS CMD_PC_BuildBalanceChassisCmd
#else
#define _FN_PC_BALANCE_CHASSIS NULL
#endif
#if CMD_ENABLE_MODULE_GIMBAL && CMD_ENABLE_SRC_NUC
#define _FN_NUC_GIMBAL CMD_NUC_BuildGimbalCmd
#else
#define _FN_NUC_GIMBAL NULL
#endif
#if CMD_ENABLE_MODULE_SHOOT && CMD_ENABLE_SRC_NUC
#define _FN_NUC_SHOOT CMD_NUC_BuildShootCmd
#else
#define _FN_NUC_SHOOT NULL
#endif
CMD_SourceHandler_t sourceHandlers[CMD_SRC_NUM] = {
#if CMD_ENABLE_SRC_RC
[CMD_SRC_RC] = {CMD_SRC_RC, _FN_RC_CHASSIS, _FN_RC_GIMBAL, _FN_RC_SHOOT, _FN_RC_TRACK, _FN_RC_ARM, NULL, _FN_RC_BALANCE_CHASSIS},
#endif
#if CMD_ENABLE_SRC_PC
[CMD_SRC_PC] = {CMD_SRC_PC, _FN_PC_CHASSIS, _FN_PC_GIMBAL, _FN_PC_SHOOT, _FN_PC_TRACK, _FN_PC_ARM, NULL, _FN_PC_BALANCE_CHASSIS},
#endif
#if CMD_ENABLE_SRC_NUC
#if CMD_ENABLE_MODULE_REFUI
#define _FN_NUC_REFUI CMD_NUC_BuildRefUICmd
#else
#define _FN_NUC_REFUI NULL
#endif
[CMD_SRC_NUC] = {CMD_SRC_NUC, NULL, _FN_NUC_GIMBAL, _FN_NUC_SHOOT, NULL, NULL, _FN_NUC_REFUI, NULL},
#endif
#if CMD_ENABLE_SRC_REF
[CMD_SRC_REF] = {CMD_SRC_REF, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
#endif
};
int8_t CMD_Arbitrate(CMD_t *ctx) {
if (ctx == NULL) {
return CMD_ERR_NULL;
}
/* RC > PC priority arbitration */
CMD_InputSource_t candidates[] = {
#if CMD_ENABLE_SRC_RC
CMD_SRC_RC,
#endif
#if CMD_ENABLE_SRC_PC
CMD_SRC_PC,
#endif
};
const int num_candidates = sizeof(candidates) / sizeof(candidates[0]);
/* keep current source if still online */
if (ctx->active_source < CMD_SRC_NUM &&
#if CMD_ENABLE_SRC_REF
ctx->active_source != CMD_SRC_REF &&
#endif
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:
/* reset output sources */
#if CMD_ENABLE_MODULE_CHASSIS
ctx->output.chassis.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_GIMBAL
ctx->output.gimbal.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_SHOOT
ctx->output.shoot.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_TRACK
ctx->output.track.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_ARM
ctx->output.arm.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_REFUI
ctx->output.refui.source = ctx->active_source;
#endif
#if CMD_ENABLE_MODULE_BALANCE_CHASSIS
ctx->output.balance_chassis.source = ctx->active_source;
#endif
CMD_Behavior_ProcessAll(ctx, &ctx->input, &ctx->last_input, CMD_MODULE_NONE);
#if CMD_ENABLE_SRC_NUC && CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_GIMBAL && CMD_ENABLE_MODULE_SHOOT
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;
#if CMD_ENABLE_MODULE_REFUI
ctx->output.refui.source = CMD_SRC_NUC;
#endif
}
}
}
#endif
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 CMD_ENABLE_MODULE_GIMBAL
if (sourceHandlers[ctx->output.gimbal.source].gimbalFunc != NULL) {
sourceHandlers[ctx->output.gimbal.source].gimbalFunc(ctx);
}
#endif
#if CMD_ENABLE_MODULE_CHASSIS
if (sourceHandlers[ctx->output.chassis.source].chassisFunc != NULL) {
sourceHandlers[ctx->output.chassis.source].chassisFunc(ctx);
}
#endif
#if CMD_ENABLE_MODULE_SHOOT
if (sourceHandlers[ctx->output.shoot.source].shootFunc != NULL) {
sourceHandlers[ctx->output.shoot.source].shootFunc(ctx);
}
#endif
#if CMD_ENABLE_MODULE_TRACK
if (sourceHandlers[ctx->output.track.source].trackFunc != NULL) {
sourceHandlers[ctx->output.track.source].trackFunc(ctx);
}
#endif
#if CMD_ENABLE_MODULE_ARM
if (sourceHandlers[ctx->output.arm.source].armFunc != NULL) {
sourceHandlers[ctx->output.arm.source].armFunc(ctx);
}
#endif
#if CMD_ENABLE_MODULE_REFUI
if (sourceHandlers[ctx->output.refui.source].refuiFunc != NULL) {
sourceHandlers[ctx->output.refui.source].refuiFunc(ctx);
} else {
ctx->output.refui.cmd = UI_NOTHING;
}
#endif
#if CMD_ENABLE_MODULE_BALANCE_CHASSIS
if (sourceHandlers[ctx->output.balance_chassis.source].balanceChassisFunc != NULL) {
sourceHandlers[ctx->output.balance_chassis.source].balanceChassisFunc(ctx);
}
#endif
return CMD_OK;
}
#if CMD_ENABLE_SRC_REF
static void CMD_REF_BuildOutput(CMD_t *ctx) {
ctx->output.ref = ctx->input.ref;
}
#endif
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);
#if CMD_ENABLE_SRC_REF
CMD_REF_BuildOutput(ctx);
#endif
return ret;
}