yincheng.zhong
7 天以前 b53fff11e6f0d560594834de32886239cbba90a3
STM32H743/APL/motion_control_task.c
@@ -13,6 +13,8 @@
#include <string.h>
#include <math.h>
#define ENABLE_MC_CTRL_LOG 1
#include "DBG.h"
#include "GPS.h"
#include "PythonLink.h"
@@ -23,6 +25,7 @@
#include "pwm_ctrol.h"
#include "motion_mode.h"
#include "SBUS.h"
#include "AppConfig.h"
#define RAD2DEG                      (57.29577951308232f)
@@ -82,6 +85,11 @@
            &g_motion_config,
            g_motion_path_xy,
            g_motion_path_point_count);
    DBG_Printf("[MC_INIT] Path loaded: count=%u, first_point=(%.2f,%.2f)\r\n",
               g_motion_path_point_count,
               g_motion_path_xy[0],
               g_motion_path_xy[1]);
    BaseType_t ret = xTaskCreate(
        MotionControl_TaskEntry,
@@ -137,6 +145,12 @@
                g_last_heading_deg = gprmi.m_fHeadingAngle;
                g_last_pitch_deg = gprmi.m_fPitchAngle;
                g_last_roll_deg = gprmi.m_fRollAngle;
                /* 检测pose_valid状态变化 */
                if (g_last_pose_valid == HIDO_FALSE)
                {
                    HIDO_Debug2("[MC_POSE]pose_valid: FALSE->TRUE (GPS recovered)\r\n");
                }
                g_last_pose_valid = HIDO_TRUE;
                g_last_gps_ms = now;
                g_last_gprmi_tow = gps_timestamp;
@@ -145,6 +159,12 @@
        }
        else if (gps_valid == HIDO_FALSE && (now - g_last_gps_ms) > 200U)
        {
            /* 检测pose_valid状态变化 */
            if (g_last_pose_valid == HIDO_TRUE)
            {
                HIDO_Debug2("[MC_POSE]pose_valid: TRUE->FALSE (GPS timeout, age=%ums)\r\n",
                            now - g_last_gps_ms);
            }
            g_motion_state.pose_valid = HIDO_FALSE;
            g_last_pose_valid = HIDO_FALSE;
        }
@@ -166,7 +186,40 @@
        /* --- 2) 调用运动控制器(固定 75 Hz dt) --- */
        MC_Output output;
        HIDO_BOOL sbus_valid = (SBUS_IsSignalValid(MOTION_SBUS_TIMEOUT_MS) == HIDO_TRUE);
        HIDO_UINT16 ch8 = SBUS_GetChannel(MOTION_SBUS_AUTO_CHANNEL);
        HIDO_UINT16 ch8_raw = SBUS_GetChannel(MOTION_SBUS_AUTO_CHANNEL);
        /* 过滤SBUS异常值:
         * 1024 = SBUS_CENTER_VALUE(信号丢失时的默认返回值)
         * <172 或 >1811 = SBUS有效范围之外(对应PWM 1000-2000)
         * 当检测到异常值时,保持之前的有效值不变 */
        static HIDO_UINT16 ch8 = 1000;  /* 默认手动模式 */
        static HIDO_UINT32 s_ch8_failsafe_count = 0;
        static HIDO_UINT16 s_ch8_last_valid = 1000;
        /* 判断是否为有效SBUS值:172-1811范围内,且不是精确的1024 */
        HIDO_BOOL is_valid = (ch8_raw >= 172 && ch8_raw <= 1811 && ch8_raw != 1024);
        if (is_valid)
        {
            ch8 = ch8_raw;  /* 只更新有效值 */
            s_ch8_last_valid = ch8_raw;
            if (s_ch8_failsafe_count > 0)
            {
                DBG_Printf("[MC_CTRL] CH8 recovered from failsafe, count=%u, new value=%u\r\n",
                           s_ch8_failsafe_count, ch8);
                s_ch8_failsafe_count = 0;
            }
        }
        else
        {
            /* 异常值,保持之前的值,记录failsafe事件 */
            s_ch8_failsafe_count++;
            if (s_ch8_failsafe_count == 1)
            {
                DBG_Printf("[MC_CTRL] CH8 failsafe detected (%u), keeping previous value=%u\r\n",
                           ch8_raw, ch8);
            }
        }
        
        /* 检查 GPS 状态:必须初始化完成且连接到 GNSS */
        HIDO_BOOL gps_ready = HIDO_FALSE;
@@ -177,15 +230,178 @@
            HIDO_BOOL gnss_connected = ((status & IM23A_STATUS_GNSS_CONNECT) != 0U);
            gps_ready = (init_ok == HIDO_TRUE) && (gnss_connected == HIDO_TRUE);
        }
#if HITL_SIMULATION
        /* 硬件在环仿真模式:
         * 1. 强制 SBUS 信号有效
         * 2. 模拟 CH8 通道:上电前 10 秒为 1000 (手动),10 秒后变为 1800 (自动)
         *    这样可以测试从手动切换到自动的逻辑 (状态机复位)
         */
        sbus_valid = HIDO_TRUE;
        /* 仿真模式下,只要收到过一次有效数据,就认为 GPS 就绪,避免因为标志位未置位导致不启动 */
        if (gps_valid == HIDO_TRUE)
        {
            gps_ready = HIDO_TRUE;
        }
        static HIDO_UINT16 g_hitl_ch8 = 1000;
        static HIDO_UINT32 s_hitl_start_ms = 0;
        if (s_hitl_start_ms == 0)
        {
            s_hitl_start_ms = HAL_GetTick();
        }
        if (HAL_GetTick() - s_hitl_start_ms > 1000U)
        {
            g_hitl_ch8 = 1800; // 10秒后自动切入自动模式
        }
        ch8 = g_hitl_ch8;
