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

求助:加速度计数组双峰值检测算法优化(规避小峰干扰)

Solution for Accelerometer Peak-Valley Detection with Position Constraints

Hey there, let's fix your peak detection logic to avoid small peak interference and meet your requirements: find the second peak, trace back to a valley below 5, then locate the first peak near position 40. Here's how to refactor your code step by step:

Key Issues in Original Code

Your current logic just tracks the two largest values in the array, which doesn't account for the required sequence (peak1 → valley <5 → peak2) or filter out small, noisy peaks. We need to implement local peak/valley detection with explicit constraints to get reliable results.

Modified Code with Detailed Explanations

First, we'll add helper methods to detect valid peaks/valleys (using a sliding window to filter noise), then rework the detection flow to follow your desired logic:

public class Accelerometer extends AppCompatActivity implements SensorEventListener {
    private TextView xText, yText, zText, resultText, resultText2;
    private Sensor mySensor;
    private SensorManager SM;
    int pos = 0;
    double array[] = new double[10000];
    // Adjust this window size based on your sampling rate (filters small peaks)
    private static final int PEAK_WINDOW_SIZE = 3;
    // Range around position 40 to search for the first peak
    private static final int PEAK1_TARGET_RANGE = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_accelerometer);

        // Initialize sensor manager and accelerometer
        SM = (SensorManager) getSystemService(SENSOR_SERVICE);
        mySensor = SM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        SM.registerListener(this, mySensor, SensorManager.SENSOR_DELAY_NORMAL);

        // Setup stop button
        Button buttonStop = findViewById(R.id.buttonStop);
        buttonStop.setOnClickListener(v -> stopSensor());

        // Assign text views
        xText = findViewById(R.id.xText);
        yText = findViewById(R.id.yText);
        zText = findViewById(R.id.zText);
        resultText = findViewById(R.id.resultText);
        resultText2 = findViewById(R.id.resultText2);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Not used
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];

        // Update UI with raw accelerometer values
        xText.setText("X: " + String.format("%.2f", x));
        yText.setText("Y: " + String.format("%.2f", y));
        zText.setText("Z: " + String.format("%.2f", z));

        // Calculate magnitude and store in array
        double mag = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
        if (pos < array.length) {
            array[pos] = mag;
            pos++;
        }
    }

    // Helper: Check if a position is a local peak (higher than all neighbors in the window)
    private boolean isLocalPeak(double[] data, int index) {
        // Skip edge positions that can't have a full window
        if (index < PEAK_WINDOW_SIZE || index >= pos - PEAK_WINDOW_SIZE) {
            return false;
        }
        double current = data[index];
        // Verify all surrounding points are smaller than current
        for (int i = index - PEAK_WINDOW_SIZE; i <= index + PEAK_WINDOW_SIZE; i++) {
            if (i != index && data[i] >= current) {
                return false;
            }
        }
        return true;
    }

    // Helper: Check if a position is a valid valley (lower than neighbors, and <5)
    private boolean isLocalValley(double[] data, int index) {
        if (index < PEAK_WINDOW_SIZE || index >= pos - PEAK_WINDOW_SIZE) {
            return false;
        }
        double current = data[index];
        if (current >= 5) return false; // Valley must be below 5
        // Verify all surrounding points are larger than current
        for (int i = index - PEAK_WINDOW_SIZE; i <= index + PEAK_WINDOW_SIZE; i++) {
            if (i != index && data[i] <= current) {
                return false;
            }
        }
        return true;
    }

    private void stopSensor() {
        SM.unregisterListener(this);
        // Save collected data to file (fixed newline character from /n to \n)
        File path = getApplicationContext().getExternalFilesDir(null);
        File file = new File(path, "my_file-name.txt");
        try (FileOutputStream outputStream = new FileOutputStream(file)) {
            for (int i = 0; i < pos; i++) {
                String s = Double.toString(array[i]) + ",";
                outputStream.write(s.getBytes());
            }
            outputStream.write("\n".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Start peak-valley detection logic
        try {
            if (pos < PEAK_WINDOW_SIZE * 3) {
                resultText.setText("Not enough data");
                resultText2.setText("Not enough data");
                return;
            }

            // Step 1: Find the second valid local peak
            int peak2Index = -1;
            int peakCount = 0;
            for (int i = PEAK_WINDOW_SIZE; i < pos - PEAK_WINDOW_SIZE; i++) {
                if (isLocalPeak(array, i)) {
                    peakCount++;
                    if (peakCount == 2) {
                        peak2Index = i;
                        break;
                    }
                }
            }

            if (peak2Index == -1) {
                resultText.setText("No second peak found");
                resultText2.setText("No second peak found");
                return;
            }

            // Step 2: Trace back from peak2 to find the first valid valley (<5)
            int valleyIndex = -1;
            for (int i = peak2Index - PEAK_WINDOW_SIZE; i >= PEAK_WINDOW_SIZE; i--) {
                if (isLocalValley(array, i)) {
                    valleyIndex = i;
                    break;
                }
            }

            if (valleyIndex == -1) {
                resultText.setText("No valid valley (<5) found");
                resultText2.setText("Peak2 at: " + peak2Index);
                return;
            }

            // Step 3: Find first peak near position 40 (prioritize target range)
            int peak1Index = -1;
            // Define range around 40, clamped to valid data bounds
            int rangeStart = Math.max(PEAK_WINDOW_SIZE, 40 - PEAK1_TARGET_RANGE);
            int rangeEnd = Math.min(valleyIndex - PEAK_WINDOW_SIZE, 40 + PEAK1_TARGET_RANGE);

            // First check the target range around 40
            for (int i = rangeEnd; i >= rangeStart; i--) {
                if (isLocalPeak(array, i)) {
                    peak1Index = i;
                    break;
                }
            }

            // If no peak in target range, fall back to the last peak before the valley
            if (peak1Index == -1) {
                for (int i = valleyIndex - PEAK_WINDOW_SIZE; i >= PEAK_WINDOW_SIZE; i--) {
                    if (isLocalPeak(array, i)) {
                        peak1Index = i;
                        break;
                    }
                }
            }

            // Update UI with results
            if (peak1Index != -1) {
                resultText.setText("Peak1 at: " + peak1Index);
                resultText2.setText("Peak2 at: " + peak2Index);
            } else {
                resultText.setText("No Peak1 found");
                resultText2.setText("Peak2 at: " + peak2Index);
            }

        } catch (Exception e) {
            e.printStackTrace();
            resultText.setText("Error: " + e.getMessage());
        }
    }
}

Key Improvements Breakdown

  1. Noise Filtering: The isLocalPeak and isLocalValley methods use a sliding window to ignore small, spiky peaks—only values that are the clear highest/lowest in their surrounding area count as valid.
  2. Sequence Enforcement: We strictly follow your desired flow: find the second peak first, then trace back to a valley below 5, then locate the first peak.
  3. Position Constraint for Peak1: We prioritize searching for Peak1 in a configurable range around position 40. If no peak is found there, we fall back to the last valid peak before the valley.
  4. File Write Fix: Corrected the newline character from /n to \n so your saved data file has proper line breaks.

Tuning Tips

  • Adjust PEAK_WINDOW_SIZE: Increase this value (e.g., to 5) if small peaks are still slipping through; decrease it if valid peaks are being missed.
  • Modify PEAK1_TARGET_RANGE: Widen or narrow the range around position 40 to adjust how strictly we enforce the "near 40" requirement.

内容的提问来源于stack exchange,提问作者Joe Dobbs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:18:58