算法系列之 基于Linux Alsa的AVAS实现

目录

1.概述

[1.1 AVAS简介](#1.1 AVAS简介)

[1.2 AVAS出现的原因](#1.2 AVAS出现的原因)

2.实现方案

[2.1 方案简介](#2.1 方案简介)

[2.2 技术要求](#2.2 技术要求)

2.3方案架构

[3. 关键代码实现](#3. 关键代码实现)

[3.1 Avas_core文件](#3.1 Avas_core文件)

3.1.1数据结构

[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(&params);
    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;
}
相关推荐
仰望星空的凡人2 个月前
一文了解新能源汽车的AVAS
汽车·avas
孤独的追光者2 年前
代码解读 | Hybrid Transformers for Music Source Separation[06]
python·深度学习·机器学习·音频算法·demucs