Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorientation #112

Open
wants to merge 158 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
158 commits
Select commit Hold shift + click to select a range
f7ddeda
First implementation of helper function to rotate acceleration accord…
DMegaritis Mar 13, 2024
11b88bb
First implementation of filter_signals_100Hz function high and low ba…
DMegaritis Mar 13, 2024
5985866
utils implemented with gaitmap functions using rotations instead of q…
DMegaritis Mar 14, 2024
be4220b
Improved implementation of utils
DMegaritis Mar 14, 2024
5bd9037
ValueError added
DMegaritis Mar 15, 2024
21ac8a2
First implementation of CorrectSensorOrientationDynamic
DMegaritis Mar 15, 2024
92c5656
Simplified and more robust since data are already sliced before this …
DMegaritis Mar 21, 2024
d9ae217
first implementation of CorrectOrientationSensorAxes.py
DMegaritis Mar 21, 2024
b7029b6
Updated dockstrings
DMegaritis Mar 22, 2024
cef1a32
resolved warnings
DMegaritis Mar 22, 2024
3bf800e
updated filters using the mobgap toolbox
DMegaritis Mar 25, 2024
2ea92e7
Improved selection of non-GS signal
DMegaritis Mar 26, 2024
fac639e
Updated data types
DMegaritis Mar 27, 2024
4c23ce9
Updated quat multiplication in line with MATLAB
DMegaritis Mar 28, 2024
ea66faa
Updated method of butter.
DMegaritis Apr 1, 2024
2694ec6
Updated method of butter
DMegaritis Apr 1, 2024
4ae5eb2
Updated mobgap filters
DMegaritis Apr 2, 2024
2b2202d
Fixed bugs
DMegaritis Apr 2, 2024
2c281ae
Merge remote-tracking branch 'origin/main' into Reorientation
DMegaritis Apr 2, 2024
2fe26d7
Added Reorientation after updating to mobgap
DMegaritis Apr 2, 2024
1ea128f
Added init
DMegaritis Apr 2, 2024
f7d189e
Updated renaming
DMegaritis Apr 4, 2024
633f04d
Madgwick testing on lab data
DMegaritis Apr 5, 2024
16b5704
Madgwick testing on lab data
DMegaritis Apr 5, 2024
97dff64
Madgwick application on whole array
DMegaritis Apr 9, 2024
8312381
Applying madwick in the whole array
DMegaritis Apr 10, 2024
64dcf47
Update constant (th) to reflect data in m/s2
DMegaritis Apr 11, 2024
b9b1103
Signal correction for non GS areas moved out of the main loop to allo…
DMegaritis Apr 18, 2024
f527e37
Signal correction for non GS areas moved out of the main loop to allo…
DMegaritis Apr 18, 2024
3a386ab
Updated initial orientation according to Matlab function (UpdateIMUslt2)
DMegaritis Apr 18, 2024
59fc41e
Fixed bugs
DMegaritis Apr 18, 2024
eb4f1fd
Updated dockstrings
DMegaritis May 6, 2024
a0b56c8
Updated dockstrings and tidied code
DMegaritis Jun 14, 2024
8245763
Named instead of positional access
DMegaritis Jul 3, 2024
5c63434
Named instead of positional access
DMegaritis Jul 3, 2024
c6ba9b8
Added else clause to handle short data properly
DMegaritis Jul 3, 2024
200faa5
corIMUdata is not a copy of data.
DMegaritis Jul 3, 2024
400ec59
Use of iter_gs
DMegaritis Jul 4, 2024
e61d123
fit_transform replaced manual transformation
DMegaritis Jul 4, 2024
dd23668
Vectorised standardisation
DMegaritis Jul 4, 2024
d6f34a8
Vectorised filtering and standardisation
DMegaritis Jul 4, 2024
1be7fcb
Added comments next to operations.
DMegaritis Jul 5, 2024
068a6cd
Updated comments
DMegaritis Jul 5, 2024
ffc228f
acceleration function returns a pd.DataFrame
DMegaritis Jul 6, 2024
e0bffba
Updated arrays to pd.dataframes so named access can be used
DMegaritis Jul 6, 2024
edc1c75
Corrections on named access
DMegaritis Jul 6, 2024
4f023b7
Updated comments of operations
DMegaritis Jul 8, 2024
a0a4b9c
Updated comments of operations
DMegaritis Jul 8, 2024
33a3af7
Corrected 'n' referring to the number of walking bouts
DMegaritis Jul 8, 2024
5fd8497
Added ValueError and cleaned
DMegaritis Jul 9, 2024
57d9f0e
Added ValueError and cleaned
DMegaritis Jul 9, 2024
49f3d30
Updated init
DMegaritis Jul 9, 2024
ef90ec8
Updated import statements
DMegaritis Jul 9, 2024
cbb8be8
acceleration: IMU shape should be 6 cols and not 3
DMegaritis Jul 11, 2024
82546ec
Updated value errors
DMegaritis Jul 11, 2024
4875f85
Updated value errors
DMegaritis Jul 11, 2024
af2ac53
Rebased to class structure
DMegaritis Jul 11, 2024
a3bd654
Removed old structure
DMegaritis Jul 11, 2024
d361ac0
Name update
DMegaritis Jul 11, 2024
87e1d56
Removed testing of mad
DMegaritis Jul 11, 2024
40e2b7f
Updated init to new structure
DMegaritis Jul 11, 2024
c5caefb
Updated dockstrings
DMegaritis Jul 11, 2024
fd968f8
rebased to main
DMegaritis Jul 22, 2024
530e8bf
Updated imports
DMegaritis Jul 22, 2024
3a534c2
Updated column names to "acc_is", "acc_ml", "acc_pa", "gyr_is", "gyr_…
DMegaritis Jul 24, 2024
f42b26d
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
61755c6
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
05ee6cd
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
e70b415
Merge remote-tracking branch 'origin/main' into Reorientation
DMegaritis Sep 24, 2024
6805a26
GS is called with the whole data rather than with acc alone
DMegaritis Sep 24, 2024
69035ee
Implemented examples with disoriented data
DMegaritis Sep 24, 2024
6d5cb52
Disoriented data
DMegaritis Sep 24, 2024
0b68fb6
First implementation of helper function to rotate acceleration accord…
DMegaritis Mar 13, 2024
0eb45b6
First implementation of filter_signals_100Hz function high and low ba…
DMegaritis Mar 13, 2024
95a4301
utils implemented with gaitmap functions using rotations instead of q…
DMegaritis Mar 14, 2024
f422ad5
Improved implementation of utils
DMegaritis Mar 14, 2024
918e14b
ValueError added
DMegaritis Mar 15, 2024
a83d1f7
First implementation of CorrectSensorOrientationDynamic
DMegaritis Mar 15, 2024
3a49584
Simplified and more robust since data are already sliced before this …
DMegaritis Mar 21, 2024
f725fec
first implementation of CorrectOrientationSensorAxes.py
DMegaritis Mar 21, 2024
1f085e3
Updated dockstrings
DMegaritis Mar 22, 2024
ca45fa8
resolved warnings
DMegaritis Mar 22, 2024
fde0d26
updated filters using the mobgap toolbox
DMegaritis Mar 25, 2024
6024941
Improved selection of non-GS signal
DMegaritis Mar 26, 2024
6728a89
Updated data types
DMegaritis Mar 27, 2024
7cb05d4
Updated quat multiplication in line with MATLAB
DMegaritis Mar 28, 2024
84d0caf
Updated method of butter.
DMegaritis Apr 1, 2024
e50708d
Updated method of butter
DMegaritis Apr 1, 2024
e0c0187
Updated mobgap filters
DMegaritis Apr 2, 2024
097ddc0
Fixed bugs
DMegaritis Apr 2, 2024
cb21703
Added Reorientation after updating to mobgap
DMegaritis Apr 2, 2024
5e41bbb
Added init
DMegaritis Apr 2, 2024
bc78779
Updated renaming
DMegaritis Apr 4, 2024
fd265eb
Madgwick testing on lab data
DMegaritis Apr 5, 2024
1902d3d
Madgwick testing on lab data
DMegaritis Apr 5, 2024
cb00cde
Madgwick application on whole array
DMegaritis Apr 9, 2024
c869780
Applying madwick in the whole array
DMegaritis Apr 10, 2024
ca5d970
Update constant (th) to reflect data in m/s2
DMegaritis Apr 11, 2024
635511a
Signal correction for non GS areas moved out of the main loop to allo…
DMegaritis Apr 18, 2024
3e33eb7
Signal correction for non GS areas moved out of the main loop to allo…
DMegaritis Apr 18, 2024
ffd7646
Updated initial orientation according to Matlab function (UpdateIMUslt2)
DMegaritis Apr 18, 2024
3b968e0
Fixed bugs
DMegaritis Apr 18, 2024
1c88a38
Updated dockstrings
DMegaritis May 6, 2024
649c221
Updated dockstrings and tidied code
DMegaritis Jun 14, 2024
9ed23ae
Named instead of positional access
DMegaritis Jul 3, 2024
10667fd
Named instead of positional access
DMegaritis Jul 3, 2024
c828a3c
Added else clause to handle short data properly
DMegaritis Jul 3, 2024
0d4a293
corIMUdata is not a copy of data.
DMegaritis Jul 3, 2024
5025048
Use of iter_gs
DMegaritis Jul 4, 2024
99327f4
fit_transform replaced manual transformation
DMegaritis Jul 4, 2024
633be63
Vectorised standardisation
DMegaritis Jul 4, 2024
558c35d
Vectorised filtering and standardisation
DMegaritis Jul 4, 2024
7bd9853
Added comments next to operations.
DMegaritis Jul 5, 2024
fd40633
Updated comments
DMegaritis Jul 5, 2024
fd96cb0
acceleration function returns a pd.DataFrame
DMegaritis Jul 6, 2024
60f1c8a
Updated arrays to pd.dataframes so named access can be used
DMegaritis Jul 6, 2024
411a2a6
Corrections on named access
DMegaritis Jul 6, 2024
7abc9ad
Updated comments of operations
DMegaritis Jul 8, 2024
4d6b02b
Updated comments of operations
DMegaritis Jul 8, 2024
736b2fe
Corrected 'n' referring to the number of walking bouts
DMegaritis Jul 8, 2024
49a08cc
Added ValueError and cleaned
DMegaritis Jul 9, 2024
0874453
Added ValueError and cleaned
DMegaritis Jul 9, 2024
4611c8d
Updated init
DMegaritis Jul 9, 2024
92988f0
Updated import statements
DMegaritis Jul 9, 2024
90eedea
acceleration: IMU shape should be 6 cols and not 3
DMegaritis Jul 11, 2024
a08f24b
Updated value errors
DMegaritis Jul 11, 2024
feff62c
Updated value errors
DMegaritis Jul 11, 2024
5338e09
Rebased to class structure
DMegaritis Jul 11, 2024
094eeb2
Removed old structure
DMegaritis Jul 11, 2024
653f580
Name update
DMegaritis Jul 11, 2024
c0ddcf3
Removed testing of mad
DMegaritis Jul 11, 2024
a715e2a
Updated init to new structure
DMegaritis Jul 11, 2024
5aa7576
Updated dockstrings
DMegaritis Jul 11, 2024
145d683
Updated imports
DMegaritis Jul 22, 2024
f163deb
Updated column names to "acc_is", "acc_ml", "acc_pa", "gyr_is", "gyr_…
DMegaritis Jul 24, 2024
a4d0edc
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
d4cacef
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
290c7ed
first implementation of the re-orientation example
DMegaritis Jul 24, 2024
db4c111
GS is called with the whole data rather than with acc alone
DMegaritis Sep 24, 2024
089d102
Implemented examples with disoriented data
DMegaritis Sep 24, 2024
c0169ef
Disoriented data
DMegaritis Sep 24, 2024
4d87de5
Merge remote-tracking branch 'origin/Reorientation' into Reorientation
DMegaritis Nov 15, 2024
3d340fa
test
DMegaritis Nov 15, 2024
34e935e
Merge remote-tracking branch 'origin/main' into Reorientation
DMegaritis Jan 4, 2025
c80b453
Updated acceleration function to return the data with the same column…
DMegaritis Jan 4, 2025
2c371bd
Updated acceleration function to return the data with the same column…
DMegaritis Jan 4, 2025
58c28a1
Updated to use mobgap madgwick function
DMegaritis Jan 4, 2025
0853c41
Updated plot syntax
DMegaritis Jan 4, 2025
6a00aa2
Updated GSD to Ionescu which is orientation agnostic
DMegaritis Jan 6, 2025
9966c5f
Corrected plots
DMegaritis Jan 6, 2025
9ef3735
Corrected wrong orientation
DMegaritis Jan 6, 2025
c5a7a86
Corrected wrong orientation
DMegaritis Jan 6, 2025
b40a141
Corrected wrong orientation
DMegaritis Jan 6, 2025
627e31d
Updated comments
DMegaritis Jan 6, 2025
eb56628
Updated comments
DMegaritis Jan 6, 2025
1250d01
Updated indexing because a single datapoint at the end of each bout w…
DMegaritis Jan 6, 2025
d7ea86f
Updated indexing because a single datapoint at the end of each bout w…
DMegaritis Jan 6, 2025
fa8bbbd
Removed local figure saving
DMegaritis Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/BodyCurvature.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/C90.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/C90IO.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/CC90.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/CC90IO.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/InsideOut.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/Upsidedown.csv

