目录
[1.1 AVAS简介](#1.1 AVAS简介)
[1.2 AVAS出现的原因](#1.2 AVAS出现的原因)
[2.1 方案简介](#2.1 方案简介)
[2.2 技术要求](#2.2 技术要求)
[3. 关键代码实现](#3. 关键代码实现)
[3.1 Avas_core文件](#3.1 Avas_core文件)
[3.1.2 计算频率](#3.1.2 计算频率)
[3.2 audio_engine代码](#3.2 audio_engine代码)
[3.2.1 引擎关键结构](#3.2.1 引擎关键结构)
[3.2.2 产生波形](#3.2.2 产生波形)
[3.2.3 初始化引擎](#3.2.3 初始化引擎)
[3.2.4 播放线程:](#3.2.4 播放线程:)
[3.3 主函数调用](#3.3 主函数调用)
1.概述
1.1 AVAS简介
AVAS(Acoustic Vehicle Alerting System,声学车辆警报系统)是一种用于电动汽车和混合动力汽车的声音警报系统。由于这些车辆在低速行驶时几乎没有发动机噪音,可能对行人(特别是视障人士)构成安全隐患,AVAS的作用就是在车辆低速行驶时发出可识别的声音。
1.2 AVAS出现的原因
- 行人安全: 在低速行驶时,电动汽车几乎无声,行人难以察觉
- 盲人辅助: 视力障碍者依赖听觉判断车辆位置和速度
- 法规要求: 多国已立法要求电动汽车安装AVAS系统
- 事故预防: 降低因车辆无声造成的交通事故
对于AVAS,有相应的国标,最新的是GBT 371532018 电动汽车低速提示音》, 从2024年慢慢开始变成强制性要求。
2.实现方案
2.1 方案简介
本文实现一个AVAS简单算法,算法对应相应的demo,可以在linux系统下运行,基于Linux的ALSA接口。
- 完全符合国标: 严格遵循GBT 371532018标准
- 两种音色: 提供未来科技、正弦波音色
- 智能调节: 声音随车速智能变化
- 自动验证: 内置国标合规性检查
- 可视化分析: 完整的频谱和波形分析工具
2.2 技术要求
1)适用范围
标准适用于最高车速大于20 km/h的电动汽车,要求在车速不大于20 km/h时发出提示音。
2) 速度范围
激活速度: 车速 ≤ 20 km/h
系统响应: 车速变化时声音特性应相应变化
3) 声压级要求
| 测量位置 | 最小声压级 | 最大声压级 |
| 车辆前方2m处 | 56 dB(A) | 75 dB(A) |
| 车辆侧方2m处 | 56 dB(A) | 75 dB(A) |
4) 频率要求
总体频率范围: 160 Hz ~ 5000 Hz
主要频率成分: 至少包含2个1/3倍频程
频率分布: 应包含多个频率成分,避免单一纯音
5) 声音特性要求
- 可识别性: 声音应易于识别为车辆声音
- 方向性: 行人应能判断车辆方向和运动趋势
- 连续性: 车辆行驶时应连续发声
- 变化性: 声音特性应随车速变化
2.3方案架构
AVAS架构如下:

图1 AVAS系统架构
这里AVAS系统比较简单,包括AVAS控制器 AvasController,音频生成器AvasCore以及音频调度引擎AudioEngine。AvasController是总控,根据输入信号,生成音频后,通过音频接口发声。
以下为类图和序列图:

图2 类图

图3序列图
代码文件结构如下:

图4 代码结构
avas_core是核心算法类,audio_engine产生波形,并调用linux alsa接口完成播放
3. 关键代码实现
3.1 Avas_core文件
3.1.1数据结构
cpp
/* 常量定义 */
#define AVAS_MAX_SPEED_KMH 20.0f /* AVAS触发的最大车速 (km/h) */
#define AVAS_MIN_FREQ_HZ 315.0f /* 最小频率 (Hz) */
#define AVAS_MAX_FREQ_HZ 5000.0f /* 最大频率 (Hz) */
#define AVAS_MIN_DB 56.0f /* 最小声压级 (dB) */
#define AVAS_MAX_DB 75.0f /* 最大声压级 (dB) */
/**
* @brief AVAS状态结构体
*/
typedef struct {
float speed_kmh; /* 当前车速 (km/h) */
float frequency_hz; /* 当前提示音频率 (Hz) */
float volume_db; /* 当前音量 (dB) */
bool is_active; /* AVAS系统是否激活 */
} avas_state_t;
3.1.2 计算频率
cpp
/**
* @brief 计算提示音频率(随车速线性增加)
* @param speed_kmh 车速 (km/h)
* @return 频率 (Hz)
*/
static float calculate_frequency(float speed_kmh) {
if (speed_kmh <= 0.0f) {
return AVAS_MIN_FREQ_HZ;
}
/* 频率随车速线性增加: 0 km/h -> 315 Hz, 20 km/h -> 2000 Hz */
float freq = AVAS_MIN_FREQ_HZ +
(speed_kmh / AVAS_MAX_SPEED_KMH) * (2000.0f - AVAS_MIN_FREQ_HZ);
/* 限制在标准范围内 */
if (freq < AVAS_MIN_FREQ_HZ) freq = AVAS_MIN_FREQ_HZ;
if (freq > AVAS_MAX_FREQ_HZ) freq = AVAS_MAX_FREQ_HZ;
return freq;
}
3.1.3 计算音量
cpp
**
* @brief 计算音量(随车速变化)
* @param speed_kmh 车速 (km/h)
* @return 音量 (dB)
*/
static float calculate_volume(float speed_kmh) {
/* 音量随速度略微增加: 0 km/h -> 60 dB, 20 km/h -> 70 dB */
float volume = 60.0f + (speed_kmh / AVAS_MAX_SPEED_KMH) * 10.0f;
/* 限制在标准范围内 */
if (volume < AVAS_MIN_DB) volume = AVAS_MIN_DB;
if (volume > AVAS_MAX_DB) volume = AVAS_MAX_DB;
return volume;
}
3.1.4 更新状态
cpp
/**
* @brief 更新AVAS系统状态
*/
bool avas_update(avas_state_t *state, float speed_kmh) {
if (state == NULL) return false;
/* 确保速度非负 */
if (speed_kmh < 0.0f) speed_kmh = 0.0f;
state->speed_kmh = speed_kmh;
/* GB/T 37153-2018: 车速 <= 20 km/h 时激活AVAS */
if (speed_kmh <= AVAS_MAX_SPEED_KMH) {
state->is_active = true;
state->frequency_hz = calculate_frequency(speed_kmh);
state->volume_db = calculate_volume(speed_kmh);
} else {
state->is_active = false;
state->frequency_hz = 0.0f;
state->volume_db = 0.0f;
}
return state->is_active;
}
3.2 audio_engine代码
3.2.1 引擎关键结构
cpp
/* 音效风格 */
typedef enum {
SOUND_STYLE_SINE = 0, /* 正弦波(平滑科技感) */
SOUND_STYLE_FUTURISTIC = 1 /* 未来感(带谐波) */
} sound_style_t;
/**
* @brief 音频引擎状态
*/
typedef struct {
void *alsa_handle; /* ALSA设备句柄 */
sound_style_t style; /* 当前音效风格 */
float current_freq; /* 当前频率 */
float current_volume; /* 当前音量 */
bool is_playing; /* 是否正在播放 */
uint32_t sample_rate; /* 采样率 */
double phase; /* 波形相位 */
} audio_engine_t;
3.2.2 产生波形
cpp
/**
* @brief 生成正弦波音效
*/
static void generate_sine_wave(int16_t *buffer, int frames, audio_engine_t *engine) {
float amplitude = db_to_amplitude(engine->current_volume);
float freq = engine->current_freq;
for (int i = 0; i < frames; i++) {
float sample = amplitude * sinf(2.0f * M_PI * engine->phase);
buffer[i] = (int16_t)(sample * 32767.0f);
engine->phase += freq / SAMPLE_RATE;
if (engine->phase >= 1.0) engine->phase -= 1.0;
}
}
cpp
/**
* @brief 生成未来感音效(基础频率+谐波)
*/
static void generate_futuristic_sound(int16_t *buffer, int frames, audio_engine_t *engine) {
float amplitude = db_to_amplitude(engine->current_volume);
float freq = engine->current_freq;
for (int i = 0; i < frames; i++) {
/* 基础频率 */
float sample = 0.6f * sinf(2.0f * M_PI * engine->phase);
/* 添加三次谐波(增加科技感) */
sample += 0.25f * sinf(2.0f * M_PI * engine->phase * 3.0f);
/* 添加五次谐波(增加丰富度) */
sample += 0.15f * sinf(2.0f * M_PI * engine->phase * 5.0f);
buffer[i] = (int16_t)(sample * amplitude * 32767.0f);
engine->phase += freq / SAMPLE_RATE;
if (engine->phase >= 1.0) engine->phase -= 1.0;
}
}
3.2.3 初始化引擎
cpp
/**
* @brief 初始化音频引擎
*/
int audio_engine_init(audio_engine_t *engine, sound_style_t style) {
if (engine == NULL) return -1;
memset(engine, 0, sizeof(audio_engine_t));
engine->style = style;
engine->sample_rate = SAMPLE_RATE;
engine->phase = 0.0;
/* 打开ALSA设备 */
snd_pcm_t *pcm_handle;
int err = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) {
return -1;
}
/* 配置ALSA参数 */
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(pcm_handle, params, CHANNELS);
snd_pcm_hw_params_set_rate_near(pcm_handle, params, &engine->sample_rate, 0);
err = snd_pcm_hw_params(pcm_handle, params);
if (err < 0) {
snd_pcm_close(pcm_handle);
return -1;
}
snd_pcm_prepare(pcm_handle);
/* 启动播放线程 */
g_audio_data.pcm_handle = pcm_handle;
g_audio_data.engine = engine;
g_audio_data.running = true;
if (pthread_create(&g_audio_data.thread, NULL, audio_playback_thread, &g_audio_data) != 0) {
snd_pcm_close(pcm_handle);
return -1;
}
engine->alsa_handle = pcm_handle;
return 0;
}
3.2.4 播放线程:
cpp
/**
* @brief 音频播放线程
*/
static void *audio_playback_thread(void *arg) {
audio_thread_data_t *data = (audio_thread_data_t *)arg;
int16_t buffer[BUFFER_FRAMES];
while (data->running) {
if (data->engine->is_playing) {
/* 根据风格生成音频 */
if (data->engine->style == SOUND_STYLE_SINE) {
generate_sine_wave(buffer, BUFFER_FRAMES, data->engine);
} else {
generate_futuristic_sound(buffer, BUFFER_FRAMES, data->engine);
}
/* 输出到ALSA */
snd_pcm_sframes_t frames = snd_pcm_writei(data->pcm_handle, buffer, BUFFER_FRAMES);
if (frames < 0) {
frames = snd_pcm_recover(data->pcm_handle, frames, 0);
}
if (frames < 0) {
/* 恢复失败,重新准备 */
snd_pcm_prepare(data->pcm_handle);
}
} else {
/* 静音时短暂休眠 */
usleep(10000); // 10ms
}
}
return NULL;
}
3.3 主函数调用
cpp
int main() {
......
/* 初始化AVAS系统 */
avas_init(&avas_state);
avas_update(&avas_state, current_speed);
/* 初始化音频引擎 */
if (audio_engine_init(&audio, current_style) != 0) {
......
}
/* 主循环 */
while (running) {
/* 显示状态 */
display_status(&avas_state, current_style);
/* 检查按键 */
int key = get_key();
if (key != -1) {
......
}
/* 限制速度范围 */
if (current_speed < 0.0f) current_speed = 0.0f;
if (current_speed > 30.0f) current_speed = 30.0f;
/* 更新AVAS状态 */
avas_update(&avas_state, current_speed);
}
/* 根据AVAS状态控制音频 */
if (avas_state.is_active) {
audio_engine_update(&audio, avas_state.frequency_hz, avas_state.volume_db);
if (!audio.is_playing) {
audio_engine_play(&audio, avas_state.frequency_hz, avas_state.volume_db);
}
} else {
audio_engine_stop(&audio);
}
......
}
/* 清理资源 */
audio_engine_destroy(&audio);
clear_screen();
printf("\n" COLOR_GREEN "感谢使用AVAS演示系统!\n" COLOR_RESET);
return 0;
}