前言
最近在做智能车项目时,接触到了汽车大灯随动转向技术。传统汽车大灯在夜间转弯时,由于照射方向固定,经常会出现”盲区”,驾驶员很难提前看清弯道情况。而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寄存器映射手册

















暂无评论内容