Large diffs are not rendered by default.

13,760 changes: 13,760 additions & 0 deletions example_data/data_csv/data_reorientation/UpsidedownInsideOut.csv

Large diffs are not rendered by default.

417 changes: 417 additions & 0 deletions examples/reorientation/_reorientation.py

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions mobgap/Reorientation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Algorithms to rotate the signal according to the position of the IMU."""

from mobgap.Reorientation import _filteringsignals_100Hz
from mobgap.Reorientation._correct_orientation_sensor_axes import CorrectOrientationSensorAxes
from mobgap.Reorientation._correct_sensor_orientation_dynamic import CorrectSensorOrientationDynamic

__all__ = ["CorrectSensorOrientationDynamic", "CorrectOrientationSensorAxes", "_filteringsignals_100Hz"]
143 changes: 143 additions & 0 deletions mobgap/Reorientation/_correct_orientation_sensor_axes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import pandas as pd
import numpy as np
import scipy.constants
from typing_extensions import Self
from mobgap.pipeline import iter_gs
from mobgap.Reorientation._correct_sensor_orientation_dynamic import CorrectSensorOrientationDynamic
from mobgap.gait_sequences import GsdAdaptiveIonescu, GsdIonescu
from mobgap.Reorientation._filteringsignals_100Hz import filtering_signals_100hz
from mobgap.initial_contacts._hklee_algo_improved import groupfind
from mobgap.data_transform import (
SavgolFilter,
chain_transformers,
)


class CorrectOrientationSensorAxes:
"""

Updates the orientation of the IMU data based on the orientation of the sensor.
Parameters
----------
data
Accelerometer and gyroscope data. The data should be in m/s2 and deg/s.
sampling_rate_hz
Sampling rate of the data in Hz.

Returns
-------
corIMUdata
Corrected IMU data.
corIMUdataSequence
Sequence of the corrected IMU data including start and stop of each walking bout.

Notes
-----
Deviations from the MATLAB implementation:
- th constant was amended to 0.65 * 9.81 to convert it to m/s^2 from g-units
- Within the loop MATLAB calculated the 'start' and 'end' samples by multiplying with fs.
This is not necessary, since 'start' and 'end' are already in samples.
- Simple rotation applied in areas of the signal without a walking bout would only occur if more than
2 walking bouts were present. Now this is applied to the whole signal even if walking bouts are less than 2.
"""


corIMUdata: pd.DataFrame
corIMUdataSequence: pd.DataFrame

def __init__(self, sampling_rate_hz: float):
self.sampling_rate_hz = sampling_rate_hz

def update_orientation(self, data: pd.DataFrame) -> Self:
self.data = data

acc = self.data.loc[:, ["acc_is", "acc_ml", "acc_pa"]]

self.corIMUdata = self.data.copy()
self.corIMUdataSequence = pd.DataFrame(columns=['Start', 'End'])

# Threshold to test alignment of av with gravity.
# In matlab it was 0.65 and used with g-units.
# In the present implementation it is converted to m/s^2
th = 0.65 * scipy.constants.g

# parameter for smoothing filter
n_sgfilt = 9041
accx = acc.loc[:, ["acc_is"]].values

# Condition to support the minimal signal length required for the filter parameter
# low pass filtering of vertical acc
if n_sgfilt < len(accx):
av_filt = filtering_signals_100hz(accx, 'low', 0.1, sampling_rate=self.sampling_rate_hz)

savgol_win_size_samples = n_sgfilt

savgol = SavgolFilter(
window_length_s=savgol_win_size_samples / self.sampling_rate_hz,
polyorder_rel=1 / savgol_win_size_samples,
)

filter_chain = [("savgol", savgol)]

av_filt1 = chain_transformers(av_filt, filter_chain, sampling_rate_hz=self.sampling_rate_hz)

# Output of filter chains should be a pd.DataFrame so the iter_gs function can work
av_filt1 = pd.DataFrame(av_filt1, columns=['acc_is'])

gs = GsdIonescu().detect(data, sampling_rate_hz=self.sampling_rate_hz).gs_list_

# Adding a specific index to gs so the iter_gs function can work
if gs.index.name is None:
gs['gs_id'] = range(len(gs))
else:
if gs.index.name != 'gs_id' and gs.index.name != 'wb_id':
gs['gs_id'] = range(len(gs))

gsLabel = np.zeros(len(acc.loc[:, "acc_is"]))
n = len(gs)
k = 0

if n > 2:
for GS, data_slice in iter_gs(av_filt1, gs):

gsLabel[GS.start:GS.end] = 1

avm = np.mean(data_slice)

if avm >= th:
# TODO: check maybe this is not needed because corIMUdata is allready = data so acc is there
self.corIMUdata.iloc[GS.start:GS.end, 0:3] = acc.iloc[GS.start:GS.end, :]
elif avm <= -th:
self.corIMUdata.iloc[GS.start:GS.end, 0] = -acc.iloc[GS.start:GS.end, 0]
self.corIMUdataSequence.loc[k, ['Start', 'End']] = [GS.start, GS.end]
k = k + 1
else:
self.corIMUdata.iloc[GS.start:GS.end, :] = CorrectSensorOrientationDynamic(
self.data.iloc[GS.start:GS.end, :], self.sampling_rate_hz)
self.corIMUdataSequence.loc[k, ['Start', 'End']] = [GS.start, GS.end]
k = k + 1

ind_noGS = groupfind(gsLabel == 0)

# ind_noGS should be a pd.DataFrame so the iter_gs function can work
ind_noGS = pd.DataFrame(ind_noGS, columns=['start', 'end'])

# Adding a specific index to ind_noGS so the iter_gs function can work
if ind_noGS.index.name is None:
ind_noGS['gs_id'] = range(len(ind_noGS))
else:
if ind_noGS.index.name != 'gs_id' and ind_noGS.index.name != 'wb_id':
ind_noGS['gs_id'] = range(len(ind_noGS))

for GS, data_slice in iter_gs(av_filt1, ind_noGS):
avm = np.mean(data_slice)

if avm >= th:
self.corIMUdata.iloc[GS.start:GS.end, 0:3] = acc.iloc[GS.start:GS.end, :]
elif avm <= -th:
self.corIMUdata.iloc[GS.start:GS.end+1, 0] = -acc.iloc[GS.start:GS.end+1, 0]

else:
return self

return self
184 changes: 184 additions & 0 deletions mobgap/Reorientation/_correct_sensor_orientation_dynamic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import pandas as pd
import numpy as np
from scipy.signal import correlate
from sklearn.decomposition import PCA
from mobgap.orientation_estimation._madgwick import MadgwickAHRS
from mobgap.Reorientation._utils import acceleration
from mobgap.data_transform import (
SavgolFilter,
chain_transformers,
)


def CorrectSensorOrientationDynamic(data: pd.DataFrame, sampling_rate_hz: float) -> pd.DataFrame:

"""
Corrects the orientation of the IMU data based on the orientation of the sensor.

Parameters
----------
data
Accelerometer and gyroscope data. The data should be in m/s2 and deg/s.
sampling_rate_hz
Sampling rate of the data in Hz.

Returns
-------
IMU_corrected
Corrected IMU data.

Notes
-----
Points of deviation from the MATLAB implementation:
- The Madgwick filter is applied to the entire data array (walking bout) at once, as opposed to row by row in the
original implementation.
- The signal is already sliced before calling the present function (CorrectSensorOrientationDynamic).
Thus, this script has been simplified by removing a loop and a variable which were not needed.
- In MATLAB quaternions are represented as xyzw but python requires wxyz. Thus, the quaternions are adjusted.
- Parameters are expressed in the units used in mobgap,specifically, we use m/s^2 instead of g.
"""

acc = data.loc[:, ["acc_is", "acc_ml", "acc_pa"]]
gyr = data.loc[:, ["gyr_is", "gyr_ml", "gyr_pa"]]

# Madwick is called with the initial orientation specified inside the Matlab Madgwick function used in the Matlab
# implementation (UpdateIMUslt2). This choice was made since the first "initial orientation"
# specified in the Matlab implementation is overwritten in the UpdateIMUslt2 function.

mad = MadgwickAHRS(beta=0.1, initial_orientation=np.array([-0.0863212587840360, -0.639984676042049,
0, 0.763523578361070]))

chosenacc = acc.copy()
chosengyr = gyr.copy()

# applying the madgwick algo to the entire data array
data = pd.concat((chosenacc, chosengyr), axis=1)
mad = mad.estimate(data, sampling_rate_hz=sampling_rate_hz)
# Slicing quaternion to make it identical in length to the data
# this removes the first instance which is the initial orientation
quaternion = mad.orientation_.iloc[1:, :]

quaternion = quaternion.to_numpy()

# Adjust quaternion from x, y, z, w to w, x, y, z
quaternion = quaternion[:, [3, 0, 1, 2]]

av = acceleration(data, quaternion)

# Principal component analysis
pca_acc = PCA()
newacc = pca_acc.fit_transform(chosenacc)

# newacc to pd dataframe so named access can be used later
newacc = pd.DataFrame(newacc, columns=['PC1', 'PC2', 'PC3'])

pca_gyr = PCA()
newgyr = pca_gyr.fit_transform(chosengyr)

# newgyr to pd dataframe so named access can be used later
newgyr = pd.DataFrame(newgyr, columns=['PC1', 'PC2', 'PC3'])

if newacc['PC1'].mean() < 0:
newacc['PC1'] = -newacc['PC1']

# av_magpca
av_magpca = av.copy()
av_magpca.loc[:, 'acc_is'] = newacc.loc[:, 'PC1'] # Vertical acceleration is replaced by the first PCA component,
# indicating the higher variance (like the vertical acceleration)

# gyr_magpca
gyr_magpca = chosengyr.reset_index(drop=True).copy() # Resetting index (to remove time) to match the newgyr index
gyr_magpca.loc[:, 'gyr_is'] = newgyr.loc[:, 'PC1'] # Yaw: Vertical gyroscope is replaced by the first PCA component,
# indicating the higher variance (yaw)

# Standardisation vectorised
av_standardized = ((av - av.mean()) / av.std())
newacc_standardized = ((newacc - newacc.mean()) / newacc.std())

sig1 = av_standardized.loc[:, 'acc_pa']
sig2 = newacc_standardized.loc[:, 'PC2']
sig3 = newacc_standardized.loc[:, 'PC3']
sig4 = av_standardized.loc[:, 'acc_ml']

# Calculating dot products to compare directionality and magnitude of agreement between different axes
# The PCA components are ordered by variance explained: the first component has the highest variance,
# followed by the second, and then the third.
cor1 = np.dot(sig1, sig2) # 'agreement' between AP and 2nd PCA component
cor2 = np.dot(sig1, sig3) # 'agreement' between AP and 3rd PCA component
cor3 = np.dot(sig3, sig4) # 'agreement' between 3rd PCA component and ML
cor4 = np.dot(sig2, sig4) # 'agreement' between ML and 2nd PCA component

if abs(cor1) > abs(cor2): # AP and 2nd PCA component are more 'aligned' than AP and 3rd PCA component
if cor1 > 0: # AP and ML have same direction
av_magpca.loc[:, 'acc_pa'] = newacc.loc[:, 'PC2'] # AP is replaced with 2nd component of PCA
gyr_magpca.loc[:, 'gyr_pa'] = newgyr.loc[:, 'PC2']
else: # AP and ML have opposite direction
av_magpca.loc[:, 'acc_pa'] = -newacc.loc[:, 'PC2'] # AP is replaced with the neg of the 2nd PCA component
gyr_magpca.loc[:, 'gyr_pa'] = newgyr.loc[:, 'PC2']

if cor3 > 0: # AP (PCA) and ML have same direction
av_magpca.loc[:, 'acc_ml'] = newacc.loc[:, 'PC3'] # ML is replaced with the 3rd component of PCA
gyr_magpca.loc[:, 'gyr_ml'] = newgyr.loc[:, 'PC3']
else: # AP (PCA) and ML have opposite direction
av_magpca.loc[:, 'acc_ml'] = -newacc.loc[:, 'PC3'] # ML is replaced with the neg of the 3rd PCA component
gyr_magpca.loc[:, 'gyr_ml'] = newgyr.loc[:, 'PC3']
else: # AP and AP (following PCA) are more 'aligned' than AP and ML
if cor2 > 0: # AP and AP (PCA) have same direction
av_magpca.loc[:, 'acc_pa'] = newacc.loc[:, 'PC3'] # AP is replaced with the 3rd component of PCA
gyr_magpca.loc[:, 'gyr_pa'] = newgyr.loc[:, 'PC3']
else: # AP and AP (PCA) have opposite direction
av_magpca.loc[:, 'acc_pa'] = -newacc.loc[:, 'PC3'] # AP is replaced with the neg of the 3rd PCA component
gyr_magpca.loc[:, 'gyr_pa'] = newgyr.loc[:, 'PC3']

if cor4 > 0: # ML and ML (PCA) have same direction
av_magpca.loc[:, 'acc_ml'] = newacc.loc[:, 'PC2'] # ML is replaced with 2nd component of PCA
gyr_magpca.loc[:, 'gyr_ml'] = newgyr.loc[:, 'PC2']
else: # ML and ML (PCA) have opposite direction
av_magpca.loc[:, 'acc_ml'] = -newacc.loc[:, 'PC2'] # ML is replaced with the neg of the 2nd component of PCA
gyr_magpca.loc[:, 'gyr_ml'] = newgyr.loc[:, 'PC2']
av_pca_final = av_magpca.copy()
gyr_pca_final = gyr_magpca.copy()

# Savitzky-Golay filter
savgol_win_size_samples = 11
savgol = SavgolFilter(
window_length_s=savgol_win_size_samples / sampling_rate_hz,
polyorder_rel=0 / savgol_win_size_samples,
)

filter_chain = [("savgol", savgol)]

# Filtering vectorised
av_pca_filt = np.zeros_like(av_magpca)

for i in range(av_magpca.shape[1]):
av_pca_filt[:, i] = chain_transformers(av_magpca.iloc[:, i], filter_chain, sampling_rate_hz=sampling_rate_hz)

# Standardization vectorised
av_pca_filt_standardized = (av_pca_filt - np.mean(av_pca_filt, axis=0)) / np.std(av_pca_filt, axis=0)

sig5 = av_pca_filt_standardized[:, 2]
sig6 = av_pca_filt_standardized[:, 1]
sig7 = av_pca_filt_standardized[:, 0]

# Cross-correlation
r1 = correlate(sig7, sig5) # Cross-correlation indicating lag of signals, this uses the filtered signals
r2 = correlate(sig7, sig6)

if np.max(r1) > np.max(r2): # AP and V correlate more strongly than V and ML. Nothing changes
av_pca_final.loc[:, "acc_pa"] = av_magpca.loc[:, "acc_pa"]
av_pca_final.loc[:, "acc_ml"] = av_magpca.loc[:, "acc_ml"]

gyr_pca_final.loc[:, "gyr_pa"] = gyr_magpca.loc[:, "gyr_pa"]
gyr_pca_final.loc[:, "gyr_ml"] = gyr_magpca.loc[:, "gyr_ml"]

else: # V and ML correlate more strongly than AP and V.
av_pca_final.loc[:, "acc_pa"] = av_magpca.loc[:, "acc_ml"] # AP is replaced with ML
av_pca_final.loc[:, "acc_ml"] = av_magpca.loc[:, "acc_pa"]

gyr_pca_final.loc[:, "gyr_pa"] = gyr_magpca.loc[:, "gyr_ml"]
gyr_pca_final.loc[:, "gyr_ml"] = gyr_magpca.loc[:, "gyr_pa"]

IMU_corrected = pd.concat([av_pca_final, gyr_pca_final], axis=1)

return IMU_corrected
Loading
Loading