21、传感器数据导出:将采集的数据导出为CSV文件,方便在PC上做进一步分析

好,咱们终于到了数据导出这一步。说实话,前面折腾了那么多传感器采集、滤波、校准,最终目的不就是为了拿到干净的数据,然后做分析吗?我个人习惯是把数据导出成CSV格式,因为Excel、Python、MATLAB都能直接打开,省去一堆格式转换的麻烦。

为什么非得是CSV?

你想想看,我们在手机上采集的光线数据,本质就是一串带时间戳的数字。CSV(逗号分隔值)这种格式,说白了就是纯文本表格。它有几个好处:

  • 通用性极强:任何系统、任何软件都能读
  • 体积小:比Excel文件小得多,适合大量传感器数据
  • 可读性好:用记事本打开也能看,调试方便
  • 无版权问题:不像某些专有格式需要特定软件

核心思路:把SensorEvent里的时间戳和光照值,一行一行写到文件里,每行用逗号隔开。就这么简单。

数据导出的标准格式

我在项目中遇到过不少坑,比如时间戳格式不统一、数值精度丢失、中文乱码等等。后来我总结了一套比较稳的格式:

列名 说明 示例
timestamp Unix时间戳(毫秒) 1712345678000
timestamp_human 可读时间(ISO 8601) 2024-04-06 10:30:00.123
lux 光照强度(勒克斯) 320.5
accuracy 传感器精度等级 3

我的小技巧:同时保留机器时间戳和人类可读时间。机器时间戳方便排序和计算差值,人类可读时间方便肉眼检查。两列都留着,不冲突。

代码实现:从采集到导出

嗯,这里要注意一个关键点:文件写入操作不能放在UI线程里做。我曾经犯过这个错,结果界面卡得跟幻灯片似的。正确的做法是用后台线程或者协程来处理。

下面是我常用的一个导出工具类,你直接拿去用就行:

public class CsvExporter {

    private static final String CSV_HEADER = "timestamp,timestamp_human,lux,accuracy\n";

    public static File exportToCsv(Context context, List<SensorData> dataList, String fileName) {
        File file = new File(context.getExternalFilesDir(null), fileName);
        
        try (FileWriter fw = new FileWriter(file);
             BufferedWriter bw = new BufferedWriter(fw)) {
            
            // 写入表头
            bw.write(CSV_HEADER);
            
            // 逐行写入数据
            for (SensorData data : dataList) {
                StringBuilder line = new StringBuilder();
                line.append(data.timestamp).append(",");
                line.append(formatTimestamp(data.timestamp)).append(",");
                line.append(data.lux).append(",");
                line.append(data.accuracy).append("\n");
                bw.write(line.toString());
            }
            
            bw.flush();
            return file;
            
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String formatTimestamp(long millis) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault());
        return sdf.format(new Date(millis));
    }
}

避坑指南:我曾经在导出大量数据时忘了加flush(),结果文件里少了一半数据。记得在写入完成后调用flush(),或者用try-with-resources自动关闭流。

数据采集与导出的完整流程

为了让整个流程更清晰,我画了一张图。你看一眼就明白数据是怎么从传感器一路走到CSV文件的:

光线传感器数据导出流程 光线传感器 每秒采集50-100次 数据缓存队列 ArrayList<SensorData> 数据格式化 拼接CSV行 CSV ⚠ 关键注意事项 1. 文件写入必须在后台线程执行,避免ANR 2. 建议每采集100条数据就写入一次,防止内存溢出 3. 文件名加上时间戳,避免覆盖:light_20240406_1030.csv 4. 导出完成后用MediaScannerConnection通知系统 5. 如果数据量超过10万行,考虑分文件导出 导出完成后,用USB复制到电脑即可分析

导出后的数据长什么样?

导出完成后,你用Excel打开CSV文件,应该能看到类似这样的数据:

timestamp,timestamp_human,lux,accuracy
1712345678000,2024-04-06 10:30:00.000,320.5,3
1712345678010,2024-04-06 10:30:00.010,321.2,3
1712345678020,2024-04-06 10:30:00.020,319.8,3
1712345678030,2024-04-06 10:30:00.030,322.1,3

你看,每一行就是一次传感器采样。时间戳精确到毫秒,光照值保留一位小数。这样的数据拿到PC上,用Python的pandas库一读,画个折线图,光照变化趋势一目了然。

我的经验:如果你打算用Python做后续分析,建议在CSV里多加一列「event_delta」,即相邻两次采样的事件间隔。这样你可以检查传感器是否出现了丢帧或者采样不均匀的情况。

导出文件的管理

数据导出了,文件放哪儿也是个问题。我个人习惯把文件放在应用的专属外部存储目录里:

// 获取应用专属的外部存储目录
File exportDir = context.getExternalFilesDir("sensor_data");
// 实际路径类似:/storage/emulated/0/Android/data/你的包名/files/sensor_data/

这样做的好处是:

  • 不需要申请存储权限(Android 10以上)
  • 卸载应用时文件自动删除,不污染用户存储
  • 通过USB连接电脑后,这个目录可见,可以直接复制

曾经踩过的坑:我一开始把文件存在了公共的Downloads目录,结果用户卸载应用后留下一堆垃圾文件。后来改成getExternalFilesDir,清爽多了。而且Android 11开始对公共目录的访问限制更严,用专属目录反而省事。

好了,数据导出这块就这些。你只要按照上面的流程走,从传感器采集到CSV文件落地,基本不会出大问题。拿到数据后,你就可以在PC上尽情分析了——画曲线、算平均值、做FFT,都随你。

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