forked from zietzm/Helmholtz_Test_Bench
Added helmholtz cage angle analysis.
This commit is contained in:
+27
-4
@@ -1,4 +1,4 @@
|
||||
import math
|
||||
from math import pi, sqrt
|
||||
import time
|
||||
from datetime import datetime
|
||||
from threading import Thread
|
||||
@@ -139,11 +139,19 @@ class CoilConstantCalibration(Thread):
|
||||
def calibration_procedure(self):
|
||||
# All generated fields will be compared to this using a simple difference method
|
||||
ambient_field = g.MAGNETOMETER.field
|
||||
# The coil constant must be determined for every axis
|
||||
|
||||
# This generates linearly spaced current setpoints and excludes zero
|
||||
currents = np.linspace(-self.MEASUREMENT_RANGE, self.MEASUREMENT_RANGE, self.MEASUREMENT_POINTS * 2 + 1)
|
||||
currents = np.delete(currents, self.MEASUREMENT_POINTS)
|
||||
|
||||
# Stores three vectors that correspond to the x,y,z actuation field directions.
|
||||
axis_field_directions = []
|
||||
|
||||
# Result variables
|
||||
coil_constants = np.zeros(3)
|
||||
k_deviations = np.zeros(3)
|
||||
|
||||
# The coil constant must be determined for every axis
|
||||
for i in range(3):
|
||||
k_samples = []
|
||||
for c_idx, c in enumerate(currents):
|
||||
@@ -160,6 +168,10 @@ class CoilConstantCalibration(Thread):
|
||||
field_diff_mag = np.linalg.norm(g.MAGNETOMETER.field - ambient_field)
|
||||
k_samples.append(field_diff_mag / c)
|
||||
|
||||
# Save vector as principal coil direction if it is the last sample with the largest positive current
|
||||
if c_idx == currents.shape[0] - 1:
|
||||
axis_field_directions.append(g.MAGNETOMETER.field - ambient_field)
|
||||
|
||||
# Average samples for axis
|
||||
coil_constants[i] = np.average(k_samples)
|
||||
k_deviations[i], _ = self.calculate_standard_deviation(k_samples)
|
||||
@@ -167,8 +179,12 @@ class CoilConstantCalibration(Thread):
|
||||
# Put device into an off and ready state
|
||||
self.cage_dev.idle()
|
||||
|
||||
angles = {'xy': self.angle_between(axis_field_directions[0], axis_field_directions[1]) * 180 / pi,
|
||||
'yz': self.angle_between(axis_field_directions[1], axis_field_directions[2]) * 180 / pi,
|
||||
'xz': self.angle_between(axis_field_directions[0], axis_field_directions[2]) * 180 / pi}
|
||||
self.put_message('coil_constant_results', {'k': coil_constants,
|
||||
'k_dev': k_deviations})
|
||||
'k_dev': k_deviations,
|
||||
'angle': angles})
|
||||
|
||||
@staticmethod
|
||||
def calculate_standard_deviation(data):
|
||||
@@ -181,8 +197,15 @@ class CoilConstantCalibration(Thread):
|
||||
for datapoint in data:
|
||||
std_dev += (datapoint - average) ** 2
|
||||
deviations.append(datapoint - average)
|
||||
std_dev = math.sqrt(std_dev / n)
|
||||
std_dev = sqrt(std_dev / n)
|
||||
return std_dev, deviations
|
||||
|
||||
@staticmethod
|
||||
def angle_between(v1, v2):
|
||||
""" Returns the angle in radians between vectors 'v1' and 'v2'"""
|
||||
v1_u = v1 / np.linalg.norm(v1)
|
||||
v2_u = v2 / np.linalg.norm(v2)
|
||||
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
|
||||
|
||||
def put_message(self, command, arg):
|
||||
self.view_queue.put({'cmd': command, 'arg': arg})
|
||||
+22
-2
@@ -554,6 +554,7 @@ class CalibrateAmbientField(Frame):
|
||||
# Contains results for coil constant calibration
|
||||
self.coil_constant_vars = [StringVar(), StringVar(), StringVar()]
|
||||
self.coil_constant_dev_vars = [StringVar(), StringVar(), StringVar()]
|
||||
self.coil_angle_vars = [StringVar(), StringVar(), StringVar()]
|
||||
|
||||
row_counter = 0
|
||||
|
||||
@@ -682,6 +683,21 @@ class CalibrateAmbientField(Frame):
|
||||
axis_data.grid(row=2, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
coil_constants_dev_results_unit = Label(coil_constants_results_frame, text="\u03BCT/A")
|
||||
coil_constants_dev_results_unit.grid(row=2, column=4, padx=5, pady=5, sticky="nw")
|
||||
# Inter-axis angle labels
|
||||
for i, label in enumerate(['X-Y', 'Y-Z', 'X-Z']):
|
||||
axis_label = Label(coil_constants_results_frame, text=label)
|
||||
axis_label.grid(row=3, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
# Inter-axis angles
|
||||
coil_angles_label = Label(coil_constants_results_frame, text="Angles:")
|
||||
coil_angles_label.grid(row=4, column=0, padx=5, pady=5, sticky="nw")
|
||||
for i in range(3):
|
||||
axis_data = Entry(coil_constants_results_frame,
|
||||
textvariable=self.coil_angle_vars[i],
|
||||
width=15,
|
||||
state='readonly')
|
||||
axis_data.grid(row=4, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
coil_angles_unit = Label(coil_constants_results_frame, text="°")
|
||||
coil_angles_unit.grid(row=4, column=4, padx=5, pady=5, sticky="nw")
|
||||
|
||||
# This starts an endless polling loop
|
||||
self.update_view()
|
||||
@@ -749,6 +765,10 @@ class CalibrateAmbientField(Frame):
|
||||
# Remember to convert from T/A to microT/A
|
||||
self.coil_constant_vars[i].set("{:.3f}".format(results['k'][i] * 1e6))
|
||||
self.coil_constant_dev_vars[i].set("{:.3f}".format(results['k_dev'][i] * 1e6))
|
||||
# Coil angles use dict access
|
||||
self.coil_angle_vars[0].set("{:.2f}".format(results['angle']['xy']))
|
||||
self.coil_angle_vars[1].set("{:.2f}".format(results['angle']['yz']))
|
||||
self.coil_angle_vars[2].set("{:.2f}".format(results['angle']['xz']))
|
||||
|
||||
def calibration_procedure_ambient(self):
|
||||
try:
|
||||
@@ -757,7 +777,7 @@ class CalibrateAmbientField(Frame):
|
||||
self.start_ambient_calibration_button.configure(text="Running")
|
||||
self.deactivate_buttons()
|
||||
except DeviceAccessError as e:
|
||||
print("Error starting calibration procedure: {}".format(e))
|
||||
messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e))
|
||||
|
||||
def calibration_procedure_coil_constants(self):
|
||||
try:
|
||||
@@ -766,7 +786,7 @@ class CalibrateAmbientField(Frame):
|
||||
self.start_k_calibration_button.configure(text="Running")
|
||||
self.deactivate_buttons()
|
||||
except DeviceAccessError as e:
|
||||
print("Error starting calibration procedure: {}".format(e))
|
||||
messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e))
|
||||
|
||||
|
||||
class HardwareConfiguration(Frame):
|
||||
|
||||
Reference in New Issue
Block a user