如何计算给定 (x,y,z) 磁力计和加速度计数据的航向 (N/W/S/E)?
How can I calculate the heading (N/W/S/E) given (x,y,z) magnetometer and accelerometer data?
我正在使用 react-native-sensor 从这些传感器中获取原始数据。
import {magnetometer, acclerometer} from 'react-native-sensors';
const subscription = accelerometer.subscribe(({ x, y, z, timestamp }) =>
console.log({ x, y, z, timestamp })
this.setState({ accelerometer: { x, y, z, timestamp } })
);
const subscription = magnetometer.subscribe(({ x, y, z, timestamp }) =>
console.log({ x, y, z })
this.setState({ magnetometer: { x, y, z, timestamp } })
);
给定这6个数据点,我怎样才能得到度数和方向呢?什么是合适的算法?
我不明白这个answer中的算法。这个答案利用了 alpha、beta、gamma……这和 "x, y, z" 一样吗?为什么只使用 3 个数据点而不是 6 个?为什么有些 其他 答案说需要加速度计数据(用于倾斜调整?)。为什么没有利用所有 6 个数据点的答案?
(注意:文档有拼写错误 "magenetometer")
磁力计测量地球磁场。此信息与 phone 内部的加速器相结合。加速器获取有关 phone 在 space 中的位置的信息。它能够通过 phone 内的固态传感器精确定位 phone 的位置,这些传感器可以测量它们的倾斜度和运动。根据算法软件开发公司 Sensor Platforms 的说法,这些设备提供的信息意味着无论 phone 处于哪个方向,指南针应用程序都可以显示主要方向。
类似project: compass-react-native-non-expo under MIT License仅使用设备的内置磁力计传感器,使用包 react-native-sensors 识别方向和计算角度,使用来自磁力计的 3 个数据点:
subscribe = async () => {
new Magnetometer({
updateInterval: 100
})
.then(magnetometerObservable => {
this._subscription = magnetometerObservable;
this._subscription.subscribe(sensorData => {
console.log(sensorData);
this.setState({magnetometer: this._angle(sensorData)});
});
})
.catch(error => {
console.log("The sensor is not available");
});
};
_unsubscribe = () => {
this._subscription && this._subscription.stop();
this._subscription = null;
};
_angle = (magnetometer) => {
if (magnetometer) {
let {x, y, z} = magnetometer;
if (Math.atan2(y, x) >= 0) {
angle = Math.atan2(y, x) * (180 / Math.PI);
}
else {
angle = (Math.atan2(y, x) + 2 * Math.PI) * (180 / Math.PI);
}
}
return Math.round(angle);
};
_direction = (degree) => {
if (degree >= 22.5 && degree < 67.5) {
return 'NE';
}
else if (degree >= 67.5 && degree < 112.5) {
return 'E';
}
else if (degree >= 112.5 && degree < 157.5) {
return 'SE';
}
else if (degree >= 157.5 && degree < 202.5) {
return 'S';
}
else if (degree >= 202.5 && degree < 247.5) {
return 'SW';
}
else if (degree >= 247.5 && degree < 292.5) {
return 'W';
}
else if (degree >= 292.5 && degree < 337.5) {
return 'NW';
}
else {
return 'N';
}
};
// Match the device top with pointer 0° degree. (By default 0° starts from the right of the device.)
_degree = (magnetometer) => {
return magnetometer - 90 >= 0 ? magnetometer - 90 : magnetometer + 271;
};
另一个 project: react-native-sensor-manager 使用来自 磁力计和加速度计的 6 个数据点 来计算方向:
float[] mGravity;
float[] mGeomagnetic;
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor mySensor = sensorEvent.sensor;
WritableMap map = mArguments.createMap();
if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER)
mGravity = sensorEvent.values;
if (mySensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
mGeomagnetic = sensorEvent.values;
if (mGravity != null && mGeomagnetic != null) {
float R[] = new float[9];
float I[] = new float[9];
boolean success = mSensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success) {
long curTime = System.currentTimeMillis();
float orientation[] = new float[3];
mSensorManager.getOrientation(R, orientation);
float heading = (float)((Math.toDegrees(orientation[0])) % 360.0f);
float pitch = (float)((Math.toDegrees(orientation[1])) % 360.0f);
float roll = (float)((Math.toDegrees(orientation[2])) % 360.0f);
if (heading < 0) {
heading = 360 - (0 - heading);
}
if (pitch < 0) {
pitch = 360 - (0 - pitch);
}
if (roll < 0) {
roll = 360 - (0 - roll);
}
map.putDouble("azimuth", heading);
map.putDouble("pitch", pitch);
map.putDouble("roll", roll);
sendEvent("Orientation", map);
lastUpdate = curTime;
}
}
}
这就是我在 android 中工作的方法:
let angle = Math.atan2(y, x);
angle = angle * (180 / Math.PI)
angle = angle + 90
angle = (angle +360) % 360
来自磁力计的数据在 android 和 ios 之间不一致。我最终制作了自己的包裹:
我正在使用 react-native-sensor 从这些传感器中获取原始数据。
import {magnetometer, acclerometer} from 'react-native-sensors';
const subscription = accelerometer.subscribe(({ x, y, z, timestamp }) =>
console.log({ x, y, z, timestamp })
this.setState({ accelerometer: { x, y, z, timestamp } })
);
const subscription = magnetometer.subscribe(({ x, y, z, timestamp }) =>
console.log({ x, y, z })
this.setState({ magnetometer: { x, y, z, timestamp } })
);
给定这6个数据点,我怎样才能得到度数和方向呢?什么是合适的算法?
我不明白这个answer中的算法。这个答案利用了 alpha、beta、gamma……这和 "x, y, z" 一样吗?为什么只使用 3 个数据点而不是 6 个?为什么有些 其他 答案说需要加速度计数据(用于倾斜调整?)。为什么没有利用所有 6 个数据点的答案?
(注意:文档有拼写错误 "magenetometer")
磁力计测量地球磁场。此信息与 phone 内部的加速器相结合。加速器获取有关 phone 在 space 中的位置的信息。它能够通过 phone 内的固态传感器精确定位 phone 的位置,这些传感器可以测量它们的倾斜度和运动。根据算法软件开发公司 Sensor Platforms 的说法,这些设备提供的信息意味着无论 phone 处于哪个方向,指南针应用程序都可以显示主要方向。
类似project: compass-react-native-non-expo under MIT License仅使用设备的内置磁力计传感器,使用包 react-native-sensors 识别方向和计算角度,使用来自磁力计的 3 个数据点:
subscribe = async () => {
new Magnetometer({
updateInterval: 100
})
.then(magnetometerObservable => {
this._subscription = magnetometerObservable;
this._subscription.subscribe(sensorData => {
console.log(sensorData);
this.setState({magnetometer: this._angle(sensorData)});
});
})
.catch(error => {
console.log("The sensor is not available");
});
};
_unsubscribe = () => {
this._subscription && this._subscription.stop();
this._subscription = null;
};
_angle = (magnetometer) => {
if (magnetometer) {
let {x, y, z} = magnetometer;
if (Math.atan2(y, x) >= 0) {
angle = Math.atan2(y, x) * (180 / Math.PI);
}
else {
angle = (Math.atan2(y, x) + 2 * Math.PI) * (180 / Math.PI);
}
}
return Math.round(angle);
};
_direction = (degree) => {
if (degree >= 22.5 && degree < 67.5) {
return 'NE';
}
else if (degree >= 67.5 && degree < 112.5) {
return 'E';
}
else if (degree >= 112.5 && degree < 157.5) {
return 'SE';
}
else if (degree >= 157.5 && degree < 202.5) {
return 'S';
}
else if (degree >= 202.5 && degree < 247.5) {
return 'SW';
}
else if (degree >= 247.5 && degree < 292.5) {
return 'W';
}
else if (degree >= 292.5 && degree < 337.5) {
return 'NW';
}
else {
return 'N';
}
};
// Match the device top with pointer 0° degree. (By default 0° starts from the right of the device.)
_degree = (magnetometer) => {
return magnetometer - 90 >= 0 ? magnetometer - 90 : magnetometer + 271;
};
另一个 project: react-native-sensor-manager 使用来自 磁力计和加速度计的 6 个数据点 来计算方向:
float[] mGravity;
float[] mGeomagnetic;
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor mySensor = sensorEvent.sensor;
WritableMap map = mArguments.createMap();
if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER)
mGravity = sensorEvent.values;
if (mySensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
mGeomagnetic = sensorEvent.values;
if (mGravity != null && mGeomagnetic != null) {
float R[] = new float[9];
float I[] = new float[9];
boolean success = mSensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success) {
long curTime = System.currentTimeMillis();
float orientation[] = new float[3];
mSensorManager.getOrientation(R, orientation);
float heading = (float)((Math.toDegrees(orientation[0])) % 360.0f);
float pitch = (float)((Math.toDegrees(orientation[1])) % 360.0f);
float roll = (float)((Math.toDegrees(orientation[2])) % 360.0f);
if (heading < 0) {
heading = 360 - (0 - heading);
}
if (pitch < 0) {
pitch = 360 - (0 - pitch);
}
if (roll < 0) {
roll = 360 - (0 - roll);
}
map.putDouble("azimuth", heading);
map.putDouble("pitch", pitch);
map.putDouble("roll", roll);
sendEvent("Orientation", map);
lastUpdate = curTime;
}
}
}
这就是我在 android 中工作的方法:
let angle = Math.atan2(y, x);
angle = angle * (180 / Math.PI)
angle = angle + 90
angle = (angle +360) % 360
来自磁力计的数据在 android 和 ios 之间不一致。我最终制作了自己的包裹: