Photo by Andrea Zanenga on Unsplash

React Native Camera: Enhancing Stability with Accelerometer- Part2

Fadi Shaar
7 min readJan 11, 2023

--

Using the pitch and roll approach as described in part1, you can quickly and easily determine if the device is stable based on the angle at which it is tilted. This can be useful in situations where you want to detect small movements of the device, such as when taking a photo or shooting a video. However, the pitch and roll approach can be subject to error, as these angles are affected by both the rotation and the translation of the device.

Alternatively, using other approaches such as standard deviation of the accelerometer data, or using some advanced algorithm such as Kalman Filter and complimentary filter can give you more accurate representation of the device’s stability. In this article (part2), will discuss three differnt methods to check the stability of a mobile device:

  • Standard deviation of the accelerometer data
  • Kalman Filter
  • complimentary filter

Standard Deviation of Accelerometer Data

The standard deviation of the accelerometer data can be used to check the stability of a device. By looking at the variance of the Accelerometer data, you can determine how much the device is moving, which can be useful for detecting larger movements of the device or for measuring the level of activity of the user. This approach can give you a more accurate measurement of the stability of the device, but it will be more complex and would require more processing power.

Here’s an example of how you might use the standard deviation of the accelerometer data over a period of time to determine the stability of the device:

import { Accelerometer } from 'react-native-sensors';
import { useState, useEffect } from 'react';

function App() {
const [accelerometerData, setAccelerometerData] = useState([]);
const [isPhoneStable, setIsPhoneStable] = useState(false);

useEffect(() => {
const subscription = new Accelerometer({
updateInterval: 100 // optional
})
.subscribe(({ x, y, z }) => {
setAccelerometerData(prevData => [...prevData, { x, y, z }]);
});

return () => subscription.unsubscribe();
}, []);

useEffect(() => {
if (accelerometerData.length >= 10) {
const { x, y, z } = accelerometerData.reduce((acc, curr) => {
acc.x.push(curr.x);
acc.y.push(curr.y);
acc.z.push(curr.z);
return acc;
}, { x: [], y: [], z: [] });
const stability = checkStability(x, y, z);
setIsPhoneStable(stability);
setAccelerometerData([]);
}
}, [accelerometerData]);

return (
<View>
{isPhoneStable ? <Button title="Take a photo" onPress={onTakePhoto} /> : <Text>Hold steady...</Text>}
</View>
);
}

function checkStability(x, y, z) {
const threshold = 0.2;
const x_std = standardDeviation(x);
const y_std = standardDeviation(y);
const z_std = standardDeviation(z);
return x_std < threshold && y_std < threshold && z_std < threshold;
}

function standardDeviation(values) {
const avg = mean(values);

const squareDiffs = values.map(value => {
const diff = value - avg;
return diff * diff;
});

const avgSquareDiff = mean(squareDiffs);
return Math.sqrt(avgSquareDiff);
}

function mean(data) {
const sum = data.reduce((sum, value) => {
return sum + value;
}, 0);
return sum / data.length;
}

This example uses the standard deviation of the accelerometer data over a period of time to determine the stability of the device. It maintains a buffer of the last 10 accelerometer readings and uses those to calculate the standard deviation. Then it compares this standard deviation with a threshold value of 0.2, if all standard deviation (x, y, z) is less than threshold then it returns true, indicating that the phone is stable. You can adjust the threshold value and the number of samples used to calculate the standard deviation to match your requirements.

Kalman Filter

A Kalman filter is a mathematical algorithm that can estimate the state of a system based on a series of measurements. It is typically used to estimate the state of a device when the measurements are corrupted by noise. In the case of determining the stability of a device, a Kalman filter could be used to estimate the device’s stability based on a series of accelerometer measurements.

To implement a Kalman filter, you can use a third-party library such as kalmanjs. First, you would need to initialize the filter with some initial parameters such as the measurement noise and the process noise.

Here’s an example of how you might implement a Kalman filter in React Native:

import { useState, useEffect } from 'react';
import { Accelerometer } from 'react-native-sensors';
import {KalmanFilter} from 'kalmanjs';

function App() {
const [accelerometerData, setAccelerometerData] = useState({});
const [isPhoneStable, setIsPhoneStable] = useState(false);

const kalmanX = new KalmanFilter({R: 0.01, Q: 3});
const kalmanY = new KalmanFilter({R: 0.01, Q: 3});
const kalmanZ = new KalmanFilter({R: 0.01, Q: 3});

useEffect(() => {
const subscription = new Accelerometer({
updateInterval: 100 // optional
}).subscribe(({ x, y, z }) => {
setAccelerometerData({ x: kalmanX.filter(x), y: kalmanY.filter(y), z: kalmanZ.filter(z) });
});
return () => subscription.unsubscribe();
}, []);

useEffect(() => {
const stability = checkStability(accelerometerData);
setIsPhoneStable(stability);
}, [accelerometerData]);

return (
<View>
{isPhoneStable ? <Button title="Take a photo" onPress={onTakePhoto} /> : <Text>Hold steady...</Text>}
</View>
);
}

