IMU的态度估计来自角速度的态度旋转大约是预期值的一半。为什么?

发布于 2025-02-06 13:37:10 字数 4783 浏览 2 评论 0原文

我正在努力按照 https://ahrs.readthedocs.io/en/latest/filters/madgwick.html 。我已经可以基于磁力计确定方向(但是,这遭到了非常快速的动作,因此我想走陀螺仪路线)。

旋转基本上可以执行它应该做的事情(我使用四元组的旋转矩阵实时渲染一个彩色盒子),但速率要低得多。从本质上讲,由我的代码产生的旋转大约(但不是很快)的1/2速度与现实世界中的旋转速度一样快。

简单地将角度乘以2

  • 并不能完全解决问题(仍然存在某个滞后)
  • 并没有帮助我理解问题

是我在概念上正在做的事情吗?我只是看不到一个主要问题吗?由于我需要这些东西来撰写主人的论文(在运动科学中),因此对任何帮助都非常感谢。

更新:我已经实施了@jamestursa的建议,并切换到quaternion.createfromaxisangle()而不是quaternion.createfromyawpitchroll()。不幸的是,这并不能解决问题。我还为四个类别类实施了单位测试,以检查其表现是否如预期的。确实如此,所以我怀疑这是错误的。

阅读有效的quaternion角度速度 ,但这完全可以做我想的,只是进一步减慢了旋转。

因此,我仍然被旋转所困扰,而旋转基本上大约是(但不是)应该做的一半。同样,任何帮助都会受到调查。

/// <summary>
        /// Numerically integrate angular velocities in x,y,z to get orientation in x,y,z
        /// Does not work. Rotation happens at around (but not quite) half the rate as in the real world.
        /// </summary>
        /// <param name="dt">time in ms since last invocation</param>
        private void UpdateAngularVelocities(double dt)
        {
            // rotation threshold (°/sec) to minimize noise
            // set to 0 for the time being
            double threshold = 0.0; 

            // iterative quaternion multiplication based on madgwick filter
            // https://ahrs.readthedocs.io/en/latest/filters/madgwick.html
            // keep an AngularOrientation Vector for illustration purposes

            // apply rotation velocity threshold
            // ie, discard any rotation that is slower than the defined threshold
            var wtx = (Math.Abs(w[0]) > threshold) ? w[0] : 0.0;
            var wty = (Math.Abs(w[1]) > threshold) ? w[1] : 0.0;
            var wtz = (Math.Abs(w[2]) > threshold) ? w[2] : 0.0;

            // rotation velocities, stored as a Vector for easier use
            // theses will be used to onstruct the Quaternion
            Vector3 deltaTRotation = new Vector3((float)ConvertToRadians(wtx), (float)ConvertToRadians(wty), (float)ConvertToRadians(wtz));
            Vector3 deltaAxis = deltaTRotation;

            // scale rotation with time
            // dt is supplied in ms, we need s to get from °/s to °
            // use half angle because of
            // https://stackoverflow.com/questions/24197182/efficient-quaternion-angular-velocity/24201879#24201879
            deltaTRotation.X *= (float)(dt / 1000.0) * 0.5f;
            deltaTRotation.Y *= (float)(dt / 1000.0) * 0.5f;
            deltaTRotation.Z *= (float)(dt / 1000.0) * 0.5f;


            // build quaternion from scaled rotations
            // use Quaternion.CreateFromAxisAngle() to get simultaneous angles
            // https://stackoverflow.com/questions/72578478/imu-orientation-from-gyro
            // Quaternion qDelta = Quaternion.CreateFromYawPitchRoll(deltaTRotation.Y, deltaTRotation.X, deltaTRotation.Z);
            Quaternion qDelta = Quaternion.CreateFromAxisAngle(Vector3.Normalize(deltaAxis), deltaTRotation.Length());

            // deal with NaN, in case no Rotation takes place and hence, ||qCurrent|| = 0 which makes Normalize throw NaN
            if (qDelta.Length() > 0.0)
            {
                // combine new rotation with previous rotation
                qRotation = qDelta * qRotation;
                //AdditiveOrientation += 0.5f * Vector4.Normalize(new Vector4(deltaTRotation.X, deltaTRotation.Y, deltaTRotation.Z, deltaTRotation.W)) * qDelta.Length();
            }

            // https://ahrs.readthedocs.io/en/latest/filters/madgwick.html suggests an addition of the old state and the new state.
            // this results in the quaternion not being a unit quaternion / versor anymore. Ditch the addition and stick with simple multiplication
            //qRotation = qRotation + Quaternion.Multiply(qCurrent * qRotation, (float)(0.5));// * dt / 1000.0)); 
            //qRotation = Quaternion.Normalize(qRotation);


            // rotate magnetic orientation for illustration purposes
            // always start rotation from (arbitrary) [1,0,0] and apply most recent quaternion
            // does not work yet
            // rotates roughly half the angle it should
            AngularOrientation = Vector4.Transform(new Vector4(1.0f,0.0f,0.0f,1.0f), qRotation);

            // save angular velocities for later use
            OmegaXData.Add(deltaTRotation.X);
            OmegaYData.Add(deltaTRotation.Y);
            OmegaZData.Add(deltaTRotation.Z);
        }

