aboutsummaryrefslogtreecommitdiff
path: root/src/argaze/TobiiGlassesPro2/TobiiInertialMeasureUnit.py
blob: 6bc89f7168716f48dc1ce131e9160fe2989788f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python

import json
import time

from argaze import DataStructures
from argaze.TobiiGlassesPro2 import TobiiData

import numpy
from scipy.optimize import curve_fit

class TobiiInertialMeasureUnit():
    """Ease Tobbi IMU data handling."""

    def __init__(self):
        """Define IMU calibration data."""

        self.__gyroscope_offset = numpy.array([0., 0., 0.])
        self.__accelerometer_coefficients = numpy.array([[1., 0.], [1., 0.], [1., 0.]])

    def load_calibration_file(self, calibration_filepath):
        """Load IMU calibration from a .json  file."""

        with open(calibration_filepath) as calibration_file:

            # Deserialize .json
            # TODO find a better way
            calibration_data = json.load(calibration_file)

            # Load calibration data
            self.__gyroscope_offset = numpy.array(calibration_data['gyroscope_offset'])
            self.__accelerometer_coefficients = numpy.array(calibration_data['accelerometer_coefficients'])

    def save_calibration_file(self, calibration_filepath):
        """Save IMU calibration into .json file."""

        calibration_data = {
            'gyroscope_offset': list(self.__gyroscope_offset), 
            'accelerometer_coefficients': [list(self.__accelerometer_coefficients[0]), list(self.__accelerometer_coefficients[1]), list(self.__accelerometer_coefficients[2])]
            }

        with open(calibration_filepath, 'w', encoding='utf-8') as calibration_file:

            json.dump(calibration_data, calibration_file, ensure_ascii=False, indent=4)

    def calibrate_gyroscope_offset(self, gyroscope_ts_buffer):

        # Consider gyroscope values without timestamps
        gyroscope_values = []
        for ts, data_object in gyroscope_ts_buffer.items():
            gyroscope_values.append(data_object.value)

        # Calculate average value for each axis
        gx_offset = numpy.mean(numpy.array(gyroscope_values)[:, 0])
        gy_offset = numpy.mean(numpy.array(gyroscope_values)[:, 1])
        gz_offset = numpy.mean(numpy.array(gyroscope_values)[:, 2])

        # Store result
        self.__gyroscope_offset = numpy.array([gx_offset, gy_offset, gz_offset])

        return self.__gyroscope_offset

    def get_gyroscope_offset(self):
        """Get gyroscope offset."""

        return self.__gyroscope_offset

    def apply_gyroscope_offset(self, gyroscope_data_object):
        """Remove gyroscope offset to given gyroscope data."""

        return TobiiData.Gyroscope(gyroscope_data_object.value - self.__gyroscope_offset)

    def _accelerometer_linear_fit(self, x, a, b):

        return a * x + b

    def calibrate_accelerometer_axis_coefficients(self, axis, upward_ts_buffer, downward_ts_buffer, perpendicular_ts_buffer):
        """Calibrate one accelerometer axis using three data set (upward/+1g, downward/-1g, perpendicular/0g) for linear fit."""

        # Consider accelerometer axis values without timestamps
        accelerometer_values = []
        expected_values = []

        for (upward_ts, upward_data_object), (downward_ts, downward_data_object), (perpendicular_ts, perpendicular_data_object) in zip(upward_ts_buffer.items(), downward_ts_buffer.items(), perpendicular_ts_buffer.items()):
            
            accelerometer_values.append(upward_data_object.value[axis])
            expected_values.append(+1.0)

            accelerometer_values.append(downward_data_object.value[axis])
            expected_values.append(-1.0)

            accelerometer_values.append(perpendicular_data_object.value[axis])
            expected_values.append(0.0)

        # Find optimal coefficients according linear fit between accelerometer values and expected values
        optimal_coefficients, _ = curve_fit(self._accelerometer_linear_fit, accelerometer_values, expected_values, maxfev = 10000)

        # Store results for the given axis
        self.__accelerometer_coefficients[axis] = numpy.array(optimal_coefficients)

    def get_accelerometer_coefficients(self):
        """Get accelerometer coefficients."""

        return self.__accelerometer_coefficients

    def apply_accelerometer_coefficients(self, accelerometer_data_object):
        """Add accelerometer offset to given accelerometer data."""

        x = self._accelerometer_linear_fit(accelerometer_data_object.value[0], *self.__accelerometer_coefficients[0])
        y = self._accelerometer_linear_fit(accelerometer_data_object.value[1], *self.__accelerometer_coefficients[1])
        z = self._accelerometer_linear_fit(accelerometer_data_object.value[2], *self.__accelerometer_coefficients[2])

        return TobiiData.Accelerometer([x, y , z])