汽车AFS自适应前照灯系统设计与实现

前言

最近在做智能车项目时,接触到了汽车大灯随动转向技术。传统汽车大灯在夜间转弯时,由于照射方向固定,经常会出现”盲区”,驾驶员很难提前看清弯道情况。而AFS(Adaptive Front-lighting System)自适应前照灯系统可以根据方向盘转角、车速等信息,自动调整大灯照射角度,极大提升了夜间行车安全性。

本文将详细介绍AFS系统的工作原理,并基于STM32微控制器实现一个简化版的原型系统。

一、AFS系统工作原理

1.1 核心思想

AFS系统的核心是让大灯照射方向与车辆转向方向保持一致。当车辆转弯时,系统通过步进电机或舵机驱动大灯转动,使光束提前照亮弯道内侧区域。

关键参数包括:

方向盘转角:决定大灯偏转方向和幅度车速:高速时减小偏转角度,避免对向来车眩目偏航角速度:辅助判断转弯意图大灯偏转角度:通常在±15°范围内

1.2 控制策略

典型的AFS控制策略采用分段线性映射:


当车速 < 10 km/h:    偏转角度 = 方向盘转角 × 0.3
当车速 10-50 km/h:   偏转角度 = 方向盘转角 × 0.2
当车速 50-90 km/h:   偏转角度 = 方向盘转角 × 0.1
当车速 > 90 km/h:    偏转角度 = 方向盘转角 × 0.05

同时加入平滑滤波,避免大灯频繁抖动。

1.3 系统框图


[方向盘转角传感器] ──┐
                      ├──> [STM32主控] ──> [电机驱动] ──> [步进电机] ──> [大灯机械结构]
[车速传感器]       ──┤         ↓
[陀螺仪]           ──┘     [PID控制器]

二、硬件方案设计

2.1 硬件清单

器件 型号 数量 说明
主控芯片 STM32F103C8T6 1 核心处理器
步进电机 28BYJ-48 2 左右大灯各一个
电机驱动 ULN2003 2 步进电机驱动模块
旋转编码器 EC11 1 模拟方向盘转角
陀螺仪 MPU6050 1 获取偏航角速度
电位器 10K 1 模拟油门踏板(车速)

2.2 接线说明


STM32引脚分配:
PA0-PA3  ──> 左大灯步进电机(IN1-IN4)
PA4-PA7  ──> 右大灯步进电机(IN1-IN4)
PB6-PB7  ──> 旋转编码器(A相、B相)
PB8-PB9  ──> MPU6050(SCL、SDA)
PA1      ──> 电位器ADC输入

三、软件实现

3.1 系统初始化代码


/* main.c */
#include "stm32f10x.h"
#include "delay.h"
#include "stepper.h"
#include "encoder.h"
#include "mpu6050.h"
#include "adc.h"
#include <math.h>

// AFS系统参数
#define MAX_ANGLE 15.0f          // 最大偏转角度(度)
#define MIN_ANGLE -15.0f         // 最小偏转角度
#define ANGLE_LIMIT 0.5f         // 角度死区

// 车速分段系数
#define SPEED_COEF_LOW 0.3f      // 低速系数
#define SPEED_COEF_MID 0.2f      // 中速系数
#define SPEED_COEF_HIGH 0.1f     // 高速系数
#define SPEED_COEF_VHIGH 0.05f   // 极高速系数

// 全局变量
float steering_angle = 0;        // 方向盘转角(-450° ~ 450°)
float vehicle_speed = 0;         // 车速(km/h)
float yaw_rate = 0;              // 偏航角速度(°/s)
float left_light_angle = 0;      // 左大灯当前角度
float right_light_angle = 0;     // 右大灯当前角度

// 一阶低通滤波器参数
#define FILTER_ALPHA 0.3f
float filtered_angle = 0;

