3 Commits

3 changed files with 88 additions and 24 deletions
-2
View File
@@ -12,6 +12,4 @@ python-dateutil==2.8.2
pytz==2021.3 pytz==2021.3
scipy==1.7.1 scipy==1.7.1
six==1.16.0 six==1.16.0
numpy~=1.19.3
screeninfo~=0.8.1 screeninfo~=0.8.1
+74 -15
View File
@@ -3,12 +3,10 @@ import time
from datetime import datetime from datetime import datetime
from threading import Thread from threading import Thread
import numpy as np import numpy as np
from numpy.lib.scimath import sqrt as csqrt
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import LabelFrame from tkinter import LabelFrame
import scipy.optimize import scipy.optimize
from scipy import linalg as linalg_scipy
from src.utility import ui_print from src.utility import ui_print
from src.exceptions import DeviceBusy, DeviceAccessError from src.exceptions import DeviceBusy, DeviceAccessError
@@ -436,8 +434,9 @@ class MagnetometerCalibrationComplete(Thread):
def run(self): def run(self):
try: try:
self.calibration_procedure() raw_data = self.calibration_procedure()
self.put_message('finished', None) self.put_message('finished', None)
return raw_data
except Exception as e: except Exception as e:
self.put_message('failed', e) self.put_message('failed', e)
finally: finally:
@@ -498,14 +497,7 @@ class MagnetometerCalibrationComplete(Thread):
# Put device into an off and ready state # Put device into an off and ready state
self.cage_dev.idle() self.cage_dev.idle()
return raw_data
# Use collected data to build and solve system of equations
# sensor_parameters = self.solve_system(raw_data) # FLAG: untested!
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(
raw_data, self.matrix_trans_mgm_to_hh)
# Pass results to UI
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
def set_progress(self, offset_complete, test_vec_index): def set_progress(self, offset_complete, test_vec_index):
progress = int(offset_complete) * 0.2 + (test_vec_index / self.calibration_points) * 0.8 progress = int(offset_complete) * 0.2 + (test_vec_index / self.calibration_points) * 0.8
@@ -582,7 +574,6 @@ class MagnetometerCalibrationComplete(Thread):
# Retrieve calibration parameters # Retrieve calibration parameters
q_mat_inv = np.linalg.inv(q_mat) q_mat_inv = np.linalg.inv(q_mat)
b = -np.dot(q_mat_inv, n) b = -np.dot(q_mat_inv, n)
#a_mat_inv = np.real(1 / csqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * linalg_scipy.sqrtm(q_mat))
a_mat_inv = np.real(mag_amp_avg_set / np.sqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * scipy.linalg.sqrtm(q_mat)) a_mat_inv = np.real(mag_amp_avg_set / np.sqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * scipy.linalg.sqrtm(q_mat))
a_mat = np.linalg.inv(a_mat_inv) a_mat = np.linalg.inv(a_mat_inv)
# Calculate error # Calculate error
@@ -825,10 +816,78 @@ class MagnetometerCalibrationComplete(Thread):
return m, n, d return m, n, d
def fit_ellipsoid(mag_x_m, mag_y_m, mag_z_m):
""" Estimate ellipsoid parameters from a set of points.
Parameters
----------
mag_x_m, mag_y_m, mag_z_m : array_like, array_like, array_like
The samples (M,N) where M=3 (x,y,z) and N=number of samples.
Returns
-------
s : array_like
The samples (M,N) where M=3 (x,y,z) and N=number of samples.
Returns
-------
M, n, d : array_like, array_like, float
The ellipsoid parameters M, n, d.
References
----------
.. [1] Qingde Li; Griffiths, J.G., "Least squares ellipsoid specific
fitting," in Geometric Modeling and Processing, 2004.
Proceedings, vol., no., pp.335-340, 2004
.. https://github.com/nliaudat/magnetometer_calibration/blob/main/calibrate.py
"""
# Converts to samples (M,N) where M=3 (x,y,z) and N=number of samples.
s = np.array([mag_x_m, mag_y_m, mag_z_m])
# d (samples)
d = np.array([s[0] ** 2., s[1] ** 2., s[2] ** 2.,
2. * s[1] * s[2], 2. * s[0] * s[2], 2. * s[0] * s[1],
2. * s[0], 2. * s[1], 2. * s[2], np.ones_like(s[0])])
# s, s_11, s_12, s_21, s_22 (eq. 11)
s = np.dot(d, d.T)
s_11 = s[:6, :6]
s_12 = s[:6, 6:]
s_21 = s[6:, :6]
s_22 = s[6:, 6:]
# c (Eq. 8, k=4)
c = np.array([[-1, 1, 1, 0, 0, 0],
[1, -1, 1, 0, 0, 0],
[1, 1, -1, 0, 0, 0],
[0, 0, 0, -4, 0, 0],
[0, 0, 0, 0, -4, 0],
[0, 0, 0, 0, 0, -4]])
# v_1 (eq. 15, solution)
e = np.dot(np.linalg.inv(c),
s_11 - np.dot(s_12, np.dot(np.linalg.inv(s_22), s_21)))
e_w, e_v = np.linalg.eig(e)
v_1 = e_v[:, np.argmax(e_w)]
if v_1[0] < 0:
v_1 = -v_1
# v_2 (eq. 13, solution)
v_2 = np.dot(np.dot(-np.linalg.inv(s_22), s_21), v_1)
# Quadratic-form parameters
m = np.array([[v_1[0], v_1[3], v_1[4]],
[v_1[3], v_1[1], v_1[5]],
[v_1[4], v_1[5], v_1[2]]])
n = np.array([[v_2[0]],
[v_2[1]],
[v_2[2]]])
d = v_2[3]
return m, n, d
@staticmethod @staticmethod
def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m,
cal_y, cal_x, cal_y, cal_z, mag_amp_avg_set):
cal_z, mag_amp_avg_set):
plot_fontsize = 5 plot_fontsize = 5
ax_width = 0.2 ax_width = 0.2
+14 -7
View File
@@ -1694,7 +1694,7 @@ class CalibrateMagnetometerComplete(Frame):
state='readonly') state='readonly')
axis_data.grid(row=row_counter + row, column=1 + column, padx=5, pady=5, sticky="nw") axis_data.grid(row=row_counter + row, column=1 + column, padx=5, pady=5, sticky="nw")
results_label_unit = Label(calibration_results_frame, text="-") results_label_unit = Label(calibration_results_frame, text="-")
results_label_unit.grid(row=row_counter + row, column=1 + column+1, padx=5, pady=5, sticky="nw") results_label_unit.grid(row=row_counter + row, column=1 + 3, padx=5, pady=5, sticky="nw")
row_counter += 3 row_counter += 3
""" """
# A_mat # A_mat
@@ -1719,7 +1719,7 @@ class CalibrateMagnetometerComplete(Frame):
state='readonly') state='readonly')
axis_data.grid(row=row_counter, column=1 + row, padx=5, pady=5, sticky="nw") axis_data.grid(row=row_counter, column=1 + row, padx=5, pady=5, sticky="nw")
results_label_unit = Label(calibration_results_frame, text="T") results_label_unit = Label(calibration_results_frame, text="T")
results_label_unit.grid(row=row_counter, column=1 + row+1, padx=5, pady=5, sticky="nw") results_label_unit.grid(row=row_counter, column=1 + row + 1, padx=5, pady=5, sticky="nw")
row_counter += 1 row_counter += 1
# Save calibration buttons # Save calibration buttons
@@ -1890,6 +1890,14 @@ class CalibrateMagnetometerComplete(Frame):
self.right_column) self.right_column)
self.calibration_thread.start() self.calibration_thread.start()
self.deactivate_buttons() self.deactivate_buttons()
# Use collected data to build and solve system of equations
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(raw_data, [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
# Pass results to UI
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column,
mag_x_set, mag_y_set, mag_z_set,
mag_x_m, mag_y_m, mag_z_m,
cal_x, cal_y, cal_z, mag_amp_avg_set)
except MagFieldOutOfBounds as e: except MagFieldOutOfBounds as e:
messagebox.showwarning("Calibration failed", "\n{}".format(e)) messagebox.showwarning("Calibration failed", "\n{}".format(e))
except (DeviceAccessError, TclError) as e: except (DeviceAccessError, TclError) as e:
@@ -1921,11 +1929,10 @@ class CalibrateMagnetometerComplete(Frame):
# Execute calibration function and display results # Execute calibration function and display results
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system( sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(
raw_data, mgm_to_helmholtz_cos_trans) raw_data, mgm_to_helmholtz_cos_trans)
MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column,
mag_z_set, mag_x_set, mag_y_set, mag_z_set,
mag_x_m, mag_x_m, mag_y_m, mag_z_m,
mag_y_m, mag_z_m, cal_x, cal_y, cal_x, cal_y, cal_z, mag_amp_avg_set)
cal_z, mag_amp_avg_set)
# Pass results to UI # Pass results to UI
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data}) self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
except TypeError: except TypeError: