17、车载地图与导航:Google Maps Automotive API;导航应用开发(路线规划、实时交通);HUD与仪表盘导航投射;POI搜索与语音导航
各位同学,今天我们来聊聊车载系统里最“重”的一个模块——地图与导航。说实话,在IVI开发中,地图导航的复杂度能排进前三。我当年刚接触这个领域时,以为就是调个SDK画个路线,结果被各种坐标系、投影变换、HUD刷新率问题折磨得够呛。今天我把这些坑都给你们趟一遍,咱们直接上干货。
17.1 Google Maps Automotive API 接入要点
Google Maps Automotive API 和普通手机版的 Maps SDK 不太一样。它专门为车载环境做了裁剪,比如限制了某些手势操作(防止驾驶员分心),也提供了 Car App 专用的生命周期管理。
接入时,你需要在 AndroidManifest 里声明 automotive 应用,并申请地图权限。我个人习惯把 API Key 放在 build.gradle 的 manifestPlaceholders 里,而不是硬编码在清单文件中——这样换环境时只需要改一个地方。
核心配置示例:
// build.gradle (app级别)
android {
defaultConfig {
manifestPlaceholders = [
googleMapsApiKey: "YOUR_API_KEY_HERE"
]
}
}
// AndroidManifest.xml
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${googleMapsApiKey}" />
<uses-library
android:name="com.google.android.maps"
android:required="true" />
我的经验: 在车载设备上调试地图时,记得开启 android:hardwareAccelerated="true"。我曾经遇到过地图滑动卡顿,排查了半天,最后发现是硬件加速没开——车载 GPU 如果不走硬件加速,地图渲染帧率直接掉到 20fps 以下。
17.2 导航应用开发:路线规划与实时交通
导航的核心逻辑其实就三件事:算路、引导、重算。算路用的是 Directions API,引导靠的是 Navigation SDK,重算则是当驾驶员偏离路线时自动触发。
路线规划时,我建议你们用 PolylineOptions 把路线画在地图上,同时设置 zIndex 确保路线在最上层。实时交通数据通过 TrafficLayer 叠加显示,代码很简单:
// 开启实时交通图层
val trafficLayer = googleMap.getTrafficLayer()
trafficLayer.isEnabled = true
// 请求路线(使用 Directions API)
val directionsUrl = "https://maps.googleapis.com/maps/api/directions/json" +
"?origin=" + startLat + "," + startLng +
"&destination=" + endLat + "," + endLng +
"&traffic_model=best_guess" +
"&departure_time=now" +
"&key=" + apiKey
// 解析返回的 JSON,提取 polyline 点集
// 然后绘制到地图上
val polylineOptions = PolylineOptions()
.addAll(points)
.width(12f)
.color(Color.BLUE)
.zIndex(100f)
googleMap.addPolyline(polylineOptions)
注意: 实时交通数据是有使用配额的。免费版每天有调用次数限制,生产环境一定要走付费方案。我曾经在测试阶段不小心写了个死循环请求交通数据,结果一天就把一个月的配额用完了……
实时交通的更新频率,我建议控制在 30 秒一次。太频繁了浪费流量,太慢了又不够实时。你想想看,高速公路上 30 秒能跑出去一公里,这个频率刚好够用。
17.3 HUD 与仪表盘导航投射
这是车载导航里最炫酷也最麻烦的部分。HUD(抬头显示)和仪表盘导航投射,说白了就是把导航信息从中控屏“搬”到驾驶员正前方的屏幕上。
Android Automotive 提供了 CarNavigationStatusManager 来管理导航状态。你需要把当前的路段、转向箭头、剩余距离等信息,通过这个 Manager 发送给仪表盘或 HUD 系统。
// 获取 CarNavigationStatusManager
val car = Car.createCar(context)
val navManager = car.getCarManager(Car.CAR_NAVIGATION_SERVICE)
as CarNavigationStatusManager
// 构建导航状态
val turnEvent = CarNavigationTurnEvent.Builder(
CarNavigationTurnEvent.TURN_EVENT_TYPE_TURN_LEFT,
150, // 距离(米)
"前方150米左转进入解放路"
).build()
// 发送到仪表盘
navManager.sendNavigationTurnEvent(turnEvent)
HUD 投射的关键点:
- 坐标系转换: HUD 的投影距离一般是 2-3 米,需要把地图坐标转换为 HUD 的物理坐标。这个转换矩阵每个车型都不一样,需要和 Tier1 供应商联调。
- 刷新率匹配: 仪表盘的刷新率通常是 30fps 或 60fps,而地图渲染可能只有 15fps。你需要做帧同步,否则会出现画面撕裂。
- 安全约束: HUD 上不能显示视频、动画等可能分散注意力的内容。Google 的规范里明确写了,HUD 只能显示静态或低动态的导航信息。
我记得在做一个量产项目时,HUD 投射的箭头总是偏右 5 度。排查了三天,最后发现是 HUD 的光学反射镜曲率参数没对齐。嗯,这种硬件层面的问题,软件只能做补偿校准。
17.4 POI 搜索与语音导航
POI(兴趣点)搜索是用户最常用的功能之一。Google Maps Automotive API 提供了 PlacesClient 来做周边搜索。我建议你们用 findCurrentPlace() 获取当前位置附近的 POI,然后用 Autocomplete 做搜索建议。
// 创建 PlacesClient
val placesClient = Places.createClient(context)
// 构建搜索请求
val request = FindCurrentPlaceRequest.builder(
listOf(Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.TYPES)
).build()
// 执行搜索
placesClient.findCurrentPlace(request).addOnSuccessListener { response ->
for (placeLikelihood in response.placeLikelihoods) {
val place = placeLikelihood.place
val likelihood = placeLikelihood.likelihood
// 在地图上添加标记
googleMap.addMarker(
MarkerOptions()
.position(place.latLng!!)
.title(place.name)
)
}
}
语音导航这块,Android Automotive 集成了 Google Assistant 的 Driving Mode。你可以通过 VoiceInteractionService 来接收语音指令,比如“导航到最近的加油站”。
避坑指南: 我曾经在 POI 搜索时没做去重处理,结果同一个加油站在地图上显示了三个标记。原因是 Places API 返回了不同数据源(Google、四维图新、高德)的同一地点。解决方案是用 placeId 做唯一性校验,只保留置信度最高的那个。
语音导航的 TTS(文字转语音)播报,我建议用 TextToSpeech 引擎,但要注意两点:一是播报时机要提前,比如“前方 300 米右转”要在距离 350 米时就开始播报,因为语音有延迟;二是播报过程中要降低媒体音量,用 AudioManager 的 DUCK 模式做闪避。
| 功能模块 | 关键 API / 类 | 注意事项 |
|---|---|---|
| 地图显示 | SupportMapFragment, GoogleMap | 硬件加速必须开启 |
| 路线规划 | Directions API, PolylineOptions | 注意配额限制,使用 traffic_model |
| 实时交通 | TrafficLayer | 更新频率 30s 为宜 |
| HUD 投射 | CarNavigationStatusManager | 坐标系转换需联调 |
| POI 搜索 | PlacesClient, findCurrentPlace | 做 placeId 去重 |
| 语音导航 | TextToSpeech, VoiceInteractionService | 提前播报,音量闪避 |
最后说一句,车载导航开发最考验的不是技术本身,而是对驾驶场景的理解。你写的每一行代码,最终都会影响驾驶员的安全。所以,多想想“如果我是驾驶员,这个功能会不会让我分心?”——这个问题,比任何技术难题都重要。