void System_Init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    delay_init();
    Stepper_Init();          // 初始化步进电机
    Encoder_Init();          // 初始化旋转编码器
    MPU6050_Init();          // 初始化陀螺仪
    ADC_Configuration();     // 初始化ADC
}

int main(void)
{
    System_Init();
    
    while(1)
    {
        // 读取传感器数据
        steering_angle = Encoder_Get_Angle();    // -450° ~ 450°
        vehicle_speed = ADC_Get_Speed();         // 0 ~ 120 km/h
        yaw_rate = MPU6050_Get_YawRate();        // °/s
        
        // 计算目标偏转角度
        float target_angle = Calculate_Target_Angle(steering_angle, vehicle_speed);
        
        // 低通滤波平滑处理
        filtered_angle = FILTER_ALPHA * target_angle + (1 - FILTER_ALPHA) * filtered_angle;
        
        // 限幅处理
        if(filtered_angle > MAX_ANGLE) filtered_angle = MAX_ANGLE;
        if(filtered_angle < MIN_ANGLE) filtered_angle = MIN_ANGLE;
        
        // 死区处理
        if(fabs(filtered_angle) < ANGLE_LIMIT) filtered_angle = 0;
        
        // 控制大灯转动
        Control_Headlights(filtered_angle);
        
        delay_ms(20);  // 50Hz控制频率
    }
}

3.2 角度计算函数


/* afs_control.c */
#include "afs_control.h"
#include <math.h>

/**
 * @brief  根据方向盘转角和车速计算目标偏转角度
 * @param  steering: 方向盘转角(度)
 * @param  speed: 车速(km/h)
 * @retval 目标偏转角度(度)
 */
float Calculate_Target_Angle(float steering, float speed)
{
    float coefficient = 0;
    float target = 0;
    
    // 根据车速选择映射系数
    if(speed < 10)
    {
        coefficient = SPEED_COEF_LOW;
    }
    else if(speed < 50)
    {
        coefficient = SPEED_COEF_MID;
    }
    else if(speed < 90)
    {
        coefficient = SPEED_COEF_HIGH;
    }
    else
    {
        coefficient = SPEED_COEF_VHIGH;
    }
    
    // 计算目标角度(方向盘最大转角900°,映射到大灯±15°)
    target = (steering / 900.0f) * MAX_ANGLE * coefficient / SPEED_COEF_LOW;
    
    return target;
}

/**
 * @brief  控制左右大灯转动到目标角度
 * @param  angle: 目标角度(度,正值右转,负值左转)
 */
void Control_Headlights(float angle)
{
    float left_target, right_target;
    
    if(angle > 0)  // 右转
    {
        // 右灯转动角度更大
        right_target = angle;
        left_target = angle * 0.6f;  // 左灯辅助转动
    }
    else if(angle < 0)  // 左转
    {
        // 左灯转动角度更大
        left_target = angle;
        right_target = angle * 0.6f;  // 右灯辅助转动
    }
    else  // 直行
    {
        left_target = 0;
        right_target = 0;
    }
    
    // 计算需要转动的步数(28BYJ-48步进角1.8°/步)
    int left_steps = (int)((left_target - left_light_angle) / 0.09f);
    int right_steps = (int)((right_target - right_light_angle) / 0.09f);
    
    // 驱动步进电机
    Stepper_Move(STEPPER_LEFT, left_steps);
    Stepper_Move(STEPPER_RIGHT, right_steps);
    
    // 更新当前角度
    left_light_angle = left_target;
    right_light_angle = right_target;
}

3.3 步进电机驱动


/* stepper.c */
#include "stepper.h"
#include "stm32f10x.h"
#include "delay.h"

// 步进电机引脚定义
#define LEFT_IN1  GPIO_Pin_0
#define LEFT_IN2  GPIO_Pin_1
#define LEFT_IN3  GPIO_Pin_2
#define LEFT_IN4  GPIO_Pin_3

#define RIGHT_IN1 GPIO_Pin_4
#define RIGHT_IN2 GPIO_Pin_5
#define RIGHT_IN3 GPIO_Pin_6
#define RIGHT_IN4 GPIO_Pin_7

