Source code for qmt.blocks.oriest

# SPDX-FileCopyrightText: 2021 Daniel Laidig <laidig@control.tu-berlin.de>
#
# SPDX-License-Identifier: MIT
import math

import numpy as np

import qmt


def _calcAccMagDisAngle(quat, acc, mag):
    try:  # using math.cos and the VQF functions is a lot faster
        from vqf import VQF
        accE = VQF.quatRotate(quat, acc)
        magE = VQF.quatRotate(quat, mag)
        VQF.normalize(accE)
        VQF.clip(accE, -1, 1)
        return np.array([math.acos(accE[2]), abs(math.atan2(magE[0], magE[1]))], float)
    except ImportError:
        accE = qmt.normalized(qmt.rotate(quat, acc))
        magE = qmt.rotate(quat, mag)
        return np.array([np.arccos(np.clip(accE[2], -1, 1)), np.abs(np.arctan2(magE[0], magE[1]))], float)


[docs] class OriEstVQFBlock(qmt.Block): """ VQF orientation estimation algorithm. See https://github.com/dlaidig/vqf, https://doi.org/10.1016/j.inffus.2022.10.014, and https://arxiv.org/abs/2203.17024 for more information about this algorithm. This algorithm is also available as a function: :meth:`qmt.oriEstVQF`. """ def __init__(self, Ts): """ :param Ts: sampling time in seconds """ super().__init__() self.Ts = Ts from vqf import VQF self.obj = VQF(Ts) self.params = self.obj.params
[docs] def command(self, command): assert command == ['reset'] self.obj.resetState()
[docs] def setParams(self, params): super().setParams(params) for name, value in params.items(): if name == 'tauAcc': self.obj.setTauAcc(value) elif name == 'tauMag': self.obj.setTauMag(value) elif name == 'motionBiasEstEnabled': self.obj.setMotionBiasEstEnabled(value) elif name == 'restBiasEstEnabled': self.obj.setRestBiasEstEnabled(value) elif name == 'magDistRejectionEnabled': self.obj.setMagDistRejectionEnabled(value) elif name in ('restThGyr', 'restThAcc'): self.obj.setRestDetectionThresholds(self.params['restThGyr'], self.params['restThAcc']) else: print(f'ignored param {name}')
[docs] def step(self, gyr, acc, mag=None, debug=False): self.obj.update(gyr, acc, mag) quat = self.obj.getQuat6D() if mag is None else self.obj.getQuat9D() if not debug: return quat debug = {} state = self.obj.state debug['bias'] = state['bias'] # note that state['lastAccDisAngle'] is different since it is based on the filtered acceleration debug['disAngle'] = _calcAccMagDisAngle(quat, acc, mag) debug['state'] = state debug['params'] = self.obj.params return quat, debug
[docs] class OriEstMadgwickBlock(qmt.Block): """ Madgwicks's orientation estimation algorithm. See https://doi.org/10.1109/ICORR.2011.5975346 for more information about this algorithm. Based on the C++ implementation by Sebastian Madgwick, available at https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/. This algorithm is also available as a function: :meth:`qmt.oriEstMadgwick`. """ def __init__(self, Ts): """ :param Ts: sampling time in seconds """ super().__init__() self.Ts = Ts self.params['beta'] = 0.1 from qmt.cpp.madgwick import MadgwickAHRS self.obj = MadgwickAHRS(self.params['beta'], 1/Ts) self.qE = np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], float) # adjusts reference frame to ENU
[docs] def command(self, command): """ Supported commands: - **['reset']**: resets the orientation estimation state """ assert command == ['reset'] self.obj.setState(np.array([1, 0, 0, 0], float))
[docs] def setParams(self, params): """ Supported parameters: - **beta**: algorithm gain """ super().setParams(params) self.obj.setBeta(self.params['beta'])
[docs] def step(self, gyr, acc, mag, debug=False): """ :param gyr: gyroscope measurement [rad/s] :param acc: accelerometer measurement [m/s^2] :param mag: magnetometer measurement or None [any unit] :param debug: enables debug output :return: - **quat**: orientation quaternion - **debug**: dict with debug values (only if debug==True) """ quat = self.obj.update(gyr, acc, mag) quat = qmt.qmult(self.qE, quat) if not debug: return quat debug = dict(bias=np.zeros(3), disAngle=_calcAccMagDisAngle(quat, acc, mag)) return quat, debug
[docs] class OriEstMahonyBlock(qmt.Block): """ Mahony's orientation estimation algorithm. See https://dx.doi.org/10.1109/TAC.2008.923738 for more information about this algorithm. Based on the C++ implementation by Sebastian Madgwick, available at https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/. This algorithm is also available as a function: :meth:`qmt.oriEstMahony`. """ def __init__(self, Ts): """ :param Ts: sampling time in seconds """ super().__init__() self.Ts = Ts self.params = {'Kp': 0.5, 'Ki': 0.0} from qmt.cpp.madgwick import MahonyAHRS self.obj = MahonyAHRS(self.params['Kp'], self.params['Ki'], 1/Ts) self.qE = np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], float) # adjusts reference frame to ENU
[docs] def command(self, command): """ Supported commands: - **['reset']**: resets the orientation estimation state """ assert command == ['reset'] self.obj.setState(np.array([1, 0, 0, 0], float), np.zeros(3))
[docs] def setParams(self, params): """ Supported parameters: - **Kp**: proportional gain - **Ki**: integral gain (for gyroscope bias estimation) """ super().setParams(params) self.obj.setParams(self.params['Kp'], self.params['Ki'])
[docs] def step(self, gyr, acc, mag, debug=False): """ :param gyr: gyroscope measurement [rad/s] :param acc: accelerometer measurement [m/s^2] :param mag: magnetometer measurement or None [any unit] :param debug: enables debug output :return: - **quat**: orientation quaternion - **debug**: dict with debug values (only if debug==True) """ quat, bias = self.obj.update(gyr, acc, mag) quat = qmt.qmult(self.qE, quat) if not debug: return quat debug = dict(bias=bias, disAngle=_calcAccMagDisAngle(quat, acc, mag)) return quat, debug
[docs] class OriEstIMUBlock(qmt.Block): """ OriEstIMU orientation estimation algorithm. See https://dx.doi.org/10.1016/j.ifacol.2017.08.1534 for more information about this algorithm. This algorithm is also available as a function: :meth:`qmt.oriEstIMU`. """ def __init__(self, Ts): """ :param Ts: sampling time in seconds """ super().__init__() self.Ts = Ts self.params = {'tauAcc': 2.0, 'tauMag': 2.0, 'zeta': 0.0, 'accRating': 1.0} from qmt.cpp.oriestimu import OriEstIMU self.obj = OriEstIMU(1/Ts, **self.params)
[docs] def command(self, command): """ Supported commands: - **['reset']**: resets the orientation estimation state """ assert command == ['reset'] self.obj.resetEstimation(np.array([1, 0, 0, 0], float), np.zeros(3))
[docs] def setParams(self, params): """ Supported parameters: - **tauAcc**: time constants for acc correction (50% time) [must be >0], [in seconds] - **tauMag**: time constants for mag correction (50% time) [must be >0], [in seconds] - **zeta**: bias estimation strength [no unit] - **accRating**: enables raw rating of accelerometer, set to 0 to disable """ super().setParams(params) self.obj.setFilterParameter(self.params['tauAcc'], self.params['tauMag'], self.params['zeta']) self.obj.setRating(self.params['accRating'])
[docs] def step(self, gyr, acc, mag, debug=False): """ :param gyr: gyroscope measurement [rad/s] :param acc: accelerometer measurement [m/s^2] :param mag: magnetometer measurement or None [any unit] :param debug: enables debug output :return: - **quat**: orientation quaternion - **debug**: dict with debug values (only if debug==True) """ quat, bias, error = self.obj.update(acc, gyr, mag) if not debug: return quat debug = dict(bias=-np.asarray(bias, float), disAngle=error) return quat, debug