You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

左右鞋垫传感器数据Firebase同步异常问题求助

智能鞋垫Firebase数据同步异常解决方案

问题背景

我有左右两只智能鞋垫,需将两者生成的数据每1秒发送至Firebase平台。目前使用left_timer.scheduleAtFixedRate和right_timer.scheduleAtFixedRate两个定时器实现每秒发送逻辑,但数据存在同步异常:实际行走时仅单脚接触鞋垫,数据却显示双脚同时着地,怀疑该问题由数据发送流程延迟导致,求可行解决方案。

问题根源分析

  • 两个独立定时器无法保证严格同步,左右数据的发送时间点存在偏差,导致不同时刻的数据被标记为同一秒
  • 数据缓存字典leftDataDict/rightDataDict持续累积数据,直到发送时才清空,可能包含多帧不同时刻的传感器数据
  • Firebase异步发送的回调中才清空数据,若发送延迟,后续接收的新数据会覆盖旧数据,导致数据混乱
  • 蓝牙接收数据时,每条消息都生成独立时间戳,同一秒内的多条数据时间标记不一致,无法准确对应同一时刻的状态

解决方案

1. 统一定时器,同步发送左右数据

放弃两个独立定时器,改用单个全局定时器同时处理左右鞋垫的数据发送,确保同一时间点提交数据到Firebase。

// 全局变量
private Timer syncTimer;
private boolean isLeftRunning = false;
private boolean isRightRunning = false;
private SimpleDateFormat secondFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());

// 修改startLeftBtn点击事件
startLeftBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (is_L_insole_connected) {
            if (is_L_insole_started) {
                left_insole_device_interface.stopInsole();
                is_L_insole_started = false;
                startLeftBtn.setText("Start Left");
                isLeftRunning = false;
                checkAndStopSyncTimer();
            } else {
                left_insole_device_interface.startInsole();
                is_L_insole_started = true;
                startLeftBtn.setText("Stop Left");
                isLeftRunning = true;
                checkAndStartSyncTimer();
            }
        } else {
            Toast.makeText(Visualization.this, "Left Insole Not Connected!", Toast.LENGTH_SHORT).show();
        }
    }
});

// 修改startRightBtn点击事件
startRightBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (is_R_insole_connected) {
            if (is_R_insole_started) {
                right_insole_device_interface.stopInsole();
                is_R_insole_started = false;
                startRightBtn.setText("Start Right");
                isRightRunning = false;
                checkAndStopSyncTimer();
            } else {
                right_insole_device_interface.startInsole();
                is_R_insole_started = true;
                startRightBtn.setText("Stop Right");
                isRightRunning = true;
                checkAndStartSyncTimer();
            }
        } else {
            Toast.makeText(Visualization.this, "Right Insole Not Connected!", Toast.LENGTH_SHORT).show();
        }
    }
});

// 定时器控制方法
private void checkAndStartSyncTimer() {
    if (syncTimer == null && (isLeftRunning || isRightRunning)) {
        syncTimer = new Timer();
        syncTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(() -> {
                    if (isLeftRunning) {
                        sendCurrentInsoleData(leftDataDict, "Left_Insole");
                    }
                    if (isRightRunning) {
                        sendCurrentInsoleData(rightDataDict, "Right_Insole");
                    }
                });
            }
        }, 1000, 1000);
    }
}

private void checkAndStopSyncTimer() {
    if (syncTimer != null && !isLeftRunning && !isRightRunning) {
        syncTimer.cancel();
        syncTimer = null;
    }
}

2. 按整秒窗口缓存数据,避免累积

修改蓝牙接收逻辑,用整秒时间戳作为键,每个时间窗口只存储当前秒的最新数据,而非持续添加:

// 左鞋垫onMessageReceived中替换时间戳逻辑
if(is_L_insole_started){
    // ... 原有数据解析逻辑 ...
    
    String currentSecond = secondFormatter.format(new Date());
    // 直接覆盖当前秒的数据,确保每秒仅保留最新状态
    leftDataDict.put(currentSecond, Arrays.toString(l_data_double_arr));
    
    // ... 原有数据重置逻辑 ...
}

// 右鞋垫onMessageReceived同理修改
if(is_R_insole_started){
    // ... 原有数据解析逻辑 ...
    
    String currentSecond = secondFormatter.format(new Date());
    rightDataDict.put(currentSecond, Arrays.toString(r_data_double_arr));
    
    // ... 原有数据重置逻辑 ...
}

3. 优化Firebase发送逻辑,避免数据冲突

修改发送方法,先复制当前数据再清空缓存,避免异步发送时数据被覆盖:

public void sendCurrentInsoleData(Map<String, Object> data, String of_insole) {
    if (data.isEmpty()) return;
    
    // 复制当前数据,避免清空操作影响正在发送的内容
    Map<String, Object> dataCopy = new HashMap<>(data);
    data.clear(); // 立即清空缓存,准备接收下一秒数据
    
    db.collection("Experiments").document(PATIENT_NAME).set(patientRecord);
    db.collection("Experiments")
            .document(PATIENT_NAME)
            .collection(of_insole)
            .document(secondFormatter.format(new Date()))
            .set(dataCopy)
            .addOnSuccessListener(unused -> {
                Toast.makeText(Visualization.this, of_insole + "数据已保存", Toast.LENGTH_SHORT).show();
            })
            .addOnFailureListener(e -> {
                Toast.makeText(Visualization.this, of_insole + "数据保存失败", Toast.LENGTH_SHORT).show();
                // 保存失败时将数据放回缓存,避免丢失
                data.putAll(dataCopy);
            });
}

4. 线程安全处理数据缓存

蓝牙接收在后台线程,定时器发送在UI线程,改用ConcurrentHashMap保证线程安全:

// 替换原有HashMap定义
private Map<String, Object> leftDataDict = new ConcurrentHashMap<>();
private Map<String, Object> rightDataDict = new ConcurrentHashMap<>();

内容的提问来源于stack exchange,提问作者lead step

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:01:21