27、传感器数据存储与分析:本地数据库存储、CSV导出、数据可视化图表、机器学习预处理

传感器数据采集回来,只是第一步。真正头疼的,是怎么把这些原始数据变成有价值的东西。

我早期做的一个计步器项目,数据全存在内存里,用户一退出应用就丢了。被用户骂惨了。后来我学乖了——数据必须落地,而且要方便分析。

这一章,我们就来聊聊传感器数据的存储与分析。说白了,就是四个环节:存起来、导出去、画成图、喂给AI。

27.1 本地数据库存储:Room 实战

Android 官方推荐的本地数据库方案,就是 Room。它是对 SQLite 的一层封装,用起来比原生 API 舒服太多。

我的习惯:只要涉及结构化传感器数据,我首选 Room。性能够用,代码也干净。

先定义一个传感器数据实体:

@Entity(tableName = "sensor_data")
public class SensorRecord {
    @PrimaryKey(autoGenerate = true)
    public long id;

    public int sensorType;      // 传感器类型,如 ACCELEROMETER
    public float x, y, z;       // 三轴数据
    public long timestamp;      // 采集时间戳(纳秒)
    public long sessionId;      // 会话ID,用于区分不同采集批次
}

然后定义 DAO 接口:

@Dao
public interface SensorDao {
    @Insert
    void insert(SensorRecord record);

    @Query("SELECT * FROM sensor_data WHERE sessionId = :sessionId ORDER BY timestamp ASC")
    List<SensorRecord> getRecordsBySession(long sessionId);

    @Query("DELETE FROM sensor_data WHERE timestamp < :cutoff")
    void deleteOldData(long cutoff);
}

最后是 Database 类:

@Database(entities = {SensorRecord.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract SensorDao sensorDao();
}

初始化时,我建议用单例模式:

AppDatabase db = Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase.class,
    "sensor_db"
).build();
注意:Room 操作不能在主线程执行。记得用协程或 AsyncTask。我曾经因为忘了这茬,直接在主线程插入大量数据,导致 ANR,被测试同事追着骂了一下午。

27.2 CSV 导出:让数据走出手机

数据库里的数据,怎么给数据分析师看?CSV 是最通用的格式。Excel、Python、R 都能直接打开。

导出逻辑其实不复杂:

public File exportToCsv(List<SensorRecord> records, File file) throws IOException {
    FileWriter writer = new FileWriter(file);
    writer.write("timestamp,x,y,z\n");
    for (SensorRecord r : records) {
        writer.write(r.timestamp + "," + r.x + "," + r.y + "," + r.z + "\n");
    }
    writer.close();
    return file;
}

导出后,我一般用 MediaStore 把文件存到 Downloads 目录,方便用户通过文件管理器找到。

小技巧:如果数据量很大(比如超过 10 万行),建议分批写入,或者用 StringBuilder 缓存一下再 flush。否则频繁 IO 会让手机卡顿。

27.3 数据可视化图表:用 MPAndroidChart 画出来

光看数字,很难发现规律。画成折线图,一眼就能看出趋势。

我常用的是 MPAndroidChart 库。画一个加速度计的三轴折线图,大概这样:

LineChart chart = findViewById(R.id.chart);
List<Entry> entriesX = new ArrayList<>();
List<Entry> entriesY = new ArrayList<>();
List<Entry> entriesZ = new ArrayList<>();

for (int i = 0; i < records.size(); i++) {
    SensorRecord r = records.get(i);
    entriesX.add(new Entry(i, r.x));
    entriesY.add(new Entry(i, r.y));
    entriesZ.add(new Entry(i, r.z));
}

LineDataSet setX = new LineDataSet(entriesX, "X轴");
setX.setColor(Color.RED);
LineDataSet setY = new LineDataSet(entriesY, "Y轴");
setY.setColor(Color.GREEN);
LineDataSet setZ = new LineDataSet(entriesZ, "Z轴");
setZ.setColor(Color.BLUE);

LineData lineData = new LineData(setX, setY, setZ);
chart.setData(lineData);
chart.invalidate();

嗯,这里要注意:如果数据点太多(比如超过 1000 个),图表会变得密密麻麻,根本看不清。我一般会做降采样——每 10 个点取一个平均值,或者只显示最近 200 个点。

27.4 机器学习预处理:把传感器数据喂给模型

传感器数据要喂给机器学习模型,不能直接扔进去。得先做预处理。

常见的预处理步骤包括:

  • 去噪:用低通滤波器去掉高频噪声。我常用的是滑动平均滤波。
  • 归一化:把数据缩放到 [0, 1] 或 [-1, 1] 区间。避免不同量纲影响模型。
  • 滑动窗口:把连续数据切成固定长度的窗口。比如每 50 个采样点作为一个样本。
  • 特征提取:从窗口里提取均值、方差、峰值、过零率等特征。

举个例子,滑动窗口切分:

public List<float[]> createWindows(float[] rawData, int windowSize, int step) {
    List<float[]> windows = new ArrayList<>();
    for (int i = 0; i + windowSize <= rawData.length; i += step) {
        float[] window = Arrays.copyOfRange(rawData, i, i + windowSize);
        windows.add(window);
    }
    return windows;
}

归一化我习惯用 Z-score 标准化:

public float[] normalize(float[] data) {
    float mean = 0, std = 0;
    for (float v : data) mean += v;
    mean /= data.length;
    for (float v : data) std += (v - mean) * (v - mean);
    std = (float) Math.sqrt(std / data.length);
    for (int i = 0; i < data.length; i++) {
        data[i] = (data[i] - mean) / std;
    }
    return data;
}
核心思路:预处理做得好,模型效果差不了。我见过太多人把原始数据直接扔给模型,结果准确率惨不忍睹。预处理不是可选项,是必选项。

27.5 知识体系总览

下面这张图,把传感器数据从采集到分析的完整链路画出来了。你可以对照着看,心里有个全局观。

传感器数据存储与分析流程 传感器数据采集 本地数据库 (Room) CSV 导出 数据可视化 (图表) 机器学习预处理 Excel / Python 分析 趋势观察 / 调试 模型训练 / 推理

你看,整个链路其实很清晰。从采集到存储,再到导出、可视化、预处理,每一步都有它的价值。我个人建议,在项目初期就把这套流程搭好,后面迭代会轻松很多。

避坑指南:我曾经在一个项目中,把预处理逻辑写死在 Activity 里。后来要换模型,改得我头皮发麻。现在我都把预处理封装成独立的工具类,数据管道也做成可配置的。这样换模型、换算法,改几行配置就行。

好了,这一章的内容就到这里。传感器数据的存储与分析,说白了就是让数据流动起来,从手机里走到分析工具里,最后变成模型能理解的形式。每一步都不难,但每一步都值得认真对待。


公众号:蓝海资料掘金营,微信deep3321