当前位置:首页 > 物联网 > 智能应用
[导读]导航“前方300米右转”播出来的同时,中控屏卡片、仪表提示条、HUD箭头也得同一时刻出来;语音被打断时屏幕卡片不能还赖着不消失。很多项目把音频和UI写成两条独立逻辑——“导航模块调TTS”和“UI自己刷页面”,结果就是声音先响、界面晚半秒,或者语音助手插话后屏幕还显示旧导航卡片。正确做法是导航只发事件,音频和显示都订阅同一事件源。


导航“前方300米右转”播出来的同时,中控屏卡片、仪表提示条、HUD箭头也得同一时刻出来;语音被打断时屏幕卡片不能还赖着不消失。很多项目把音频和UI写成两条独立逻辑——“导航模块调TTS”和“UI自己刷页面”,结果就是声音先响、界面晚半秒,或者语音助手插话后屏幕还显示旧导航卡片。正确做法是导航只发事件,音频和显示都订阅同一事件源。

一、统一事件源:导航只产事件

导航规划模块别直接调TTS、也别直接改UI控件,只往事件总线发结构化事件:

typedef enum {

   NAV_EVT_NONE,

   NAV_EVT_TURN,        // 转向提示

   NAV_EVT_LANE,        // 车道引导

   NAV_EVT_REPLAN,      // 路线重算

   NAV_EVT_ARRIVE

} nav_evt_t;


typedef struct {

   nav_evt_t type;

   uint32_t  evt_id;        // 去重用,同ID只处理一次

   uint64_t  ts_ns;         // 统一时间戳

   uint8_t   priority;      // 0最高

   char      text[64];      // TTS文本

   int16_t   dist_m;

   uint8_t   dir;           // 左/右/直

   uint16_t  icon_id;

} nav_event_t;

导航产生事件→总线分发→音频服务挑text播报、HMI服务挑icon/text画卡片。两边互不调用,只认evt_id和ts。

二、音频侧:焦点+Ducking

导航语音不是“暂停音乐播自己”,是临时抢占+压低媒体:

申请TRANSIENT_MAY_DUCK,音乐降音量不暂停

告警/来电优先级高于导航,导航让位

语音助手唤醒时,导航后续排队TTS必须拦截,别残音灌喇叭

播报完成释放焦点,媒体恢复

void audio_on_nav(nav_event_t*e){

   if(audio_focus_busy(CALL)) return;          // 通话中不打断

   if(asr_listening()){ queue_defer(e); return; }

   audio_request_focus(USAGE_NAVIGATION, TRANSIENT_MAY_DUCK);

   media_duck(0.5f);

   tts_speak(e->text);

}

离线TTS模块(UART TTS)也走这套:文本下发前先抢焦点,播完发停止。

三、显示侧:同事件同帧

HMI订阅同一个nav_event,按evt_id更新卡片:

void hmi_on_nav(nav_event_t*e){

   if(e->evt_id == last_nav_id) return;        // 去重

   last_nav_id = e->evt_id;

   show_nav_card(e->text, e->dist_m, e->dir, e->icon_id);

   set_card_timeout(rule_timeout(e->type));

}

同步要点:

同时间戳提交:音频开始播的帧,HMI也在同帧commit,别各等各的定时器

多屏状态同源:中控、仪表、HUD都读同一份nav_state,不各存各的

VSync对齐:多屏用统一垂直同步,避免中控已翻页仪表还停上一步

卡片显示时长按事件类型定:转向提示随播报结束消失,限速/车道引导可常驻

四、打断回收:语音停卡片也停

最常见bug:TTS被语音助手打断,屏幕导航卡片还亮着。

规则放仲裁服务里统一管:

void nav_arbiter_on_interrupt(audio_src_t who){

   if(who == VOICE_ASST || who == CALL){

       cancel_pending_tts();          // 不再排新句

       hmi_hide_nav_card();           // 卡片回收

       media_duck_restore();

   }

}

显示层不自己判断“语音是不是在播”,只听仲裁结果。这样音频策略和UI回收是同一份真相源。

五、去重与节流

导航GPS抖动会连发同内容事件,必须拦:

evt_id去重:同ID不重播

距离阈值:距离变化<20m不刷新卡片

文本归一:同句不同GPS点合并

队列只留最新:连续转向提示只保最后一条

if(strcmp(e->text,last_text)==0 && e->type==NAV_EVT_TURN)

   return;

否则“右转”在300m/280m/260m连播三次,用户直接烦。

六、分层架构落法

导航引擎

  └─ nav_event ─▶ 事件总线(SOME/IP/DDS/Binder)

       ├─▶ 音频仲裁服务 → TTS/Media/Ducking

       └─▶ HMI状态服务 → 中控/仪表/HUD卡片

音频和显示都从总线拿事件,谁也不依赖谁;仲裁服务单独管优先级。

七、落坑清单

导航直接调TTS+直接改UI→音画不同步

语音打断只停声音不收卡片→屏显旧提示

同句GPS抖动重复播报→用户烦

媒体用暂停不用duck→音乐断一下很突兀

中控仪表各存nav状态→两屏不一致

多屏不用统一VSync→刷新错拍

告警音和导航同优先级→互相打架

卡片超时写死3秒→转向提示早灭

TTS回调里直接画UI→线程跨界卡顿

evt_id不记录→重连后重复弹窗

八、落地顺序

导航只产nav_event,不带音频/UI动作

事件总线分发,音频/HMI各自订阅

音频服务做焦点仲裁+Ducking+打断拦截

HMI按evt_id更新卡片,同帧commit

多屏走统一nav_state+VSync

打断事件统一回收卡片和TTS队列

evt_id去重+距离节流

告警/通话优先级高于导航,规则进仲裁服务

车载音画联动的本质是“一个事件、两条通道、同一仲裁”:导航不碰喇叭也不碰控件,音频走焦点管理保听清,显示走状态同源保多屏一致,打断逻辑放中间层统一收口。代码里最该避免的就是导航模块里顺手写tts_speak()又顺手set_label()——这两条一耦合,语音助手一插话屏幕就救不回来。把事件源收住,音频策略和HMI回收自然就能同拍。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