116 def __init__(self, sensor_type: str = "accel", port: int = DEFAULT_PORT):
118 self.sensor_type = sensor_type
122 self.calib_system = IMUCalibrationSystem()
123 if sensor_type == "accel":
124 self.calibrator = self.calib_system.create_calibrator(
125 SensorType.ACCELEROMETER, "imu_0"
127 elif sensor_type == "gyro":
128 self.calibrator = self.calib_system.create_calibrator(
129 SensorType.GYROSCOPE, "imu_0"
132 self.calibrator = self.calib_system.create_calibrator(
133 SensorType.MAGNETOMETER, "imu_0"
137 self.raw_readings: Dict[str, deque] = {
138 "x": deque(maxlen=MAX_POINTS),
139 "y": deque(maxlen=MAX_POINTS),
140 "z": deque(maxlen=MAX_POINTS),
142 self.timestamps = deque(maxlen=MAX_POINTS)
143 self.calibration_points: List[CalibrationPoint] = []
149 self.receiver = UDPReceiver(port)
150 self.receiver.data_received.connect(self.on_data_received)
151 self.receiver.start()
154 self.update_timer = QtCore.QTimer()
155 self.update_timer.timeout.connect(self.update_plots)
156 self.update_timer.start(UPDATE_INTERVAL_MS)
159 """Setup user interface"""
160 self.setWindowTitle(f"IMU Calibration - {self.sensor_type.upper()}")
161 self.setGeometry(100, 100, 1200, 800)
164 central = QtWidgets.QWidget()
165 self.setCentralWidget(central)
166 layout = QtWidgets.QVBoxLayout(central)
169 self.plot_widget = pg.GraphicsLayoutWidget()
170 layout.addWidget(self.plot_widget)
172 # Create plots for X, Y, Z
174 for axis in ["x", "y", "z"]:
175 plot = self.plot_widget.addPlot(title=f"{axis.upper()} Axis")
176 plot.setLabel("left", "Value")
177 plot.setLabel("bottom", "Time (s)")
178 curve = plot.plot(pen=pg.mkPen(color=axis, width=2))
179 self.plots[axis] = {"plot": plot, "curve": curve}
182 control_panel = QtWidgets.QHBoxLayout()
184 # Add calibration point button
185 self.add_point_btn = QtWidgets.QPushButton("Add Calibration Point")
186 self.add_point_btn.clicked.connect(self.add_calibration_point)
187 control_panel.addWidget(self.add_point_btn)
190 self.calibrate_btn = QtWidgets.QPushButton("Calibrate")
191 self.calibrate_btn.clicked.connect(self.perform_calibration)
192 control_panel.addWidget(self.calibrate_btn)
195 self.save_btn = QtWidgets.QPushButton("Save Calibration")
196 self.save_btn.clicked.connect(self.save_calibration)
197 control_panel.addWidget(self.save_btn)
200 self.status_label = QtWidgets.QLabel("Ready")
201 control_panel.addWidget(self.status_label)
203 layout.addLayout(control_panel)
205 def on_data_received(self, reading: IMUReading):
206 """Handle received sensor data"""
207 if self.sensor_type == "accel":
208 values = reading.accel
209 elif self.sensor_type == "gyro":
210 values = reading.gyro
214 self.raw_readings["x"].append(values[0])
215 self.raw_readings["y"].append(values[1])
216 self.raw_readings["z"].append(values[2])
217 self.timestamps.append(reading.timestamp)
219 def update_plots(self):
220 """Update plots with latest data"""
221 if not self.timestamps:
224 times = np.array(self.timestamps)
226 times = times - times[0] # Normalize to start at 0
228 for axis in ["x", "y", "z"]:
229 if len(self.raw_readings[axis]) > 0:
230 values = np.array(self.raw_readings[axis])
231 self.plots[axis]["curve"].setData(times, values)
233 def add_calibration_point(self):
234 """Add current reading as calibration point"""
235 if not self.raw_readings["x"]:
236 self.status_label.setText("No data available")
239 # Get current reading
242 self.raw_readings["x"][-1],
243 self.raw_readings["y"][-1],
244 self.raw_readings["z"][-1],
248 # Get reference value from user
249 ref, ok = self.get_reference_value()
253 # Create calibration point
254 point = CalibrationPoint(
257 temperature=25.0, # Would get from sensor
258 timestamp=time.time(),
261 self.calibrator.add_calibration_point(point)
262 self.calibration_points.append(point)
264 self.status_label.setText(
265 f"Added calibration point {len(self.calibration_points)}"
268 def get_reference_value(self) -> tuple:
269 """Get reference value from user input"""
270 dialog = QtWidgets.QDialog(self)
271 dialog.setWindowTitle("Enter Reference Value")
272 layout = QtWidgets.QVBoxLayout(dialog)
274 # Input fields for X, Y, Z
276 for axis in ["X", "Y", "Z"]:
277 label = QtWidgets.QLabel(f"{axis}:")
278 input_field = QtWidgets.QLineEdit()
279 inputs[axis.lower()] = input_field
280 row = QtWidgets.QHBoxLayout()
282 row.addWidget(input_field)
283 layout.addLayout(row)
286 buttons = QtWidgets.QDialogButtonBox(
287 QtWidgets.QDialogButtonBox.StandardButton.Ok
288 | QtWidgets.QDialogButtonBox.StandardButton.Cancel
290 buttons.accepted.connect(dialog.accept)
291 buttons.rejected.connect(dialog.reject)
292 layout.addWidget(buttons)
294 if dialog.exec() == QtWidgets.QDialog.DialogCode.Accepted:
298 float(inputs["x"].text()),
299 float(inputs["y"].text()),
300 float(inputs["z"].text()),
305 return np.zeros(3), False
307 return np.zeros(3), False
309 def perform_calibration(self):
310 """Perform calibration"""
312 params = self.calibrator.calibrate()
313 self.status_label.setText(
314 f"Calibration complete! Quality: {params.calibration_quality:.3f}"
316 except Exception as e:
317 self.status_label.setText(f"Calibration failed: {e}")
319 def save_calibration(self):
320 """Save calibration to file"""
321 filename, _ = QtWidgets.QFileDialog.getSaveFileName(
322 self, "Save Calibration", "", "JSON Files (*.json)"
325 self.calibrator.save_calibration(filename)
326 self.status_label.setText(f"Saved to {filename}")