第二章:驾驶员需求扭矩解析

大家好,我是老张。今天咱们聊聊VCU控制里最基础、也最容易被忽视的一环——驾驶员需求扭矩解析。

说白了,就是搞清楚「驾驶员到底想要车怎么走」。你踩油门、踩刹车、选模式、甚至什么都不做,VCU都得读懂你的意图。我在项目里见过太多因为信号处理不到位导致的顿挫、溜坡甚至安全故障。嗯,这一章咱们就把这些坑一个个填上。

核心知识点一览:

  • 加速踏板信号处理(含冗余校验与故障诊断)
  • 制动踏板信号处理(含制动优先逻辑)
  • 驾驶模式选择(经济/运动/雪地等)
  • 蠕行扭矩计算(起步与低速场景)
驾驶员需求扭矩解析流程 加速踏板信号 制动踏板信号 驾驶模式选择 冗余校验 故障诊断 制动优先逻辑 踏板冲突处理 模式映射表 扭矩系数调整 蠕行扭矩计算 最终需求扭矩

2.1 加速踏板信号处理

加速踏板,说白了就是驾驶员跟VCU对话的主要通道。你踩多深,VCU就该给多大扭矩。但这里有个关键问题——信号可靠性。

现在的加速踏板基本都是双路冗余设计,一路信号上升,另一路下降。我习惯叫它们APP1和APP2。正常情况下,APP1 + APP2 ≈ 5V(或某个固定值)。如果这个关系不成立,那信号就有问题。

我的经验:曾经有个项目,APP1和APP2的斜率比标定反了,结果踩到一半扭矩突然跳变。查了三天才发现是硬件焊接时把两路信号接反了。从那以后,我每次做标定都会先手动验证一下两路信号的对应关系。

信号处理流程大致如下:

  1. AD采样——获取原始电压值
  2. 有效性检查——电压是否在合理范围(通常0.5V~4.5V)
  3. 冗余校验——APP1与APP2是否满足预设关系
  4. 故障处理——如果校验失败,进入跛行模式或限制扭矩
  5. 归一化——将电压映射到0%~100%开度

代码实现上,我一般这样写:

/* 加速踏板信号处理 - 简化示例 */
float AppPercent_Calc(uint16_t app1_raw, uint16_t app2_raw)
{
    float app1_percent, app2_percent, app_final;
    
    /* 1. 原始值转百分比 */
    app1_percent = (float)(app1_raw - APP1_MIN) / (APP1_MAX - APP1_MIN) * 100.0f;
    app2_percent = (float)(app2_raw - APP2_MIN) / (APP2_MAX - APP2_MIN) * 100.0f;
    
    /* 2. 范围检查 */
    if((app1_percent < 0.0f) || (app1_percent > 100.0f) ||
       (app2_percent < 0.0f) || (app2_percent > 100.0f))
    {
        /* 信号超限,报故障 */
        SetFault(FAULT_APP_RANGE);
        return 0.0f;
    }
    
    /* 3. 冗余校验:APP1 + APP2 应接近100% */
    if(fabs(app1_percent + app2_percent - 100.0f) > APP_REDUNDANCY_THRESHOLD)
    {
        /* 冗余校验失败,报故障 */
        SetFault(FAULT_APP_REDUNDANCY);
        return 0.0f;
    }
    
    /* 4. 取两路平均值作为最终开度 */
    app_final = (app1_percent + (100.0f - app2_percent)) / 2.0f;
    
    /* 5. 防抖处理(一阶低通滤波) */
    static float app_filtered = 0.0f;
    app_filtered = APP_FILTER_COEFF * app_final + (1.0f - APP_FILTER_COEFF) * app_filtered;
    
    return app_filtered;
}

注意:防抖系数不能设得太小,否则响应太慢。我一般取0.1~0.3之间,具体看硬件噪声水平。太灵敏的话,驾驶员会感觉车「神经质」——明明没动踏板,扭矩却在抖。

2.2 制动踏板信号处理

制动踏板处理,核心就一句话:制动优先

什么意思?就是只要检测到制动踏板被踩下,不管加速踏板是什么状态,VCU都必须立即切断驱动扭矩。这是安全底线,没得商量。

制动踏板信号通常有两种形式:

  • 开关量——踩下/松开,简单粗暴
  • 模拟量——可以知道踩多深,用于能量回收协调

我个人更推荐模拟量方案。为什么?因为你可以根据制动深度来调节能量回收强度,而不是一刀切。我在一个项目里就吃过亏——用的开关量制动,结果每次轻踩刹车能量回收都全开,乘客晕得不行。

制动优先逻辑的伪代码:

/* 制动优先逻辑 */
if(BrakePedal_IsPressed())  /* 制动踏板被踩下 */
{
    /* 立即清零驱动扭矩 */
    DriverDemandTorque = 0;
    
    /* 根据制动深度计算再生制动扭矩 */
    if(BrakePedal_HasAnalogSignal())
    {
        float brake_depth = BrakePedal_GetDepth();  /* 0~100% */
        RegenTorque = BrakeDepthToRegenMap(brake_depth);
    }
    else
    {
        /* 开关量制动,使用固定再生扭矩 */
        RegenTorque = FIXED_REGEN_TORQUE;
    }
}
else
{
    /* 无制动,正常处理加速踏板 */
    DriverDemandTorque = AppPercent_To_Torque(app_percent, drive_mode);
}