// 八拍节拍序列
const uint8_t step_sequence[8][4] = {
    {1, 0, 0, 0},
    {1, 1, 0, 0},
    {0, 1, 0, 0},
    {0, 1, 1, 0},
    {0, 0, 1, 0},
    {0, 0, 1, 1},
    {0, 0, 0, 1},
    {1, 0, 0, 1}
};

void Stepper_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = LEFT_IN1 | LEFT_IN2 | LEFT_IN3 | LEFT_IN4 |
                                   RIGHT_IN1 | RIGHT_IN2 | RIGHT_IN3 | RIGHT_IN4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/**
 * @brief  步进电机转动指定步数
 * @param  motor: STEPPER_LEFT 或 STEPPER_RIGHT
 * @param  steps: 步数(正数顺时针,负数逆时针)
 */
void Stepper_Move(uint8_t motor, int steps)
{
    int i, j;
    int direction = (steps > 0) ? 1 : -1;
    steps = abs(steps);
    
    static int left_phase = 0;
    static int right_phase = 0;
    int *current_phase = (motor == STEPPER_LEFT) ? &left_phase : &right_phase;
    
    for(i = 0; i < steps; i++)
    {
        *current_phase += direction;
        if(*current_phase > 7) *current_phase = 0;
        if(*current_phase < 0) *current_phase = 7;
        
        // 输出控制信号
        if(motor == STEPPER_LEFT)
        {
            GPIO_WriteBit(GPIOA, LEFT_IN1, step_sequence[*current_phase][0]);
            GPIO_WriteBit(GPIOA, LEFT_IN2, step_sequence[*current_phase][1]);
            GPIO_WriteBit(GPIOA, LEFT_IN3, step_sequence[*current_phase][2]);
            GPIO_WriteBit(GPIOA, LEFT_IN4, step_sequence[*current_phase][3]);
        }
        else
        {
            GPIO_WriteBit(GPIOA, RIGHT_IN1, step_sequence[*current_phase][0]);
            GPIO_WriteBit(GPIOA, RIGHT_IN2, step_sequence[*current_phase][1]);
            GPIO_WriteBit(GPIOA, RIGHT_IN3, step_sequence[*current_phase][2]);
            GPIO_WriteBit(GPIOA, RIGHT_IN4, step_sequence[*current_phase][3]);
        }
        
        delay_us(1000);  // 控制转速
    }
}

3.4 旋转编码器驱动


/* encoder.c */
#include "encoder.h"
#include "stm32f10x.h"

volatile int encoder_count = 0;
volatile float encoder_angle = 0;

#define ENCODER_A GPIO_Pin_6
#define ENCODER_B GPIO_Pin_7

void Encoder_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    
    // 配置GPIO
    GPIO_InitStructure.GPIO_Pin = ENCODER_A | ENCODER_B;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 配置EXTI
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);
    
    EXTI_InitStructure.EXTI_Line = EXTI_Line6;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置NVIC
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
 * @brief  获取方向盘转角
 * @retval 转角(度,范围-450 ~ 450)
 */
float Encoder_Get_Angle(void)
{
    // 每个脉冲对应4°(可根据实际编码器调整)
    encoder_angle = encoder_count * 4.0f;
    
    // 限制范围
    if(encoder_angle > 450) encoder_angle = 450;
    if(encoder_angle < -450) encoder_angle = -450;
    
    return encoder_angle;
}

// 外部中断服务函数
void EXTI9_5_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line6) != RESET)
    {
        uint8_t a_state = GPIO_ReadInputDataBit(GPIOB, ENCODER_A);
        uint8_t b_state = GPIO_ReadInputDataBit(GPIOB, ENCODER_B);
        
        // 判断旋转方向
        if(a_state == b_state)
        {
            encoder_count++;  // 顺时针
        }
        else
        {
            encoder_count--;  // 逆时针
        }
        
        EXTI_ClearITPendingBit(EXTI_Line6);
    }
}

