11、车载传感器与数据:GPS与位置服务、车速传感器、油耗与里程数据、传感器权限管理
各位同学,今天我们来聊聊车载传感器和数据。说实话,这部分内容在 Android Automotive OS 里属于「地基」级别的存在。你想想看,没有准确的位置、没有真实的车速、没有油耗数据,那些导航、驾驶分析、远程监控的功能全都会变成空中楼阁。
我个人习惯把车载传感器分成三类:定位类(GPS/GNSS)、车身动态类(车速、轮速)、能源类(油耗、里程)。每一类都有它的脾气,咱们一个一个来拆解。
11.1 GPS与位置服务:不只是经纬度
GPS 在车机里可不是手机上的那个简单定位。车载 GPS 需要同时支持 GPS + GLONASS + Galileo + 北斗,也就是多星座融合。为什么?因为城市峡谷、隧道、高架桥下,单星座很容易丢星。
我在项目中遇到过一个问题:某款车型在穿过高架桥时,定位直接跳到了对面车道,导航疯狂重新规划路线。后来发现是 GPS 芯片的 AGPS 辅助数据没有及时更新,导致冷启动定位精度差。
FusedLocationProviderClient,不要直接读原始 NMEA 数据。融合了 GPS、Wi-Fi、基站、惯性导航(DR)的结果才稳定。
代码示例(获取位置更新):
// 在 Android Automotive OS 中,建议使用高精度模式
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, // 高精度
1000L // 1秒更新一次
).apply {
setMinUpdateDistanceMeters(5f) // 移动5米才触发更新
}.build()
val client = LocationServices.getFusedLocationProviderClient(context)
client.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
setMinUpdateDistanceMeters 一定要设置。否则车辆静止等红灯时,GPS 噪声会导致位置漂移,地图上的车辆图标会「抖动」,用户体验极差。
11.2 车速传感器:真实车速 vs 显示车速
车速传感器在 Android Automotive OS 里是通过 Vehicle HAL 提供的。具体来说,是 VehicleProperty.SPEED 这个属性。但这里有个坑:车速传感器返回的是轮速换算值,不是 GPS 速度。
你想想看,GPS 速度在隧道里会丢失,而轮速传感器不会。但轮速传感器也有问题——轮胎磨损、胎压变化都会导致误差。我见过一个案例:某台车换了非标轮胎后,仪表盘显示 120km/h,实际只有 110km/h。
所以 Android Automotive OS 的做法是:融合。用 GPS 速度校准轮速,用轮速填补 GPS 盲区。
| 数据源 | 优点 | 缺点 |
|---|---|---|
| GPS 速度 | 绝对准确,无累计误差 | 隧道/高楼丢失,更新频率低(1Hz) |
| 轮速传感器 | 实时性高(100Hz),不丢数据 | 有累计误差,受轮胎影响 |
| 融合结果 | 稳定、准确、实时 | 算法复杂,需要校准 |
VehicleProperty.SPEED 的值显示给用户。这个值是原始传感器值,单位是 m/s,而且可能包含负值(倒车时)。你需要做滤波、单位转换、符号处理。
我曾经踩过一个坑:倒车时车速传感器返回 -3.2 m/s,我直接取绝对值显示,结果用户反馈「倒车时仪表显示 11km/h,但感觉没这么快」。后来才意识到,倒车速度应该单独处理,或者直接显示 0。
11.3 油耗与里程数据:OBD 与 CAN 的博弈
油耗和里程数据,在 Android Automotive OS 里属于 VehicleProperty 中的 FUEL_CONSUMPTION 和 ODOMETER。但这里有个现实问题:很多车型的 CAN 总线并不直接提供油耗数据,而是提供喷油脉宽、进气量等原始参数。
说白了,你得自己算。我参与过一个项目,油耗数据偏差很大,后来发现是 喷油嘴老化 导致实际喷油量与 ECU 计算值不一致。嗯,硬件问题软件背锅,这种事在车载开发里太常见了。
里程数据相对简单,直接从 CAN 读取轮速脉冲累计即可。但要注意:里程数据不能随意清零,这是法规要求。Android Automotive OS 的 ODOMETER 属性是只读的,而且需要系统级权限才能访问。
代码示例(读取里程数据):
// 需要系统权限:android.car.permission.CAR_ENERGY
val car = Car.createCar(context)
val propertyManager = car.getCarPropertyManager()
// 注册里程变化监听
propertyManager.registerCallback(
object : CarPropertyEventCallback {
override fun onChangeEvent(event: CarPropertyEvent) {
if (event.propertyId == VehicleProperty.ODOMETER) {
val odometer = event.value as? Float
// 单位:公里
updateUI(odometer)
}
}
override fun onErrorEvent(propertyId: Int, zone: Int) {
Log.e("Sensor", "里程读取失败")
}
},
VehicleProperty.ODOMETER,
CarPropertyManager.SENSOR_RATE_ONCHANGE
)
11.4 传感器权限管理:谁可以碰这些数据?
车载传感器数据非常敏感。你想想看,如果某个 App 能随意读取车速和 GPS,那它完全可以知道你每天几点出门、开多快、去了哪里。所以 Android Automotive OS 有一套严格的权限体系。
核心权限有三个:
- CAR_LOCATION:访问 GPS 位置,需要用户授权,且是运行时权限。
- CAR_SPEED:读取车速,系统级权限,普通 App 拿不到。
- CAR_ENERGY:读取油耗和里程,同样是系统级权限。
CAR_SPEED 权限根本申请不到,因为它是 signature|privileged 级别的。最后只能退而求其次,用 GPS 速度估算。
权限申请示例(仅限系统应用):
<uses-permission android:name="android.car.permission.CAR_SPEED"
android:protectionLevel="signature|privileged" />
<!-- 在 AndroidManifest.xml 中声明 -->
<uses-permission android:name="android.car.permission.CAR_ENERGY" />
对于普通应用,只能通过 CarProjection 或 CarMediaService 间接获取部分数据,而且必须经过用户明确同意。说白了,Google 在设计时就把安全放在了第一位。
11.5 知识体系总览
下面这张图是我自己整理的传感器数据流,你可以看到从硬件到应用层的完整链路:
从这张图你可以看到,所有传感器数据最终都会经过 Vehicle HAL 统一上报,然后由 CarService 进行权限校验和分发。应用层只能通过 Car API 获取数据,无法直接访问硬件。
CarPropertyManager 的 getProperty 方法获取单次值,而不是持续监听。持续监听会唤醒 CPU,增加功耗。对于仪表盘这类需要实时更新的场景,再考虑注册回调。
好了,关于车载传感器与数据的内容就讲到这里。记住一句话:传感器数据是车机的血液,权限管理是免疫系统。两者缺一不可。