#endif
        
        /* 只有当满足所有条件时才执行自动控制:
         * 1. SBUS 信号有效
         * 2. CH8 > 阈值(自动模式开关)
         * 2. CH8 > 阈值(自动模式开关,带迟滞)
         * 3. GPS 初始化完成(FINIT_OK)
         * 4. GPS 连接到 GNSS(GNSS_CONNECT)*/
        if (sbus_valid == HIDO_TRUE && ch8 > MOTION_SBUS_AUTO_THRESHOLD_US && gps_ready == HIDO_TRUE)
        static HIDO_BOOL s_last_auto_condition = HIDO_FALSE;
        static HIDO_BOOL s_last_sbus_valid = HIDO_FALSE;
        static HIDO_BOOL s_last_gps_ready = HIDO_FALSE;
        static HIDO_UINT16 s_last_ch8 = 0;
        static HIDO_BOOL s_ch8_auto_state = HIDO_FALSE;  /* CH8迟滞状态 */
        static HIDO_BOOL s_last_ch8_auto_state = HIDO_FALSE;  /* 上一次的CH8迟滞状态 */
        /* CH8迟滞:防止在阈值附近抖动时反复触发
         * 进入自动模式:CH8 > 1600 (高阈值)
         * 退出自动模式:CH8 < 1400 (低阈值)
         * 死区:1400-1600,在此区间保持之前的状态
         *
         * 连续确认机制:需要连续3次检测到相同状态才切换
         * 这是最稳定的防抖方案,防止瞬态干扰 */
        #define CH8_THRESHOLD_ENTER  1600
        #define CH8_THRESHOLD_EXIT   1400
        #define CH8_CONFIRM_COUNT    3
        static HIDO_UINT8 s_ch8_enter_count = 0;  /* 连续检测到进入自动模式的次数 */
        static HIDO_UINT8 s_ch8_exit_count = 0;   /* 连续检测到退出自动模式的次数 */
        if (s_ch8_auto_state == HIDO_FALSE)
        {
            /* 当前在手动模式,检查是否应该进入自动模式 */
            if (ch8 > CH8_THRESHOLD_ENTER)
            {
                s_ch8_enter_count++;
                s_ch8_exit_count = 0;  /* 重置退出计数 */
                if (s_ch8_enter_count >= CH8_CONFIRM_COUNT)
                {
                    /* 连续3次确认,切换到自动模式 */
                    s_ch8_auto_state = HIDO_TRUE;
                    s_ch8_enter_count = 0;
                    DBG_Printf("[MC_CTRL] CH8 state confirmed: MANUAL -> AUTO (ch8=%u)\r\n", ch8);
                }
            }
            else
            {
                s_ch8_enter_count = 0;  /* 未满足条件,重置计数 */
            }
        }
        else
        {
            /* 当前在自动模式,检查是否应该退出自动模式 */
            if (ch8 < CH8_THRESHOLD_EXIT)
            {
                s_ch8_exit_count++;
                s_ch8_enter_count = 0;  /* 重置进入计数 */
                if (s_ch8_exit_count >= CH8_CONFIRM_COUNT)
                {
                    /* 连续3次确认,切换到手动模式 */
                    s_ch8_auto_state = HIDO_FALSE;
                    s_ch8_exit_count = 0;
                    DBG_Printf("[MC_CTRL] CH8 state confirmed: AUTO -> MANUAL (ch8=%u)\r\n", ch8);
                }
            }
            else
            {
                s_ch8_exit_count = 0;  /* 未满足条件,重置计数 */
            }
        }
        HIDO_BOOL current_auto_condition = (sbus_valid == HIDO_TRUE && s_ch8_auto_state == HIDO_TRUE && gps_ready == HIDO_TRUE);
        /* 检测到从手动切换到自动模式的上升沿:重置状态机从头开始
         * 增加稳定性要求:只有在IDLE或FINISHED状态时才允许重新初始化
         * 如果正在执行任务(GOTO_START或FOLLOW_PATH),忽略模式切换抖动 */
        if (s_last_auto_condition == HIDO_FALSE && current_auto_condition == HIDO_TRUE)
        {
            E_MCStage current_stage = g_motion_state.stage;
            if (current_stage == MC_STAGE_IDLE || current_stage == MC_STAGE_FINISHED)
            {
                DBG_Printf("[MC_CTRL] *** Auto mode triggered! Resetting to GOTO_START ***\r\n");
                DBG_Printf("[MC_CTRL]   SBUS: %d->%d, CH8: %u->%u (state=%d->%d), GPS_READY: %d->%d\r\n",
                           s_last_sbus_valid, sbus_valid,
                           s_last_ch8, ch8,
                           s_last_ch8_auto_state, s_ch8_auto_state,
                           s_last_gps_ready, gps_ready);
                MC_Init(&g_motion_state,
                        &g_motion_config,
                        g_motion_path_xy,
                        g_motion_path_point_count);
            }
            else
            {
                /* 正在执行任务,忽略此次模式切换(可能是信号抖动) */
                DBG_Printf("[MC_CTRL] WARNING: Auto mode re-triggered during %d, ignoring (SBUS interference)\r\n",
                           current_stage);
            }
        }
        /* 检测条件变化并记录 */
        if (s_last_sbus_valid != sbus_valid)
        {
            DBG_Printf("[MC_CTRL] SBUS valid changed: %d -> %d\r\n", s_last_sbus_valid, sbus_valid);
        }
        if (s_last_gps_ready != gps_ready)
        {
            DBG_Printf("[MC_CTRL] GPS ready changed: %d -> %d\r\n", s_last_gps_ready, gps_ready);
        }
        /* 记录状态变化(已在上面连续确认时记录,这里只更新last状态) */
        s_last_ch8_auto_state = s_ch8_auto_state;
        s_last_auto_condition = current_auto_condition;
        s_last_sbus_valid = sbus_valid;
        s_last_gps_ready = gps_ready;
        s_last_ch8 = ch8;
        if (current_auto_condition == HIDO_TRUE)
        {
            /* 记录状态切换 */
            static E_MCStage s_last_stage = MC_STAGE_IDLE;
            E_MCStage prev_stage = g_motion_state.stage;
            MC_Compute(&g_motion_state, 1.0f / MC_CFG_CONTROL_HZ, &output);
            if (prev_stage != output.stage)
            {
                const HIDO_CHAR *prev_label = MotionControl_StageLabel(prev_stage);
                const HIDO_CHAR *curr_label = MotionControl_StageLabel(output.stage);
                DBG_Printf("[MC_CTRL] *** Stage transition: %s -> %s ***\r\n", prev_label, curr_label);
                if (output.stage == MC_STAGE_GOTO_START && prev_stage == MC_STAGE_FOLLOW_PATH)
                {
                    /* 异常:从follow_path回到goto_start!记录详细信息 */
                    DBG_Printf("[MC_CTRL] WARNING: Unexpected transition from FOLLOW_PATH to GOTO_START!\r\n");
                    DBG_Printf("[MC_CTRL]   nearest_idx=%u, path_count=%u, pos=(%.2f,%.2f)\r\n",
                               g_motion_state.nearest_index,
                               g_motion_state.path_count,
                               g_motion_state.pos[0],
                               g_motion_state.pos[1]);
                }
            }
            s_last_stage = output.stage;
        }
        else
        {
@@ -193,10 +409,14 @@
            memset(&output, 0, sizeof(MC_Output));
            output.active = HIDO_FALSE;
            
            /* 移除这里的强制回中,改为在下方根据具体原因处理 */
            // MotionControl_StopOutputs();
            /* 定期打印状态提示 */
            static HIDO_UINT32 s_status_log = 0U;
            if ((s_status_log++ % 100U) == 0U)
            {
#if ENABLE_MC_CTRL_LOG
                if (sbus_valid == HIDO_FALSE)
                {
                    HIDO_Debug2("[MC_CTRL] Waiting: SBUS signal lost\r\n");
@@ -210,12 +430,14 @@
                    HIDO_UINT32 status = gps_valid ? gprmi.m_u32StatusFlags : 0U;
                    HIDO_Debug2("[MC_CTRL] Waiting: GPS not ready (status=0x%08X)\r\n", status);
                }
#endif
            }
        }
        static HIDO_UINT32 s_ctrl_log_idx = 0U;
        if ((s_ctrl_log_idx++ % 10U) == 0U)
        {
#if ENABLE_MC_CTRL_LOG
            /* 使用整数表示法打印浮点数,避免栈损坏 */
            int pos_x_int = (int)g_motion_state.pos[0];
            int pos_x_frac = (int)(fabsf(g_motion_state.pos[0] - pos_x_int) * 100);
@@ -230,11 +452,14 @@
            int tgt_y_int = (int)g_motion_state.current_target_xy[1];
            int tgt_y_frac = (int)(fabsf(g_motion_state.current_target_xy[1] - tgt_y_int) * 100);
            int fwd_int = (int)output.forward_mps;
            int fwd_frac = (int)(fabsf(output.forward_mps - fwd_int) * 100);
            int turn_int = (int)output.turn_rate;
            int turn_frac = (int)(fabsf(output.turn_rate - turn_int) * 100);
            int fwd_frac = (int)(fabsf(output.forward_mps - (float)fwd_int) * 100.0f);
            /* 修复:保留turn的符号,使用带符号格式输出 */
            float turn_abs = fabsf(output.turn_rate);
            int turn_int = (int)turn_abs;
            int turn_frac = (int)((turn_abs - (float)turn_int) * 100.0f);
            char turn_sign = (output.turn_rate < 0.0f) ? '-' : '+';
            
            HIDO_Debug2("[MC_CTRL] stage=%d pos=(%d.%02d,%d.%02d,%d.%02d) hdg=%d.%02d tgt=(%d.%02d,%d.%02d) fwd=%d.%02d turn=%d.%02d path_idx=%u\n",
            HIDO_Debug2("[MC_CTRL] stage=%d pos=(%d.%02d,%d.%02d,%d.%02d) hdg=%d.%02d tgt=(%d.%02d,%d.%02d) fwd=%d.%02d turn=%c%d.%02d path_idx=%u\n",
                        g_motion_state.stage,
                        pos_x_int, pos_x_frac,
                        pos_y_int, pos_y_frac,
@@ -243,8 +468,9 @@
                        tgt_x_int, tgt_x_frac,
                        tgt_y_int, tgt_y_frac,
                        fwd_int, fwd_frac,
                        turn_int, turn_frac,
                        turn_sign, turn_int, turn_frac,
                        g_motion_state.nearest_index);
#endif
        }
        /* --- 3) 根据控制量更新 PWM,并把 forward/turn 回传给 Python --- */
@@ -256,13 +482,55 @@
        }
        else
        {
            if(ch8 > MOTION_SBUS_AUTO_THRESHOLD_US)
             /* 非自动模式下,MotionControl_StopOutputs() 已在上面调用,这里不再重复设置
              * 如果需要手动模式直通,应该在 SBUS 模块处理,或者在这里读取 SBUS 手动通道值并输出
              * 目前逻辑是:非自动模式下,运动控制任务输出回中(安全停止),手动控制由 SBUS 回调或其他机制接管
              * 如果系统设计是 MotionControl 任务在手动模式下也负责透传遥控器信号,则需要修改此处。
              * 假设:手动模式下,SBUS 模块的中断回调会直接控制电机,或者有其他任务处理。
              * 为防止冲突,这里仅保持记录。但上面已经调用 StopOutputs 设置了 PWM 为中值。
              * 如果 SBUS 直接控制电机,这里的 StopOutputs 会与 SBUS 冲突吗?
              * 通常做法:自动模式下本任务控制 PWM;手动模式下本任务不控制 PWM(或者输出无效值)。
              * 但 StopOutputs 显式设置了 1500。如果 SBUS 也是设置 PWM,会有竞争。
              *
              * 修正:如果 ch8 < 1500 (手动模式),本任务不应该 Set_Motor_Pulse(1500),
              * 否则会覆盖 SBUS 的手动控制信号。
              *
              * 修改逻辑:
              * 1. 自动模式 (current_auto_condition == TRUE): ApplyOutput -> Set_Pulse
              * 2. 异常停止 (sbus_valid=FALSE 或 gps_ready=FALSE): StopOutputs -> Set_Pulse(1500)
              * 3. 手动模式 (sbus_valid=TRUE && ch8 < 1500): 不操作 PWM,让权给手动控制逻辑
              */
            if (sbus_valid == HIDO_FALSE)
            {
            MotionControl_StopOutputs();
            applied_steering = MC_CFG_PWM_CENTER_US;
            applied_throttle = MC_CFG_PWM_CENTER_US;
                 /* 遥控器信号丢失:强制停车 */
                 MotionControl_StopOutputs();
                 applied_steering = MC_CFG_PWM_CENTER_US;
                 applied_throttle = MC_CFG_PWM_CENTER_US;
            }
            /* 显式回退到手动模式(遥控器直通在 SBUS 模块中处理,或者在此处完全不输出) */
            else if (ch8 <= MOTION_SBUS_AUTO_THRESHOLD_US)
            {
                 /* 手动模式:不输出 PWM,仅记录(假设 SBUS 模块或其他逻辑在控制电机)
                  * 为了回显正确,这里赋值为 1500 或读取当前 PWM(如果能读到)
                  */
                 applied_steering = MC_CFG_PWM_CENTER_US;
                 applied_throttle = MC_CFG_PWM_CENTER_US;
                 /* 注意:不要调用 MotionControl_StopOutputs(),否则会在此处把手动油门强行归零 */
            }
            else if (gps_ready == HIDO_FALSE)
            {
                 /* 自动模式开关打开,但 GPS 未就绪:安全停车 */
                 MotionControl_StopOutputs();
                 applied_steering = MC_CFG_PWM_CENTER_US;
                 applied_throttle = MC_CFG_PWM_CENTER_US;
            }
            else if (output.stage == MC_STAGE_FINISHED)
            {
                 /* 已到达终点:停止输出 */
                 MotionControl_StopOutputs();
                 applied_steering = MC_CFG_PWM_CENTER_US;
                 applied_throttle = MC_CFG_PWM_CENTER_US;
            }
        }
        g_last_steering_pwm = applied_steering;
        g_last_throttle_pwm = applied_throttle;
