Sensor Fusion Library 0.6.1
Orientation sensing for Espressif (ESP32, ESP8266) processors
Loading...
Searching...
No Matches
sensor_fusion_class.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020-2021, Bjarne Hansen
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
26#include "sensor_fusion_class.h"
27
28#include <Stream.h>
29#include <stdint.h>
30
35
36const float kDegToRads = PI / 180.0;
37const float kCelsiusToKelvin = 273.15;
38const float kGeesToMPerSS = 9.80665;
39
50 [MAX_NUM_SENSORS]; // this implementation uses up to 4 sensors
51 // (accel/mag; gyro; baro/thermo)
52 // TODO - can use std::vector as per
53 // https://stackoverflow.com/questions/14114686/create-an-array-of-structure-using-new
54 // which will allow adding arbitrary # of sensors.
55
59
60} // end SensorFusion()
61
75bool SensorFusion::InstallSensor(uint8_t sensor_i2c_addr,
76 SensorType sensor_type) {
77
78 if( num_sensors_installed_ >= MAX_NUM_SENSORS ) {
79 //already have max number of sensors installed
80 return false;
81 }
82 switch (sensor_type) {
83 case SensorType::kAccelerometer:
85 sensor_i2c_addr, kLoopsPerAccelRead, NULL,
86 FXOS8700_Accel_Init, FXOS8700_Accel_Read);
88 break;
89 case SensorType::kMagnetometer:
91 sensor_i2c_addr, kLoopsPerMagRead, NULL,
92 FXOS8700_Mag_Init, FXOS8700_Mag_Read);
94 break;
95 case SensorType::kMagnetometerAccelerometer:
97 sensor_i2c_addr, kLoopsPerAccelRead, NULL,
98 FXOS8700_Init, FXOS8700_Read);
100 break;
101 case SensorType::kGyroscope:
103 sensor_i2c_addr, kLoopsPerGyroRead, NULL,
104 FXAS21002_Init, FXAS21002_Read);
106 break;
107 case SensorType::kThermometer:
108 // use the thermometer built into FXOS8700. Not precise nor calibrated,
109 // but OK.
111 sensor_i2c_addr, kLoopsPerThermRead, NULL,
112 FXOS8700_Therm_Init, FXOS8700_Therm_Read);
114 break;
115 case SensorType::kBarometer:
116 // TODO define some access functions for this
117 // TODO add barometer sensor
118 break;
119 default:
120 // unrecognized sensor type
121 break;
122 }
123 return true;
124} // end InstallSensor()
125
135bool SensorFusion::InitializeInputOutputSubsystem(const Stream *serial_port,
136 const void *tcp_client) {
137 return initializeIOSubsystem(control_subsystem_, serial_port, tcp_client);
138} // end InitializeInputOutputSubsystem()
139
143void SensorFusion::Begin(int pin_i2c_sda, int pin_i2c_scl) {
145 sfg_, pin_i2c_sda, pin_i2c_scl); // Initialize sensors and magnetic calibration
146 //if nothing connected to I2C, won't return.
147 sfg_->setStatus(sfg_, NORMAL); // Set status state to NORMAL
148 //try reading sensors, this will update the status to see if initialize worked.
150
151} // end InitializeFusionEngine()
152
159void SensorFusion::UpdateWiFiStream(void *tcp_client) {
160 UpdateTCPClient(control_subsystem_, tcp_client);
161
162} // end UpdateTCPClient()
163
173 sfg_,
174 loops_per_fuse_counter_); // Reads sensors, applies HAL, removes -32768
175
176} // end ReadSensors()
177
186 // applies fusion algorithm to data accumulated in buffers
187
188 // only run fusion every kLoopsPerFusionCalc'th time through loop
191 return;
192 }
193
194 sfg_->conditionSensorReadings(sfg_); // Pre-processing; Magnetic calibration.
195 sfg_->runFusion(sfg_); // Run fusion algorithms
196
197 // Serial.printf(" Algo took %ld us\n", sfg.SV_9DOF_GBY_KALMAN.systick);
198 sfg_->loopcounter++; // loop counter is used to "serialize" mag cal
199 // operations and blink LEDs to indicate status
200 // LED Blinking is too fast unless status updates are slowed down.
201 // Cycle at least four times per status update.
202 // TODO - a better way might be to use a timer.
203 if (0 == sfg_->loopcounter % 4) {
204 sfg_->updateStatus(sfg_); // make pending status updates visible
205 }
206
207 // assume NORMAL status next pass through the loop
208 // this resets temporary error conditions (SOFT_FAULT)
210
211 loops_per_fuse_counter_ = 1; // reset loop counter
212
213} // end RunFusion()
214
220 // Make & send data to Sensor Fusion Toolbox or whatever UART is
221 // connected to.
222 if (loops_per_fuse_counter_ == 1) { // only run if fusion has happened
223 sfg_->pControlSubsystem->stream(sfg_); // create output packet
224 sfg_->pControlSubsystem->write(sfg_); // send output packet
225 }
226
227} // end ProduceToolboxOutput()
228
236bool SensorFusion::SendArbitraryData(const char *buffer, uint16_t data_length) {
237 if (data_length > MAX_LEN_SERIAL_OUTPUT_BUF) {
238 return false;
239 }
240 char *out_buf = (char *)(sfg_->pControlSubsystem->serial_out_buf);
241 for (uint16_t i = 0; i < data_length; i++) {
242 out_buf[i] = buffer[i];
243 }
244 sfg_->pControlSubsystem->bytes_to_send = data_length;
245 sfg_->pControlSubsystem->write(sfg_); // send output packet
246 return true;
247} // end SendArbitraryData()
248
256 // process any incoming commands
257 sfg_->pControlSubsystem->readCommands(sfg_);
258} // end ProcessCommands()
259
273void SensorFusion::InjectCommand(const char *command) {
274 sfg_->pControlSubsystem->injectCommand(sfg_, (uint8_t *)command, 4);
275} // end InjectCommand()
276
295 InjectCommand("SVMC");
296} // end SaveMagneticCalibration()
297
302 if( NORMAL == sfg_->pStatusSubsystem->status ) {
303 return true;
304 } else {
305 return false;
306 }
307} // end IsDataValid()
308
318 return (int)sfg_->pStatusSubsystem->status;
319} // end GetSystemStatus()
320
321// The following Get____() methods return orientation values
322// calculated by the 9DOF Kalman algorithm (the most advanced).
323// They have been mapped to match the conventions used for
324// vessels:
325// Compass Heading; 0 at magnetic north, and increasing CW.
326// Pitch; 0 with boat level, increasing with Bow Up.
327// Roll; 0 with boat level, increasing with Starboard roll.
328// Turn Rate; positive with turn to Starboard
329// Pitch Rate; positive with Bow moving Up.
330// Roll Rate; positive with increasing Starboard heel.
331// Acceleration; X to Bow, Y to Port, Z Up.
332// This works with the Adafruit NXP FXOS8700/FXAS21002
333// breakout board, mounted with X toward the bow, Y to port,
334// and Z (component side of PCB) facing up.
335// If the sensor orienatation is different than assumed,
336// you may need to remap the axes below. If a different
337// sensor board is used, you may need also to change the
338// axes mapping in the file hal_axis_remap.c The latter
339// mapping is applied *before* the fusion algorithm,
340// whereas the below mapping is applied *after*.
341
346 // TODO - make generic so it's not dependent on algorithm used
347 return (sfg_->SV_9DOF_GBY_KALMAN.fRhoPl <= 90)
348 ? (sfg_->SV_9DOF_GBY_KALMAN.fRhoPl + 270.0)
349 : (sfg_->SV_9DOF_GBY_KALMAN.fRhoPl - 90.0);
350} // end GetHeadingDegrees()
351
356 return GetHeadingDegrees() * kDegToRads;
357} // end GetHeadingRadians()
358
363 return sfg_->SV_9DOF_GBY_KALMAN.fPhiPl;
364} // end GetPitchDegrees()
365
370 return GetPitchDegrees() * kDegToRads;
371} // end GetPitchRadians()
372
377 return -(sfg_->SV_9DOF_GBY_KALMAN.fThePl);
378} // end GetRollDegrees()
379
384 return GetRollDegrees() * kDegToRads;
385} // end GetRollRadians()
386
395 return sfg_->Temp.temperatureC;
396} // end GetTemperatureC()
397
407} // end GetTemperatureK()
408
413 return sfg_->SV_9DOF_GBY_KALMAN.fOmega[2];
414} // end GetTurnRateDegPerS()
415
421} // end GetTurnRateRadPerS()
422
427 return sfg_->SV_9DOF_GBY_KALMAN.fOmega[0];
428} // end GetPitchRateDegPerS()
429
435} // end GetPitchRateRadPerS()
436
441 return -(sfg_->SV_9DOF_GBY_KALMAN.fOmega[1]);
442} // end GetRollRateDegPerS()
443
449} // end GetRollRateRadPerS()
450
455 return sfg_->Accel.fGc[1];
456} // end GetAccelXGees()
457
462 return GetAccelXGees() * kGeesToMPerSS;
463} // end GetAccelXMPerSS()
464
469 return sfg_->Accel.fGc[0];
470} // end GetAccelYGees()
471
476 return GetAccelYGees() * kGeesToMPerSS;
477} // end GetAccelYMPerSS()
478
483 return sfg_->Accel.fGc[2];
484} // end GetAccelZGees()
485
490 return GetAccelZGees() * kGeesToMPerSS;
491} // end GetAccelZMPerSS()
492
498 quat->q0 = sfg_->SV_9DOF_GBY_KALMAN.fqPl.q0;
499 quat->q1 = sfg_->SV_9DOF_GBY_KALMAN.fqPl.q1;
500 quat->q2 = sfg_->SV_9DOF_GBY_KALMAN.fqPl.q2;
501 quat->q3 = sfg_->SV_9DOF_GBY_KALMAN.fqPl.q3;
502} // end GetOrientationQuaternion()
503
516 return sfg_->MagCal.ftrFitErrorpc;
517} // end GetMagneticFitErrorTrial()
518
530 return sfg_->MagCal.fFitErrorpc;
531} // end GetMagneticFitError()
532
548 return sfg_->MagCal.ftrB;
549} // end GetMagneticBMagTrial()
550
560 return sfg_->MagCal.fB;
561} // end GetMagneticBMag()
562
577 return sfg_->SV_9DOF_GBY_KALMAN.fDeltaPl;
578} // end GetMagneticInclinationDeg()
579
595} // end GetMagneticInclinationRad()
596
608 return sfg_->SV_9DOF_GBY_KALMAN.fQv6x1[3];
609} // end GetMagneticNoiseCovariance()
610
618 return (float)(sfg_->MagCal.iValidMagCal);
619} // end GetMagneticCalSolver()
620
621//================= end of Get____() methods ==================
622//================= start of private methods ==================
623
630 status_subsystem_); // configure the status sub-system
631
632} // end InitializeStatusSubsystem()
633
639
640} // end InitializeSensorFusionGlobals()
ControlSubsystem * control_subsystem_
command and data streaming structure
float GetRollRadians(void)
float GetMagneticBMagTrial(void)
float GetPitchRadians(void)
float GetMagneticCalSolver(void)
uint8_t num_sensors_installed_
tracks how many sensors have been added to list
const uint8_t kLoopsPerAccelRead
how often an accelerometer read is performed
float GetRollDegrees(void)
void SaveMagneticCalibration(void)
Save current magnetic calibration to non-volatile memory.
bool InstallSensor(uint8_t sensor_i2c_addr, SensorType sensor_type)
Install Sensor in linked list The max length of the list is checked, and if there is room,...
uint8_t loops_per_fuse_counter_
counts how many times through loop have been done
float GetAccelXGees(void)
float GetTurnRateRadPerS(void)
const uint8_t kLoopsPerThermRead
how often a thermometer read is performed
float GetPitchDegrees(void)
float GetMagneticInclinationRad(void)
float GetRollRateRadPerS(void)
float GetMagneticInclinationDeg(void)
float GetHeadingRadians(void)
void UpdateWiFiStream(void *tcp_client)
Update the TCP client pointer. Call when a new TCP connection is made, as reported by WiFiServer::ava...
SensorFusionGlobals * sfg_
Primary sensor fusion data structure.
const uint8_t kLoopsPerMagRead
how often a magnetometer read is performed
float GetAccelYGees(void)
float GetPitchRateDegPerS(void)
void InjectCommand(const char *command)
Inject and process a single command in the control subsystem.
float GetPitchRateRadPerS(void)
float GetAccelYMPerSS(void)
void ProduceToolboxOutput(void)
Generate and send out data, formatted for NXP Orientation Sensor Toolbox. It is not mandatory to call...
PhysicalSensor * sensors_
linked list of up to 4 sensors
float GetTurnRateDegPerS(void)
float GetAccelXMPerSS(void)
float GetMagneticNoiseCovariance(void)
float GetMagneticFitErrorTrial(void)
void ReadSensors(void)
Reads all sensors. Applies HAL remapping, removes invalid values, and stores data for later processin...
void Begin(int pin_i2c_sda=-1, int pin_i2c_scl=-1)
const uint8_t kLoopsPerGyroRead
how often a gyroscope read is performed
float GetTemperatureC(void)
bool SendArbitraryData(const char *buffer, uint16_t data_length)
float GetMagneticFitError(void)
void InitializeStatusSubsystem(void)
Initialize the Status reporting system. Status is indicated by LEDs, but other means can be added.
bool InitializeInputOutputSubsystem(const Stream *serial_port=NULL, const void *tcp_client=NULL)
void RunFusion(void)
Apply fusion algorithm to sensor raw data. Sensor readings contained in global struct are calibrated ...
float GetMagneticBMag(void)
float GetTemperatureK(void)
float GetAccelZMPerSS(void)
void GetOrientationQuaternion(Quaternion *quat)
Return the orientation as a quaternion.
const uint8_t kLoopsPerFusionCalc
how often to fuse. Usually the max of previous 3 constants.
float GetRollRateDegPerS(void)
void InitializeSensorFusionGlobals(void)
Set the starting values of variables contained in sfg_.
float GetAccelZGees(void)
void ProcessCommands(void)
Process any incoming commands. Commands may arrive by serial or WiFi connection, depending on which o...
float GetHeadingDegrees(void)
StatusSubsystem * status_subsystem_
visual status indicator structure
bool initializeIOSubsystem(ControlSubsystem *pComm, const void *serial_port, const void *tcp_client)
Initialize the control subsystem and all related hardware.
Definition control.cc:112
Defines control sub-system.
Provides function prototypes for driver level interfaces It does not have a corresponding ....
void initSensorFusionGlobals(SensorFusionGlobals *sfg, StatusSubsystem *pStatusSubsystem, ControlSubsystem *pControlSubsystem)
utility function to insert default values in the top level structure
The sensor_fusion.h file implements the top level programming interface.
#define PI
pi (it is also defined in Arduino.h)
@ NORMAL
Operation is Nominal.
const float kGeesToMPerSS
To convert acceleration in G to m/s^2, multiply by this constant.
const float kCelsiusToKelvin
To convert degrees C to K, add this constant.
const float kDegToRads
To convert Degrees to Radians, multiply by this constant.
void initializeStatusSubsystem(StatusSubsystem *pStatus)
Definition status.c:169
Application-specific status subsystem.
The ControlSubsystem encapsulates command and data streaming functions.
Definition control.h:51
float ftrFitErrorpc
trial value of fit error %
Definition magnetic.h:74
float fB
current geomagnetic field magnitude (uT)
Definition magnetic.h:65
float fFitErrorpc
current fit error %
Definition magnetic.h:67
float ftrB
trial value of geomagnetic field magnitude in uT
Definition magnetic.h:73
int32_t iValidMagCal
solver used: 0 (no calibration) or 4, 7, 10 element
Definition magnetic.h:68
An instance of PhysicalSensor structure type should be allocated for each physical sensors (combo dev...
quaternion structure definition
Definition orientation.h:25
float q3
z vector component
Definition orientation.h:29
float q0
scalar component
Definition orientation.h:26
float q1
x vector component
Definition orientation.h:27
float q2
y vector component
Definition orientation.h:28
The top level fusion structure.
struct MagCalibration MagCal
mag cal storage
initializeFusionEngine_t * initializeFusionEngine
set sensor fusion structures to initial values
installSensor_t * installSensor
function for installing a new sensor
setStatus_t * queueStatus
queue status change for next regular interval
conditionSensorReadings_t * conditionSensorReadings
preprocessing step for sensor fusion
int32_t loopcounter
counter incrementing each iteration of sensor fusion (typically 25Hz)
runFusion_t * runFusion
run the fusion routines
readSensors_t * readSensors
read all physical sensors
setStatus_t * setStatus
change status indicator immediately
updateStatus_t * updateStatus
status=next status
StatusSubsystem() provides an object-like interface for communicating status to the user.
Definition status.h:26
fusion_status_t status
Current status.
Definition status.h:29