关键点:制动优先的响应时间必须小于100ms。我见过一些方案用CAN信号传递制动状态,延迟太大,紧急情况下根本来不及。建议用硬线信号,直接进VCU的IO口。

2.3 驾驶模式选择

驾驶模式,说白了就是给同样的踏板开度配上不同的扭矩响应曲线。

常见的模式有:

模式 特点 适用场景
经济模式 踏板响应平缓,扭矩输出保守 城市通勤,追求续航
标准模式 线性响应,兼顾动力与能耗 日常驾驶
运动模式 踏板初段扭矩大,响应激进 超车、山路、追求驾驶乐趣
雪地模式 扭矩输出柔和,限制大扭矩 湿滑路面,防止打滑

模式切换时,我建议做扭矩平滑过渡。你想想看,如果驾驶员在高速上从经济模式切到运动模式,扭矩突然增大,车会猛地窜出去——这很危险。

我的做法是:模式切换后,目标扭矩按一定斜率逐渐逼近新模式下的扭矩值,过渡时间大约200~500ms。

/* 驾驶模式扭矩映射 - 简化示例 */
float DriveMode_MapTorque(float app_percent, DriveMode_t mode)
{
    float torque_base;
    
    switch(mode)
    {
        case MODE_ECO:
            torque_base = app_percent * 0.7f;  /* 经济模式,扭矩打7折 */
            break;
        case MODE_NORMAL:
            torque_base = app_percent * 1.0f;  /* 标准模式,1:1映射 */
            break;
        case MODE_SPORT:
            torque_base = app_percent * 1.3f;  /* 运动模式,扭矩放大30% */
            break;
        case MODE_SNOW:
            torque_base = app_percent * 0.5f;  /* 雪地模式,扭矩减半 */
            break;
        default:
            torque_base = app_percent * 1.0f;
            break;
    }
    
    /* 限制最大扭矩 */
    torque_base = MIN(torque_base, GetMaxTorqueByMode(mode));
    
    return torque_base;
}

避坑指南:我曾经遇到过一个问题——模式切换时扭矩没有做平滑处理,结果在雪地模式下切回标准模式,扭矩瞬间增大,车轮直接打滑。从那以后,我所有模式切换都加了斜坡函数,宁可慢一点,也不能让驾驶员吓一跳。

2.4 蠕行扭矩计算

蠕行,就是驾驶员不踩加速踏板、也不踩制动踏板时,车辆缓慢前进(或后退)的功能。说白了,就是模拟燃油车怠速的感觉。

蠕行扭矩的计算,核心取决于两个因素:

  • 车速——车速越高,蠕行扭矩越小,最终在某个车速以上完全退出
  • 坡度——上坡时需要更大的蠕行扭矩防止溜坡,下坡时可能不需要甚至需要负扭矩

我一般用查表法:

/* 蠕行扭矩查表 - 基于车速和坡度 */
float CreepTorque_Calc(float vehicle_speed, float road_grade)
{
    /* 车速-蠕行扭矩表(示例数据) */
    const float creep_map[5][2] = {
        {0.0f,  50.0f},   /* 0km/h -> 50Nm */
        {3.0f,  40.0f},   /* 3km/h -> 40Nm */
        {5.0f,  25.0f},   /* 5km/h -> 25Nm */
        {8.0f,  10.0f},   /* 8km/h -> 10Nm */
        {10.0f, 0.0f}     /* 10km/h -> 0Nm */
    };
    
    float torque_base = Interp1D(vehicle_speed, creep_map, 5);
    
    /* 坡度补偿:每1%坡度增加5Nm */
    float grade_compensation = road_grade * 5.0f;
    
    float torque_final = torque_base + grade_compensation;
    
    /* 限制蠕行扭矩范围 */
    torque_final = CLAMP(torque_final, CREEP_TORQUE_MIN, CREEP_TORQUE_MAX);
    
    return torque_final;
}

注意:蠕行扭矩不能太大,否则在平地上起步会感觉「窜」。我一般把最大蠕行扭矩限制在50~80Nm之间,具体看整车质量。另外,蠕行退出车速建议设在8~12km/h,太早退出的话,走走停停的工况会很别扭。

还有一个容易被忽略的点——蠕行与能量回收的衔接。当车速降到蠕行车速以下时,如果驾驶员还在滑行,VCU应该从能量回收平滑过渡到蠕行扭矩。否则你会感觉到一个明显的「拖拽感」突然消失,然后车又往前窜一下。嗯,这个过渡我一般用200ms左右的斜坡。


好了,这一章的内容就到这里。驾驶员需求扭矩解析,说白了就是「读懂人、读懂车、读懂路」。信号处理是基础,模式选择是策略,蠕行是细节。每一个环节都马虎不得。

如果你在实际项目中遇到什么奇怪的问题,欢迎来公众号找我聊聊。毕竟,做VCU的坑,我一个人踩不完,大家一起踩才热闹。

公众号:蓝海资料掘金营,微信deep3321