I'm working on getting the orientation of an IMU in space, following the instructions from https://ahrs.readthedocs.io/en/latest/filters/madgwick.html. I can already determine the orientation on the basis of the magnetometer (this suffers badly from very fast movements though, so I want to go the gyro route).

The rotation basically does what it is supposed to do (I use the rotation matrix from the quaternion to render a colored box in real time), but at a much lower rate. In essence, the rotation that results from my code is approximately (but not quite) 1/2 as fast as the one in the real world.

Simply multiplying the angle with 2

  • does not quite solve the problem (there still results a certain lag)
  • does not help me in understanding the problem

Is what I am doing conceptually sound? Is there a major issue I just don't see? As I need this stuff for my master's thesis (in exercise science), any help is greatly appreciated.

UPDATE: I have implemented the advice by @JamesTursa and switched to Quaternion.CreateFromAxisAngle() instead of Quaternion.CreateFromYawPitchRoll(). Unfortunately, this did not solve the issue. I have also implemented a unit test for the Quaternion class to check whether or not it behaves as expected. It does, so I doubt there's an error with that.

After reading through Efficient quaternion angular velocity, I have also tried using the half angle, but this does exactly what I supposed and simply slows the rotation further.

So, I am still stuck with a rotation that basically does approximately (but not quite) half of what it is supposed to. Again, any help is eppreciated.

/// <summary>
        /// Numerically integrate angular velocities in x,y,z to get orientation in x,y,z
        /// Does not work. Rotation happens at around (but not quite) half the rate as in the real world.
        /// </summary>
        /// <param name="dt">time in ms since last invocation</param>
        private void UpdateAngularVelocities(double dt)
        {
            // rotation threshold (°/sec) to minimize noise
            // set to 0 for the time being
            double threshold = 0.0; 

            // iterative quaternion multiplication based on madgwick filter
            // https://ahrs.readthedocs.io/en/latest/filters/madgwick.html
            // keep an AngularOrientation Vector for illustration purposes

            // apply rotation velocity threshold
            // ie, discard any rotation that is slower than the defined threshold
            var wtx = (Math.Abs(w[0]) > threshold) ? w[0] : 0.0;
            var wty = (Math.Abs(w[1]) > threshold) ? w[1] : 0.0;
            var wtz = (Math.Abs(w[2]) > threshold) ? w[2] : 0.0;

            // rotation velocities, stored as a Vector for easier use
            // theses will be used to onstruct the Quaternion
            Vector3 deltaTRotation = new Vector3((float)ConvertToRadians(wtx), (float)ConvertToRadians(wty), (float)ConvertToRadians(wtz));
            Vector3 deltaAxis = deltaTRotation;

            // scale rotation with time
            // dt is supplied in ms, we need s to get from °/s to °
            // use half angle because of
            // https://stackoverflow.com/questions/24197182/efficient-quaternion-angular-velocity/24201879#24201879
            deltaTRotation.X *= (float)(dt / 1000.0) * 0.5f;
            deltaTRotation.Y *= (float)(dt / 1000.0) * 0.5f;
            deltaTRotation.Z *= (float)(dt / 1000.0) * 0.5f;


            // build quaternion from scaled rotations
            // use Quaternion.CreateFromAxisAngle() to get simultaneous angles
            // https://stackoverflow.com/questions/72578478/imu-orientation-from-gyro
            // Quaternion qDelta = Quaternion.CreateFromYawPitchRoll(deltaTRotation.Y, deltaTRotation.X, deltaTRotation.Z);
            Quaternion qDelta = Quaternion.CreateFromAxisAngle(Vector3.Normalize(deltaAxis), deltaTRotation.Length());

            // deal with NaN, in case no Rotation takes place and hence, ||qCurrent|| = 0 which makes Normalize throw NaN
            if (qDelta.Length() > 0.0)
            {
                // combine new rotation with previous rotation
                qRotation = qDelta * qRotation;
                //AdditiveOrientation += 0.5f * Vector4.Normalize(new Vector4(deltaTRotation.X, deltaTRotation.Y, deltaTRotation.Z, deltaTRotation.W)) * qDelta.Length();
            }

            // https://ahrs.readthedocs.io/en/latest/filters/madgwick.html suggests an addition of the old state and the new state.
            // this results in the quaternion not being a unit quaternion / versor anymore. Ditch the addition and stick with simple multiplication
            //qRotation = qRotation + Quaternion.Multiply(qCurrent * qRotation, (float)(0.5));// * dt / 1000.0)); 
            //qRotation = Quaternion.Normalize(qRotation);


            // rotate magnetic orientation for illustration purposes
            // always start rotation from (arbitrary) [1,0,0] and apply most recent quaternion
            // does not work yet
            // rotates roughly half the angle it should
            AngularOrientation = Vector4.Transform(new Vector4(1.0f,0.0f,0.0f,1.0f), qRotation);

            // save angular velocities for later use
            OmegaXData.Add(deltaTRotation.X);
            OmegaYData.Add(deltaTRotation.Y);
            OmegaZData.Add(deltaTRotation.Z);
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文