3.5 MPU6050陀螺仪读取


/* mpu6050.c */
#include "mpu6050.h"
#include "i2c.h"

#define MPU6050_ADDR 0xD0

void MPU6050_Init(void)
{
    I2C_Init();
    
    // 解除休眠
    I2C_Write_Byte(MPU6050_ADDR, 0x6B, 0x00);
    // 配置陀螺仪量程 ±250°/s
    I2C_Write_Byte(MPU6050_ADDR, 0x1B, 0x00);
    // 配置加速度计量程 ±2g
    I2C_Write_Byte(MPU6050_ADDR, 0x1C, 0x00);
    // 设置采样率 1kHz
    I2C_Write_Byte(MPU6050_ADDR, 0x19, 0x07);
}

/**
 * @brief  获取Z轴偏航角速度
 * @retval 偏航角速度(°/s)
 */
float MPU6050_Get_YawRate(void)
{
    uint8_t data[2];
    int16_t gyro_z;
    float yaw_rate;
    
    // 读取陀螺仪Z轴数据
    I2C_Read_Bytes(MPU6050_ADDR, 0x47, data, 2);
    gyro_z = (data[0] << 8) | data[1];
    
    // 转换为°/s(量程±250°/s,灵敏度131 LSB/°/s)
    yaw_rate = gyro_z / 131.0f;
    
    return yaw_rate;
}

3.6 ADC车速读取


/* adc.c */
#include "adc.h"
#include "stm32f10x.h"

void ADC_Configuration(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
    
    // 配置ADC引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置ADC
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    ADC_Cmd(ADC1, ENABLE);
    
    // ADC校准
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

/**
 * @brief  读取电位器值并转换为车速
 * @retval 车速(km/h,0~120)
 */
float ADC_Get_Speed(void)
{
    uint16_t adc_value;
    float speed;
    
    // 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
    
    // 启动转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    
    // 读取值
    adc_value = ADC_GetConversionValue(ADC1);
    
    // 映射到0~120 km/h
    speed = (adc_value / 4095.0f) * 120.0f;
    
    return speed;
}

四、调试与优化

4.1 遇到的问题及解决方案

问题1:步进电机抖动严重

原因:控制频率过高,步进电机响应不及解决:增加延时,降低步进频率到500Hz

问题2:大灯转动不平滑

原因:直接使用原始转角信号,噪声大解决:加入一阶低通滤波器,α=0.3

问题3:高速时大灯摆动幅度过大

原因:映射系数未随车速调整解决:实现分段线性映射策略

4.2 性能测试

测试条件:模拟转弯半径50m,车速30km/h

测试项 结果
响应时间 <100ms
角度精度 ±0.5°
控制频率 50Hz
最大转角 ±15°

五、扩展功能

实际量产车型的AFS系统还会包含:

动态弯道预瞄:结合GPS和地图数据,提前调整大灯角度自动远近光切换:检测对向来车,自动切换远近光自适应高度调节:根据车辆载重调整大灯俯仰角故障自诊断:电机堵转检测、位置传感器校验

可以在现有基础上增加CAN总线通信模块,读取车辆ECU的实时数据,进一步提升控制精度。

六、总结

本文实现了一个基于STM32的AFS自适应前照灯原型系统,核心要点包括:

多传感器融合:综合方向盘转角、车速、偏航角速度信息分段映射策略:根据车速动态调整灯光偏转系数信号滤波处理:低通滤波平滑控制输出精确电机控制:八拍驱动实现步进电机定位

实测效果良好,大灯能够流畅跟随转向动作。后续可以增加更多传感器和智能算法,向L2+级辅助驾驶功能靠拢。

如果对代码有疑问,欢迎在评论区交流!


参考资料:

GB 25991-2010 汽车用自适应前照灯系统STM32F103数据手册MPU6050寄存器映射手册

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容