Files
ASER-NAV/App/nav/corridor_ctrl.c

170 lines
6.6 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.
#include "corridor_ctrl.h"
#include <math.h>
#include <stdbool.h>
/* ====================== 内部状态 ====================== */
static CorridorCtrlConfig_t s_cfg;
static bool s_initialized = false;
/* 辅助:浮点数限幅 */
static inline float clampf(float val, float lo, float hi)
{
if (val < lo) return lo;
if (val > hi) return hi;
return val;
}
void CorridorCtrl_Init(const CorridorCtrlConfig_t *config)
{
s_cfg = *config;
s_initialized = true;
}
void CorridorCtrl_Compute(const CorridorState_t *state,
const CorridorObs_t *obs,
float imu_wz,
RawCmd_t *out_cmd)
{
if (!s_initialized) {
out_cmd->v = 0.0f;
out_cmd->w = 0.0f;
out_cmd->flags = 0U;
return;
}
/* ========================================================
* 出沟保护: 当前激光检测到接近出口时,停止使用左右激光控制
* 避免出沟时左右激光数据突变导致车身大幅度转向
* ======================================================== */
bool near_exit = false;
if ((obs->valid_mask & (1U << 4)) != 0U) { /* 前激光有效 (bit 4) */
if (obs->d_front <= s_cfg.exit_front_dist) {
near_exit = true;
}
}
/* ========================================================
* 核心控制律:
* w_cmd = kp_theta * e_th + kd_theta * (-imu_wz) + kp_y * e_y
*
* - kp_theta * e_th : 比例项,车头偏了就回转
* - kd_theta * (-imu_wz) : 微分阻尼,等价于"阻止车头继续转"
* 用 IMU 直接读数做微分项,比差分 e_th 更丝滑无噪声
* - kp_y * e_y : 横向纠偏,车身偏了就产生角速度拉回来
*
* 出沟保护: 接近出口时,仅使用航向保持,不使用横向和角度纠偏
* ======================================================== */
float w_cmd;
bool escape_active = false;
if (near_exit) {
/* 接近出口: 仅保持航向惯性,禁用左右激光控制 */
w_cmd = -(s_cfg.kd_theta * imu_wz);
} else {
/*
* 简化原则:
* - IMU 只管航向 (e_th + wz)
* - 左右激光只管居中
*
* 只要左右两侧都完整有效,就直接用左右平均距离差做横向误差:
* e_y_direct = (d_right - d_left)/2
* 车在正中时 d_left ~= d_right因此误差应接近 0。
*
* 若当前帧双侧不完整,再退回滤波器给出的 e_y。
*/
float e_y_ctrl = state->e_y;
bool left_ok = ((obs->valid_mask & (1U << 0)) != 0U) &&
((obs->valid_mask & (1U << 1)) != 0U);
bool right_ok = ((obs->valid_mask & (1U << 2)) != 0U) &&
((obs->valid_mask & (1U << 3)) != 0U);
if (left_ok && right_ok) {
float d_left = (obs->d_lf + obs->d_lr) * 0.5f;
float d_right = (obs->d_rf + obs->d_rr) * 0.5f;
e_y_ctrl = 0.5f * (d_right - d_left);
}
/* 正常控制: IMU 管航向, 左右激光管居中 */
w_cmd = -(s_cfg.kp_theta * state->e_th
+ s_cfg.kd_theta * imu_wz
+ s_cfg.kp_y * e_y_ctrl);
/* ========================================================
* 近墙脱离保护:
* 当某一侧平均距离已经明显过小,说明车身已经在擦壁或即将擦壁。
* 此时不能只等 EKF 慢慢回中,直接叠加一个远离墙面的转向保护项。
* ======================================================== */
{
bool left_front_ok = ((obs->valid_mask & (1U << 0)) != 0U);
bool left_rear_ok = ((obs->valid_mask & (1U << 1)) != 0U);
bool right_front_ok = ((obs->valid_mask & (1U << 2)) != 0U);
bool right_rear_ok = ((obs->valid_mask & (1U << 3)) != 0U);
float w_escape = 0.0f;
float left_min = 10.0f;
float right_min = 10.0f;
if (left_front_ok && obs->d_lf < left_min) left_min = obs->d_lf;
if (left_rear_ok && obs->d_lr < left_min) left_min = obs->d_lr;
if (right_front_ok && obs->d_rf < right_min) right_min = obs->d_rf;
if (right_rear_ok && obs->d_rr < right_min) right_min = obs->d_rr;
if (left_min < s_cfg.wall_escape_dist) {
float err = s_cfg.wall_escape_dist - left_min;
w_escape -= s_cfg.wall_escape_kp * err; /* 左侧很近 -> 轻微右转 */
escape_active = true;
}
if (right_min < s_cfg.wall_escape_dist) {
float err = s_cfg.wall_escape_dist - right_min;
w_escape += s_cfg.wall_escape_kp * err; /* 右侧很近 -> 轻微左转 */
escape_active = true;
}
w_escape = clampf(w_escape, -s_cfg.wall_escape_w_max, s_cfg.wall_escape_w_max);
w_cmd += w_escape;
}
}
/* 角速度限幅:防止 PD 溢出导致原地打转 */
w_cmd = clampf(w_cmd, -s_cfg.w_max, s_cfg.w_max);
/* ========================================================
* 线速度策略:
* 基础巡航速度 v_cruise
* 当角速度偏大时适当降速(弯道减速),保持运动学协调
* 公式: v = v_cruise * (1 - k * |w/w_max|)
* k 取 0.3~0.5 较保守
* ======================================================== */
float speed_reduction = s_cfg.speed_reduction_k * fabsf(w_cmd) / s_cfg.w_max;
float v_cmd = s_cfg.v_cruise * (1.0f - speed_reduction);
/* 近墙脱离时轻微降速,避免“贴着墙还继续冲” */
if (escape_active) {
v_cmd *= 0.80f;
}
/* 线速度限幅:不允许倒车,不允许超速 */
v_cmd = clampf(v_cmd, 0.0f, s_cfg.v_max);
/* ========================================================
* 置信度降级保护:
* 当滤波器健康度 conf 过低(两边雷达全瞎),
* 说明走廊参照完全丢失,降低线速度防止盲飞
*
* 注意:阈值不宜过高,否则会过度降级导致控制器失效
* ======================================================== */
if (state->conf < 0.2f) {
/* 健康度极低:速度打三折,保持航向惯性滑行 */
v_cmd *= 0.3f;
} else if (state->conf < 0.4f) {
/* 健康度较低(单侧退化):速度打七折 */
v_cmd *= 0.7f;
}
/* 输出赋值 */
out_cmd->t_ms = state->t_ms;
out_cmd->v = v_cmd;
out_cmd->w = w_cmd;
out_cmd->flags = 0U;
}