Please keep in mind that these are more complex approaches and it would need a lot of tweaking and fine-tuning to get the desired result. Moreover, it would also require more processing power. It’s also important to note that the specific implementation of a Kalman filter can vary depending on the system’s dynamics and the requirements of the application.

Complementary Filter

Another advanced approach to determining the stability of a device is to use a complementary filter. This approach combines the data from the accelerometer and the gyroscope sensors to estimate the device’s stability. The complementary filter fuses the data from these two sensors by applying a low-pass filter to the accelerometer data and a high-pass filter to the gyroscope data.

Here is an example of how you might implement a complementary filter using the react-native-sensors library:

import { useState, useEffect } from 'react';
import { Accelerometer, Gyroscope } from 'react-native-sensors';

function App() {
const [accelerometerData, setAccelerometerData] = useState({});
const [gyroscopeData, setGyroscopeData] = useState({});
const [isPhoneStable, setIsPhoneStable] = useState(false);
const [pitch, setPitch] = useState(0);
const [roll, setRoll] = useState(0);

const alpha = 0.8;

useEffect(() => {
const accelerometerSubscription = new Accelerometer({
updateInterval: 100 // optional
}).subscribe(({ x, y, z }) => {
setAccelerometerData({ x, y, z });
});
const gyroscopeSubscription = new Gyroscope({
updateInterval: 100 // optional
}).subscribe(({ x, y, z }) => {
setGyroscopeData({ x, y, z });
});

// use the data from both sensors to calculate pitch and roll
if (accelerometerData.x && gyroscopeData.x) {
const pitchAcc = Math.atan2(-accelerometerData.x, Math.sqrt(accelerometerData.y * accelerometerData.y + accelerometerData.z * accelerometerData.z));
const rollAcc = Math.atan2(accelerometerData.y, accelerometerData.z);
setPitch(alpha * (pitch + gyroscopeData.x * dt) + (1 - alpha) * pitchAcc);
setRoll(alpha * (roll + gyroscopeData.y * dt) + (1 - alpha) * rollAcc);
}
// check for stability
useEffect(() => {
const stability = checkStability(pitch, roll);
setIsPhoneStable(stability);
}, [pitch, roll]);

return (
<View>
{isPhoneStable ? <Button title="Take a photo" onPress={onTakePhoto} /> : <Text>Hold steady...</Text>}
</View>
);
}

function checkStability(pitch, roll) {
const threshold = 5; // in degrees
return Math.abs(pitch) < threshold && Math.abs(roll) < threshold;
}

In the example above, the Accelerometer and Gyroscope classes are used from the react-native-sensors library to subscribe to the accelerometer and gyroscope sensor data, respectively.

It also uses a complementary filter to combine the data from the two sensors to estimate the device’s pitch and roll angles. The complementary filter is implemented using the equations provided in the code snippet. The variable alpha controls the weight of the gyroscope data in the filter and dt is the time difference between two data samples.

It uses these angles to check the stability by comparing the pitch and roll angles with a threshold of 5 degrees, if it’s true then device is stable.

The checkStability method is called with these angles and returns true if both angles are within the threshold and false otherwise. The returned value is used to enable or disable the button to take a photo, depending on whether the device is stable or not.

Please keep in mind that this is just an example implementation and you might need to adjust the value of alpha and dt and the threshold based on your requirements and you might also need to fine-tune the parameters to get the most accurate result. It is also important to test the implementation on different devices and in different conditions to ensure that it works well and does not have any unintended side effects.

In conclusion, there are several approaches that can be used to check the stability of a device in React Native. Each approach has its own set of advantages and disadvantages, and the choice of approach will depend on the specific requirements of the app and the level of stability that needs to be detected.

Pitch and roll angles can be a simple approach to determine stability and it’s suitable for detecting small movements of the device, but it can be subject to error.

Standard deviation of the accelerometer data can give you a more accurate measurement of the stability of the device, but it will be more complex and requires more processing power.

Kalman filter, and complimentary filter are advanced approaches and they are more accurate but require more processing power and more complex calculations.

Ultimately, it’s important to test the implementation on different devices and in different conditions to ensure that it works well and does not have any unintended side effects.

I hope you found this article informative and enjoyable to read…

--

--

Fadi Shaar

Passionate machine learning engineer with a Ph.D in computer engineering specializing in computer vision.