@@ -272,11 +540,50 @@
        g_last_pitch_deg = output.pitch_deg;
        g_last_roll_deg = output.roll_deg;
        g_last_pose_valid = g_motion_state.pose_valid;
        g_last_target_valid = output.target_valid;
        /* 检测目标点跳变到(0,0)的情况 */
        static HIDO_BOOL s_warn_zero_target = HIDO_FALSE;
        if (output.target_valid == HIDO_TRUE)
        {
            /* 检测目标点突然跳到接近(0,0)的情况 */
            HIDO_BOOL is_near_zero = (fabsf(output.target_xy[0]) < 0.1f && fabsf(output.target_xy[1]) < 0.1f);
            HIDO_BOOL was_far_from_zero = (fabsf(g_last_target_xy[0]) > 1.0f || fabsf(g_last_target_xy[1]) > 1.0f);
            if (is_near_zero && was_far_from_zero && g_last_target_valid)
            {
                int old_x_int = (int)g_last_target_xy[0];
                int old_x_frac = (int)(fabsf(g_last_target_xy[0] - old_x_int) * 100);
                int old_y_int = (int)g_last_target_xy[1];
                int old_y_frac = (int)(fabsf(g_last_target_xy[1] - old_y_int) * 100);
                HIDO_Debug2("[MC_TGT]WARNING: Target jumped to (0,0)! Previous target=(%d.%02d,%d.%02d) stage=%d\r\n",
                            old_x_int, old_x_frac, old_y_int, old_y_frac, output.stage);
                s_warn_zero_target = HIDO_TRUE;
            }
            else if (!is_near_zero && s_warn_zero_target)
            {
                int new_x_int = (int)output.target_xy[0];
                int new_x_frac = (int)(fabsf(output.target_xy[0] - new_x_int) * 100);
                int new_y_int = (int)output.target_xy[1];
                int new_y_frac = (int)(fabsf(output.target_xy[1] - new_y_int) * 100);
                HIDO_Debug2("[MC_TGT]Target recovered from (0,0) to (%d.%02d,%d.%02d)\r\n",
                            new_x_int, new_x_frac, new_y_int, new_y_frac);
                s_warn_zero_target = HIDO_FALSE;
            }
            g_last_target_xy[0] = output.target_xy[0];
            g_last_target_xy[1] = output.target_xy[1];
            g_last_target_valid = HIDO_TRUE;
        }
        else
        {
            /* target_valid变为FALSE */
            if (g_last_target_valid)
            {
                HIDO_Debug2("[MC_TGT]target_valid: TRUE->FALSE in task (will report 0,0)\r\n");
            }
            g_last_target_valid = HIDO_FALSE;
        }
        g_freq_sample_count++;
@@ -386,6 +693,8 @@
    float target_yaw_rate = output->turn_rate;  // rad/s,正值=左转,负值=右转
    float steering_f = 1500.0f;
    
    /* 调试LOG:打印转向映射过程(低频) */
    if (target_yaw_rate > 0.001f)  // 左转(正角速度)
    {
        steering_f = 1500.0f - target_yaw_rate / MC_CFG_STEERING_K_LEFT;
@@ -402,7 +711,7 @@
            steering_f = 2000.0f;
        }
    }
    else  // 直行:应用转向补偿修正机械偏差
    else  // 直行:应用转向补偿修正机械偏差(死区 ±0.001 rad/s)
    {
        steering_f = 1500.0f + (float)MC_CFG_STEERING_TRIM;
    }