使用react-native-sensors的磁力计实现平滑定向指南针

问题描述 投票:0回答:4

我正在使用

react-native-sensors
磁力计开发一个指南针应用程序。我得到了正确的值,并且罗盘工作正常,主要问题是罗盘的快速更新,方向变化过于频繁,变化为+-5度。 我想做一个平滑的定向罗盘。

_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)
  }


//Inside ComponentDidMount
magnetometer.subscribe(({ x, y, z, timestamp }) =>
      this.setState({ sensorValue: this._angle({ x, y, z }) })

android react-native react-native-sensors
4个回答
3
投票

发现一个答案听起来类似于SamuelPS的答案,我使用了LPF:用于JavaScript的低通滤波器它只是更加优化和平滑。

constructor(props) {
    super(props)
    LPF.init([])
  }

_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(LPF.next(angle))
  }

1
投票

我会提出两件事。

不要用磁力计的每个输出来更新您的状态。相反,对数据进行某种过滤。 一个简单的例子就是减少采样。假设磁力计为您提供 1000 个样本/秒(我编造的数据)。每秒对视图进行 1000 次更新实在是太多了,而不是创建一个包含 200 个样本的缓冲区,并在每次满时设置这 200 个样本的平均值的状态。在这种情况下,每秒只有 5 次更新,从而大大减少了振动感。在这里使用不同的值进行一些实验,直到找到所需的输出。如果你想要更平滑的东西,重叠缓冲区也可以工作:200 个样本缓冲区,但不必每次缓冲区满时都重置缓冲区,只需删除第一个 100 个样本即可。因此,样本减少了 1/10,但每个输出都是 100 个新样本和 100 个已经影响输出的样本之间的平均值。

第二件事是不要将罗盘指针直接放在磁力计值的位置,否则,看起来像指针在跳动(零平滑)。创建过渡动画以在改变位置时产生平滑的运动。

有了这两件事,应该就可以顺利进行了。 我希望这些信息有用,祝你的指南针好运!!


0
投票

添加 Abdullah Yahya 的答案,安装并导入 LPF 模块。设置LPF平滑值并检查是否仍然存在波动。

import LPF from "lpf";

constructor() {
  super();
  LPF.init([]);
  LPF.smoothing = 0.2;
}

_angle = magnetometer => {
let angle = 0;
if (magnetometer) {
  let {x, y} = 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(LPF.next(angle));
};

请参阅此存储库 - react-native-compass 了解详细信息。


0
投票

0° 附近的问题是因为,在计算平均值时,我们最终会混合像

[0, 359, 352, 2, 3, ..]
这样的值。为了避免“跳来跳去”的方向,我们没有对
angle
值进行平均。相反,我们计算向量的平均分量。

例如:

import { magnetometer, SensorTypes, setUpdateIntervalForType } from 'react-native-sensors';


const calculateHeading = (x: number, y: number) => {
  let angle;
  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 angle;
};

const angleToVector = (angle: number) => {
  const rad = (angle * Math.PI) / 180;
  return {
    x: Math.cos(rad),
    y: Math.sin(rad),
  };
};

const vectorToAngle = (vector: { x: number; y: number }) => {
  const rad = Math.atan2(vector.y, vector.x);
  const angle = rad * (180 / Math.PI);
  return angle >= 0 ? angle : angle + 360;
};

export const useOrientation = (bufferSize = 10) => {
  const [buffer, setBuffer] = useState(new Array<number>());
  const [angle, setAngle] = useState(0);

  useEffect(() => {
    setUpdateIntervalForType(SensorTypes.magnetometer, 100);

    const subscription = magnetometer.subscribe(({ x, y }) => {
      const value = calculateHeading(x, y);
      buffer.unshift(value);
      while (buffer.length > bufferSize) {
        buffer.pop();
      }
      const vectors = buffer.map(angleToVector);

      const avgVector = vectors.reduce(
        (acc, vec) => ({
          x: acc.x + vec.x,
          y: acc.y + vec.y,
        }),
        { x: 0, y: 0 },
      );

      avgVector.x /= buffer.length;
      avgVector.y /= buffer.length;

      const averageAngle = vectorToAngle(avgVector);

      setBuffer([...buffer]);
      setAngle(averageAngle);
    });

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

  return angle;
};
© www.soinside.com 2019 - 2024. All rights reserved.