20、传感器测试与调试:传感器模拟器使用、ADB传感器命令、数据可视化工具、性能分析
做传感器开发,最头疼的是什么?
不是写代码,是调试。真机传感器数据飘忽不定,有时候你根本分不清是代码写错了,还是硬件本身就有问题。我早期做计步器算法时,就吃过这个亏——在办公室跑得好好的,一到户外就乱跳,折腾了两天才发现是地磁干扰。
所以这一章,咱们专门聊聊怎么把传感器调试这件事,做得更稳、更快、更准。
20.1 传感器模拟器:没有真机也能跑
你可能会问:没有真机,怎么测传感器?
嗯,Android 模拟器其实自带传感器模拟功能。我个人的习惯是,在写算法初期,先用模拟器把逻辑跑通,再上真机验证。这样能省下不少来回编译的时间。
20.1.1 启用模拟器传感器面板
打开 AVD Manager,启动你的模拟器。然后在模拟器右侧工具栏里,找到三个小点(More Options),点进去选择 Virtual Sensors。
你会看到这样一个界面:
┌─────────────────────────────────────┐
│ Virtual Sensors (模拟器传感器面板) │
├─────────────────────────────────────┤
│ Accelerometer: x: 0.0 y: 9.8 z: 0.0 │
│ Gyroscope: x: 0.0 y: 0.0 z: 0.0 │
│ Magnetometer: x: 0.0 y: 0.0 z: 0.0 │
│ ... │
│ [Presets: 走路 | 跑步 | 旋转 | 自定义] │
└─────────────────────────────────────┘
你可以手动拖动滑块,模拟设备倾斜、旋转。也可以选择预设的「走路」或「跑步」模式,模拟器会自动生成一组连续的传感器数据。
20.1.2 自定义传感器数据文件
预设模式不够用?你可以自己写数据文件。
模拟器支持加载 CSV 格式的传感器数据。格式很简单:
# 时间戳(ms), 加速度X, 加速度Y, 加速度Z
0, 0.0, 9.8, 0.0
16, 0.1, 9.7, 0.2
32, -0.1, 9.9, -0.1
...
在 Virtual Sensors 面板里,点击 Load Data,选择你的 CSV 文件,模拟器就会按时间戳回放数据。我在做跌倒检测算法时,就是用这种方式,把真实采集的跌倒数据喂给模拟器,反复调试阈值参数。
20.2 ADB 传感器命令:命令行里的瑞士军刀
ADB 不只是用来装 App 的。它有一组命令,能直接操作传感器。说白了,你可以用命令行控制传感器开关、查看数据、甚至注入假数据。
20.2.1 查看传感器列表
想知道设备上有哪些传感器?一条命令搞定:
adb shell dumpsys sensorservice
输出会列出所有注册的传感器,包括类型、厂商、分辨率、最大范围等。我曾经用这个命令排查过一个问题:某款平板明明有陀螺仪,但 App 就是检测不到。一查才发现,系统把陀螺仪禁用了。
20.2.2 实时监听传感器数据
想看看传感器当前在输出什么?用这个:
adb shell dumpsys sensorservice --sensor 1
其中 1 是传感器 ID(加速度计通常是 1,陀螺仪是 4)。这条命令会持续输出数据流,直到你按 Ctrl+C。
输出示例:
Sensor: Accelerometer, time=1234567890, x=0.12, y=9.81, z=0.34
Sensor: Accelerometer, time=1234567906, x=0.11, y=9.80, z=0.35
...
20.2.3 注入传感器数据(真机调试利器)
这个功能知道的人不多,但非常实用。你可以用 ADB 向设备注入假数据:
adb shell sensorstest --sensor 1 --value 0.0,9.8,0.0
这条命令会向加速度计注入一组固定值。所有监听该传感器的 App,都会收到这组数据。
有什么用?举个例子:你写了一个计步器,想测试静止状态下会不会误计步。直接注入 0.0, 9.8, 0.0(静止状态),然后看 App 的反应。不用拿着手机傻站着等。
20.3 数据可视化工具:让数据「说话」
看 Logcat 里的数字,眼睛会瞎的。我个人的习惯是,把传感器数据实时画成曲线图。一眼就能看出异常。
20.3.1 使用 Android Studio 的 Profiler
Android Studio 自带的 Profiler 其实可以看传感器数据。不过它主要面向 CPU、内存,传感器数据需要额外配置。
你可以在 Profiler 里添加自定义数据点:
// 在代码中插入
Trace.beginSection("sensor_x");
Trace.setCounter("sensor_x", (long)(event.values[0] * 1000));
Trace.endSection();
然后 Profiler 的 Timeline 上就会显示一条实时曲线。虽然精度一般,但胜在方便,不用装额外工具。
20.3.2 第三方工具:Sensor Kinetics
如果你想要更专业的可视化,我推荐 Sensor Kinetics 这个 App。它能把加速度、陀螺仪、磁力计的数据画成三条实时曲线,还支持记录和回放。
界面大概长这样:
┌─────────────────────────────────────┐
│ Sensor Kinetics │
├─────────────────────────────────────┤
│ ┌─────────────────────────────────┐ │
│ │ 加速度 X: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁ │ │
│ │ 加速度 Y: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁ │ │
│ │ 加速度 Z: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁ │ │
│ └─────────────────────────────────┘ │
│ [记录] [回放] [导出CSV] │
└─────────────────────────────────────┘
我在做姿态解算时,经常用这个 App 记录一段真实运动数据,然后导出 CSV,再用 Python 脚本分析。比在手机上调试舒服多了。
20.3.3 自己写一个简单的可视化界面
如果你喜欢折腾,可以自己写一个简单的实时曲线图。用 Canvas 画就行,代码量不大:
public class SensorGraphView extends View {
private Paint linePaint;
private Path path;
private float[] dataBuffer = new float[200];
@Override
protected void onDraw(Canvas canvas) {
// 清空画布
canvas.drawColor(Color.WHITE);
// 绘制数据曲线
path.reset();
for (int i = 0; i < dataBuffer.length; i++) {
float x = i * getWidth() / dataBuffer.length;
float y = getHeight() / 2 - dataBuffer[i] * 10;
if (i == 0) path.moveTo(x, y);
else path.lineTo(x, y);
}
canvas.drawPath(path, linePaint);
}
public void addDataPoint(float value) {
// 滑动窗口,新数据覆盖旧数据
System.arraycopy(dataBuffer, 1, dataBuffer, 0, dataBuffer.length - 1);
dataBuffer[dataBuffer.length - 1] = value;
postInvalidate();
}
}
这样,你的 App 里就有一个实时传感器波形图了。调试时看着曲线,比看数字直观得多。
20.4 性能分析:传感器不是免费的
传感器一直在工作,意味着 CPU 一直在处理中断、数据拷贝、回调。如果不注意性能,你的 App 会变成电老虎。
20.4.1 采样率与功耗的平衡
Android 传感器支持不同的采样率:
| 采样率 | 延迟(微秒) | 典型场景 | 功耗 |
|---|---|---|---|
| SENSOR_DELAY_FASTEST | 0 | 游戏、VR | 高 |
| SENSOR_DELAY_GAME | 20,000 | 手势识别 | 中 |
| SENSOR_DELAY_UI | 60,000 | 屏幕旋转 | 低 |
| SENSOR_DELAY_NORMAL | 200,000 | 计步器 | 极低 |
我见过不少开发者,做计步器也用 FASTEST 模式。结果手机半天就没电了。其实计步器只需要 10-20Hz 的采样率,用 NORMAL 完全够用。
20.4.2 使用 Battery Historian 分析功耗
Google 提供了一个叫 Battery Historian 的工具,可以分析 App 的耗电情况。使用方法:
# 1. 重置电池统计信息
adb shell dumpsys batterystats --reset
# 2. 运行你的 App,做各种操作
# 3. 导出电池数据
adb bugreport bugreport.zip
# 4. 上传到 Battery Historian 网页工具
在生成的报告中,你可以看到传感器唤醒 CPU 的频率、每次唤醒的持续时间。如果发现传感器频繁唤醒 CPU,说明你的采样率设置过高,或者没有在不需要时注销监听器。
20.4.3 避免「传感器泄漏」
这是一个很常见的坑。我在项目中遇到过好几次:Activity 销毁了,但传感器监听器没有注销。结果传感器一直在后台工作,手机发烫。
正确的做法:
@Override
protected void onResume() {
super.onResume();
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this); // 一定要注销!
}
嗯,这里要注意:onPause 里注销,而不是 onDestroy。因为 onPause 一定会被调用,而 onDestroy 不一定。
20.5 本章知识体系
下面这张图,帮你理清传感器测试与调试的完整流程:
这张图把四个核心模块串起来了。你从模拟器开始,用 ADB 命令做精细控制,用可视化工具观察数据,最后用性能分析工具确保不耗电。每一步都有对应的工具和方法。