mirror of
https://github.com/SlimeVR/SlimeVR-Tracker-ESP.git
synced 2026-04-05 17:51:57 +02:00
Major refactoring with classes for each sensor
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
#include "motionbase.h"
|
||||
#include "BNO080.cpp"
|
||||
|
||||
BNO080 imu;
|
||||
|
||||
void motionSetup()
|
||||
{
|
||||
delay(500);
|
||||
if(!imu.begin(BNO080_DEFAULT_ADDRESS, Wire)) {
|
||||
Serial.println("Can't connect to BNO08X");
|
||||
for(int i = 0; i < 500; ++i) {
|
||||
delay(50);
|
||||
digitalWrite(LOADING_LED, LOW);
|
||||
delay(50);
|
||||
digitalWrite(LOADING_LED, HIGH);
|
||||
}
|
||||
}
|
||||
Serial.println("Connected to BNO08X");
|
||||
Wire.setClock(400000);
|
||||
imu.enableARVRStabilizedRotationVector(30);
|
||||
}
|
||||
|
||||
void motionLoop()
|
||||
{
|
||||
//Look for reports from the IMU
|
||||
if (imu.dataAvailable() == true)
|
||||
{
|
||||
cq.x = imu.getQuatI();
|
||||
cq.y = imu.getQuatJ();
|
||||
cq.z = imu.getQuatK();
|
||||
cq.w = imu.getQuatReal();
|
||||
cq *= rotationQuat;
|
||||
}
|
||||
}
|
||||
|
||||
void sendData() {
|
||||
sendQuat(&cq, PACKET_ROTATION);
|
||||
}
|
||||
|
||||
void performCalibration() {
|
||||
for(int i = 0; i < 10; ++i) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
}
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
imu.calibrateGyro();
|
||||
imu.requestCalibrationStatus(); // use this
|
||||
while(!imu.calibrationComplete()) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
imu.getReadings();
|
||||
}
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
imu.calibrateAccelerometer();
|
||||
while(!imu.calibrationComplete()) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
imu.getReadings();
|
||||
}
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
imu.calibrateMagnetometer();
|
||||
while(!imu.calibrationComplete()) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
imu.getReadings();
|
||||
}
|
||||
imu.saveCalibration();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
const char* otaPassword = "YOUROTAPASSWORDHERE";
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef _MOTIONBASE_H_
|
||||
#define _MOTIONBASE_H_
|
||||
|
||||
#include "I2Cdev.cpp"
|
||||
#include "MPU9250MotionApps.h"
|
||||
#include "defines.h"
|
||||
#include "quat.cpp"
|
||||
#include "configuration.cpp"
|
||||
#include "util.cpp"
|
||||
#include "udpclient.cpp"
|
||||
|
||||
MPU9250 accelgyro;
|
||||
DeviceConfig config;
|
||||
const CalibrationConfig &calibration = config.calibration;
|
||||
|
||||
// Vector to hold quaternion
|
||||
float q[4] = {1.0, 0.0, 0.0, 0.0};
|
||||
Quaternion rawQuat = Quaternion();
|
||||
const Quat rotationQuat = Quat(Vector3(0, 0, 1), PI / 2.0); // Adjust rotation to match Android rotations
|
||||
Quat cq = Quat();
|
||||
|
||||
void motionSetup();
|
||||
|
||||
void motionLoop();
|
||||
|
||||
void sendData();
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef _OWO_UTIL_H_
|
||||
#define _OWO_UTIL_H_
|
||||
|
||||
float vector_dot(float a[3], float b[3]);
|
||||
|
||||
void vector_normalize(float a[3]);
|
||||
|
||||
#endif // _OWO_UTIL_H_
|
||||
@@ -453,7 +453,7 @@ float BNO080::getPitch()
|
||||
dqy = dqy/norm;
|
||||
dqz = dqz/norm;
|
||||
|
||||
float ysqr = dqy * dqy;
|
||||
//float ysqr = dqy * dqy;
|
||||
|
||||
// pitch (y-axis rotation)
|
||||
float t2 = +2.0 * (dqw * dqy - dqz * dqx);
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "util.h"
|
||||
#include <math.h>
|
||||
#include "helper_3dmath.h"
|
||||
|
||||
float vector_dot(float a[3], float b[3])
|
||||
{
|
||||
@@ -215,4 +215,8 @@ class VectorFloat {
|
||||
}
|
||||
};
|
||||
|
||||
float vector_dot(float a[3], float b[3]);
|
||||
|
||||
void vector_normalize(float a[3]);
|
||||
|
||||
#endif /* _HELPER_3DMATH_H_ */
|
||||
@@ -80,7 +80,6 @@ and so on.
|
||||
// for both classes must be in the include path of your project
|
||||
#include "I2Cdev.h"
|
||||
#include "MPU9250.h"
|
||||
#include "motionbase.h"
|
||||
|
||||
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
|
||||
// is used in I2Cdev.h
|
||||
@@ -114,6 +113,8 @@ int Target[6];
|
||||
int LinesOut;
|
||||
int N;
|
||||
|
||||
MPU9250 accelgyro;
|
||||
|
||||
void ForceHeader()
|
||||
{
|
||||
LinesOut = 99;
|
||||
@@ -150,8 +151,9 @@ void GetSmoothed()
|
||||
}
|
||||
} // GetSmoothed
|
||||
|
||||
void Initialize()
|
||||
void Initialize(MPU9250 &mpu9250)
|
||||
{
|
||||
accelgyro = mpu9250;
|
||||
Serial.println("PID tuning Each Dot = 100 readings");
|
||||
/*A tidbit on how PID (PI actually) tuning works.
|
||||
When we change the offset in the MPU6050 we can get instant results. This allows us to use Proportional and
|
||||
@@ -348,9 +350,9 @@ void PullBracketsOut()
|
||||
} // keep going
|
||||
} // PullBracketsOut
|
||||
|
||||
void findOffset()
|
||||
void findOffset(MPU9250 &mpu9250)
|
||||
{
|
||||
Initialize();
|
||||
Initialize(mpu9250);
|
||||
for (int i = iAx; i <= iGz; i++)
|
||||
{ // set targets and initial guesses
|
||||
Target[i] = 0; // must fix for ZAccel
|
||||
@@ -38,8 +38,12 @@ THE SOFTWARE.
|
||||
#define _MPU9250_H_
|
||||
|
||||
#include "I2Cdev.h"
|
||||
#include "helper_3dmath.h"
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
// MotionApps 2.0 DMP implementation, built using the MPU-6050EVB evaluation board
|
||||
#define MPU9250_INCLUDE_DMP_MOTIONAPPS20
|
||||
|
||||
//Magnetometer Registers
|
||||
#define MPU9150_RA_MAG_ADDRESS 0x0C
|
||||
#define MPU9150_RA_MAG_XOUT_L 0x03
|
||||
@@ -35,13 +35,7 @@ THE SOFTWARE.
|
||||
#ifndef _MPU9250_6AXIS_MOTIONAPPS20_H_
|
||||
#define _MPU9250_6AXIS_MOTIONAPPS20_H_
|
||||
|
||||
#include "I2Cdev.h"
|
||||
#include "helper_3dmath.h"
|
||||
|
||||
// MotionApps 2.0 DMP implementation, built using the MPU-6050EVB evaluation board
|
||||
#define MPU9250_INCLUDE_DMP_MOTIONAPPS20
|
||||
|
||||
#include "MPU9250.cpp"
|
||||
#include "MPU9250.h"
|
||||
|
||||
// Tom Carpenter's conditional PROGMEM code
|
||||
// http://forum.arduino.cc/index.php?topic=129407.0
|
||||
@@ -49,8 +43,8 @@ THE SOFTWARE.
|
||||
#include <avr/pgmspace.h>
|
||||
#else
|
||||
// Teensy 3.0 library conditional PROGMEM code from Paul Stoffregen
|
||||
#ifndef __PGMSPACE_H_
|
||||
#define __PGMSPACE_H_ 1
|
||||
#ifndef _PGMSPACE_H_
|
||||
#define _PGMSPACE_H_ 1
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PROGMEM
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "credentials.h"
|
||||
#include <ArduinoOTA.h>
|
||||
#include "ota.h"
|
||||
|
||||
void otaSetup() {
|
||||
void otaSetup(const char * const otaPassword) {
|
||||
if(otaPassword[0] == '\0')
|
||||
return; // No password set up, disable OTA
|
||||
ArduinoOTA.setPassword(otaPassword);
|
||||
9
lib/ota/ota.h
Normal file
9
lib/ota/ota.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _OTA_H_
|
||||
#define _OTA_H 1
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
void otaSetup(const char * const otaPassword);
|
||||
void otaUpdate();
|
||||
|
||||
#endif // _OTA_H_
|
||||
64
src/bno080sensor.cpp
Normal file
64
src/bno080sensor.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "BNO080.h"
|
||||
#include "sensor.h"
|
||||
#include "udpclient.h"
|
||||
|
||||
void BNO080Sensor::motionSetup(DeviceConfig * config)
|
||||
{
|
||||
delay(500);
|
||||
if(!imu.begin(BNO080_DEFAULT_ADDRESS, Wire)) {
|
||||
Serial.println("Can't connect to BNO08X");
|
||||
for(int i = 0; i < 500; ++i) {
|
||||
delay(50);
|
||||
digitalWrite(LOADING_LED, LOW);
|
||||
delay(50);
|
||||
digitalWrite(LOADING_LED, HIGH);
|
||||
}
|
||||
}
|
||||
Serial.println("Connected to BNO08X");
|
||||
Wire.setClock(400000);
|
||||
imu.enableARVRStabilizedGameRotationVector(10);
|
||||
}
|
||||
|
||||
void BNO080Sensor::motionLoop()
|
||||
{
|
||||
//Look for reports from the IMU
|
||||
if (imu.dataAvailable() == true)
|
||||
{
|
||||
quaternion.x = imu.getQuatI();
|
||||
quaternion.y = imu.getQuatJ();
|
||||
quaternion.z = imu.getQuatK();
|
||||
quaternion.w = imu.getQuatReal();
|
||||
quaternion *= sensorOffset;
|
||||
newData = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BNO080Sensor::sendData() {
|
||||
if(newData) {
|
||||
newData = false;
|
||||
sendQuat(&quaternion, PACKET_ROTATION);
|
||||
}
|
||||
}
|
||||
|
||||
void BNO080Sensor::startCalibration(int calibrationType) {
|
||||
// TODO It only calibrates gyro, it should have multiple calibration modes, and check calibration status in motionLoop()
|
||||
for(int i = 0; i < 10; ++i) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(20);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
}
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
imu.calibrateGyro();
|
||||
do {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
imu.requestCalibrationStatus();
|
||||
delay(20);
|
||||
imu.getReadings();
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
delay(20);
|
||||
} while(!imu.calibrationComplete());
|
||||
imu.saveCalibration();
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
//Determines how often we sample and send data
|
||||
///////////////////////////////////////////////////////////////////
|
||||
#define samplingRateInMillis 10
|
||||
#define batterySampleRate 10000
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//Setup for the Magnetometer
|
||||
|
||||
39
src/main.cpp
39
src/main.cpp
@@ -19,13 +19,21 @@
|
||||
// This version must be compiled with library routines in subfolder "libs"
|
||||
|
||||
#include "Wire.h"
|
||||
#include "ota.cpp"
|
||||
#include "bno085.cpp"
|
||||
#include "ota.h"
|
||||
#include "sensor.h"
|
||||
#include "configuration.h"
|
||||
#include "udpclient.h"
|
||||
#include "defines.h"
|
||||
#include "credentials.h"
|
||||
|
||||
BNO080Sensor sensor{};
|
||||
DeviceConfig config{};
|
||||
|
||||
bool isCalibrating = false;
|
||||
bool blinking = false;
|
||||
unsigned long blinkStart = 0;
|
||||
unsigned long now_ms, last_ms = 0; //millis() timers
|
||||
unsigned long last_battery_sample = 0;
|
||||
|
||||
void setConfig(DeviceConfig newConfig)
|
||||
{
|
||||
@@ -74,11 +82,11 @@ void setup()
|
||||
{{ 1.01176, -0.00048, 0.00230},
|
||||
{ -0.00048, 1.00366, -0.00535},
|
||||
{ 0.00230, -0.00535, 0.99003}},
|
||||
{ 9.85, 47.89, -10.94},
|
||||
{{ 1.11351, -0.01290, -0.00077},
|
||||
{ -0.01290, 1.18048, 0.01839},
|
||||
{ -0.00077, 0.01839, 1.15212}},
|
||||
{ 24.59, -4.96, -93.38}};
|
||||
{ 24.95, 81.77, -10.36},
|
||||
{{ 1.44652, 0.02739, -0.02186},
|
||||
{ 0.02739, 1.48749, -0.00547},
|
||||
{ -0.02186, -0.00547, 1.42441}},
|
||||
{ 22.97, 13.56, -90.65}};
|
||||
if (hasConfigStored())
|
||||
{
|
||||
loadConfig(&config);
|
||||
@@ -86,7 +94,7 @@ void setup()
|
||||
setConfigRecievedCallback(setConfig);
|
||||
setCommandRecievedCallback(commandRecieved);
|
||||
|
||||
motionSetup();
|
||||
sensor.motionSetup(&config);
|
||||
|
||||
// Don't start if not connected to MPU
|
||||
/*while(!accelgyro.testConnection()) {
|
||||
@@ -98,7 +106,7 @@ void setup()
|
||||
//*/
|
||||
|
||||
setUpWiFi(&config);
|
||||
otaSetup();
|
||||
otaSetup(otaPassword);
|
||||
digitalWrite(LOADING_LED, HIGH);
|
||||
}
|
||||
|
||||
@@ -108,14 +116,14 @@ void loop()
|
||||
{
|
||||
otaUpdate();
|
||||
clientUpdate();
|
||||
if(connected)
|
||||
if(isConnected())
|
||||
{
|
||||
if (isCalibrating)
|
||||
{
|
||||
performCalibration();
|
||||
sensor.startCalibration(0);
|
||||
isCalibrating = false;
|
||||
}
|
||||
motionLoop();
|
||||
sensor.motionLoop();
|
||||
// Send updates
|
||||
now_ms = millis();
|
||||
if (now_ms - last_ms >= samplingRateInMillis)
|
||||
@@ -123,7 +131,12 @@ void loop()
|
||||
last_ms = now_ms;
|
||||
processBlinking();
|
||||
|
||||
sendData();
|
||||
sensor.sendData();
|
||||
}
|
||||
if(now_ms - last_battery_sample >= batterySampleRate) {
|
||||
last_battery_sample = now_ms;
|
||||
float battery = ((float) analogRead(A0)) / 1024.0 * 14.0;
|
||||
sendFloat(battery, PACKET_BATTERY_LEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,34 @@
|
||||
#include "motionbase.h"
|
||||
#include "MPU6050OffsetFinder.cpp"
|
||||
#include "MPU9250.h"
|
||||
#include "sensor.h"
|
||||
#include "udpclient.h"
|
||||
|
||||
// MPU control/status vars
|
||||
bool dmpReady = false; // set true if DMP init was successful
|
||||
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
|
||||
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
|
||||
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
|
||||
uint16_t fifoCount; // count of all bytes currently in FIFO
|
||||
uint8_t fifoBuffer[64]; // FIFO storage buffer
|
||||
void gatherCalibrationData(MPU9250 &imu);
|
||||
|
||||
void motionSetup() {
|
||||
void MPU6050Sensor::motionSetup(DeviceConfig * config) {
|
||||
// initialize device
|
||||
accelgyro.initialize();
|
||||
devStatus = accelgyro.dmpInitialize();
|
||||
imu.initialize();
|
||||
devStatus = imu.dmpInitialize();
|
||||
|
||||
accelgyro.setXGyroOffset(calibration.G_off[0]);
|
||||
accelgyro.setYGyroOffset(calibration.G_off[1]);
|
||||
accelgyro.setZGyroOffset(calibration.G_off[2]);
|
||||
accelgyro.setXAccelOffset(calibration.A_B[0]);
|
||||
accelgyro.setYAccelOffset(calibration.A_B[1]);
|
||||
accelgyro.setZAccelOffset(calibration.A_B[2]);
|
||||
imu.setXGyroOffset(config->calibration.G_off[0]);
|
||||
imu.setYGyroOffset(config->calibration.G_off[1]);
|
||||
imu.setZGyroOffset(config->calibration.G_off[2]);
|
||||
imu.setXAccelOffset(config->calibration.A_B[0]);
|
||||
imu.setYAccelOffset(config->calibration.A_B[1]);
|
||||
imu.setZAccelOffset(config->calibration.A_B[2]);
|
||||
|
||||
// make sure it worked (returns 0 if so)
|
||||
if (devStatus == 0) {
|
||||
// turn on the DMP, now that it's ready
|
||||
Serial.println(F("Enabling DMP..."));
|
||||
accelgyro.setDMPEnabled(true);
|
||||
mpuIntStatus = accelgyro.getIntStatus();
|
||||
imu.setDMPEnabled(true);
|
||||
mpuIntStatus = imu.getIntStatus();
|
||||
|
||||
// set our DMP Ready flag so the main loop() function knows it's okay to use it
|
||||
Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
||||
dmpReady = true;
|
||||
|
||||
// get expected DMP packet size for later comparison
|
||||
packetSize = accelgyro.dmpGetFIFOPacketSize();
|
||||
packetSize = imu.dmpGetFIFOPacketSize();
|
||||
} else {
|
||||
// ERROR!
|
||||
// 1 = initial memory load failed
|
||||
@@ -45,55 +40,55 @@ void motionSetup() {
|
||||
}
|
||||
}
|
||||
|
||||
void motionLoop() {
|
||||
void MPU6050Sensor::motionLoop() {
|
||||
// if programming failed, don't try to do anything
|
||||
if (!dmpReady) return;
|
||||
mpuIntStatus = accelgyro.getIntStatus();
|
||||
mpuIntStatus = imu.getIntStatus();
|
||||
|
||||
// get current FIFO count
|
||||
fifoCount = accelgyro.getFIFOCount();
|
||||
fifoCount = imu.getFIFOCount();
|
||||
|
||||
// check for overflow (this should never happen unless our code is too inefficient)
|
||||
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
|
||||
// reset so we can continue cleanly
|
||||
accelgyro.resetFIFO();
|
||||
imu.resetFIFO();
|
||||
Serial.println(F("FIFO overflow!"));
|
||||
|
||||
// otherwise, check for DMP data ready interrupt (this should happen frequently)
|
||||
} else if (mpuIntStatus & 0x02) {
|
||||
// wait for correct available data length, should be a VERY short wait
|
||||
while (fifoCount < packetSize) fifoCount = accelgyro.getFIFOCount();
|
||||
while (fifoCount < packetSize) fifoCount = imu.getFIFOCount();
|
||||
|
||||
// read a packet from FIFO
|
||||
accelgyro.getFIFOBytes(fifoBuffer, packetSize);
|
||||
imu.getFIFOBytes(fifoBuffer, packetSize);
|
||||
|
||||
// track FIFO count here in case there is > 1 packet available
|
||||
// (this lets us immediately read more without waiting for an interrupt)
|
||||
fifoCount -= packetSize;
|
||||
|
||||
accelgyro.dmpGetQuaternion(&rawQuat, fifoBuffer);
|
||||
imu.dmpGetQuaternion(&rawQuat, fifoBuffer);
|
||||
q[0] = rawQuat.x;
|
||||
q[1] = rawQuat.y;
|
||||
q[2] = rawQuat.z;
|
||||
q[3] = rawQuat.w;
|
||||
cq.set(-q[1], q[0], q[2], q[3]);
|
||||
cq *= rotationQuat;
|
||||
quaternion.set(-q[1], q[0], q[2], q[3]);
|
||||
quaternion *= sensorOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void sendData() {
|
||||
sendQuat(&cq, PACKET_ROTATION);
|
||||
void MPU6050Sensor::sendData() {
|
||||
sendQuat(&quaternion, PACKET_ROTATION);
|
||||
}
|
||||
|
||||
void performCalibration() {
|
||||
void MPU6050Sensor::startCalibration(int calibrationType) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
Serial.println("Starting offset finder");
|
||||
findOffset();
|
||||
gatherCalibrationData(imu);
|
||||
Serial.println("Process is over");
|
||||
digitalWrite(CALIBRATING_LED, HIGH);
|
||||
}
|
||||
|
||||
void gatherCalibrationData() {
|
||||
void gatherCalibrationData(MPU9250 &imu) {
|
||||
Serial.println("Gathering raw data for device calibration...");
|
||||
int calibrationSamples = 500;
|
||||
// Reset values
|
||||
@@ -110,7 +105,7 @@ void gatherCalibrationData() {
|
||||
delay(2000);
|
||||
for (int i = 0; i < calibrationSamples; i++)
|
||||
{
|
||||
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
|
||||
imu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
|
||||
Gxyz[0] += float(gx);
|
||||
Gxyz[1] += float(gy);
|
||||
Gxyz[2] += float(gz);
|
||||
@@ -135,7 +130,7 @@ void gatherCalibrationData() {
|
||||
for (int i = 0; i < calibrationSamples; i++)
|
||||
{
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
accelgyro.getAcceleration(&ax, &ay, &az);
|
||||
imu.getAcceleration(&ax, &ay, &az);
|
||||
calibrationData[0] = ax;
|
||||
calibrationData[1] = ay;
|
||||
calibrationData[2] = az;
|
||||
@@ -1,62 +1,54 @@
|
||||
#include "motionbase.h"
|
||||
#include "MPU9250.h"
|
||||
#include "sensor.h"
|
||||
#include "udpclient.h"
|
||||
#include "defines.h"
|
||||
#include "helper_3dmath.h"
|
||||
|
||||
//raw data and scaled as vector
|
||||
int16_t ax, ay, az;
|
||||
int16_t gx, gy, gz;
|
||||
int16_t mx, my, mz;
|
||||
float Axyz[3];
|
||||
float Gxyz[3];
|
||||
float Mxyz[3];
|
||||
float rawMag[3];
|
||||
#define gscale (250. / 32768.0) * (PI / 180.0) //gyro default 250 LSB per d/s -> rad/s
|
||||
|
||||
// NOW USING MAHONY FILTER
|
||||
|
||||
// These are the free parameters in the Mahony filter and fusion scheme,
|
||||
// Kp for proportional feedback, Ki for integral
|
||||
// with MPU-9250, angles start oscillating at Kp=40. Ki does not seem to help and is not required.
|
||||
#define Kp 10.0
|
||||
#define Ki 0.0
|
||||
|
||||
// Loop timing globals
|
||||
unsigned long now = 0, last = 0; //micros() timers
|
||||
float deltat = 0; //loop time in seconds
|
||||
|
||||
void get_MPU_scaled();
|
||||
void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat);
|
||||
|
||||
void motionSetup() {
|
||||
CalibrationConfig * calibration;
|
||||
|
||||
void MPU9250Sensor::motionSetup(DeviceConfig * config) {
|
||||
calibration = &config->calibration;
|
||||
// initialize device
|
||||
accelgyro.initialize();
|
||||
imu.initialize();
|
||||
}
|
||||
|
||||
void motionLoop() {
|
||||
void MPU9250Sensor::motionLoop() {
|
||||
// Update quaternion
|
||||
now = micros();
|
||||
deltat = (now - last) * 1.0e-6; //seconds since last update
|
||||
last = now;
|
||||
get_MPU_scaled();
|
||||
getMPUScaled();
|
||||
MahonyQuaternionUpdate(Axyz[0], Axyz[1], Axyz[2], Gxyz[0], Gxyz[1], Gxyz[2], Mxyz[1], Mxyz[0], -Mxyz[2], deltat);
|
||||
cq.set(-q[1], -q[2], -q[0], q[3]);
|
||||
cq *= rotationQuat;
|
||||
quaternion.set(-q[1], -q[2], -q[0], q[3]);
|
||||
quaternion *= sensorOffset;
|
||||
}
|
||||
|
||||
void sendData() {
|
||||
sendQuat(&cq, PACKET_ROTATION);
|
||||
void MPU9250Sensor::sendData() {
|
||||
sendQuat(&quaternion, PACKET_ROTATION);
|
||||
sendVector(rawMag, PACKET_RAW_MAGENTOMETER);
|
||||
sendVector(Axyz, PACKET_ACCEL);
|
||||
sendVector(Mxyz, PACKET_MAG);
|
||||
}
|
||||
|
||||
void get_MPU_scaled()
|
||||
void MPU9250Sensor::getMPUScaled()
|
||||
{
|
||||
float temp[3];
|
||||
int i;
|
||||
accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
imu.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
|
||||
Gxyz[0] = ((float)gx - calibration.G_off[0]) * gscale; //250 LSB(d/s) default to radians/s
|
||||
Gxyz[1] = ((float)gy - calibration.G_off[1]) * gscale;
|
||||
Gxyz[2] = ((float)gz - calibration.G_off[2]) * gscale;
|
||||
Gxyz[0] = ((float)gx - calibration->G_off[0]) * gscale; //250 LSB(d/s) default to radians/s
|
||||
Gxyz[1] = ((float)gy - calibration->G_off[1]) * gscale;
|
||||
Gxyz[2] = ((float)gz - calibration->G_off[2]) * gscale;
|
||||
|
||||
Axyz[0] = (float)ax;
|
||||
Axyz[1] = (float)ay;
|
||||
@@ -64,13 +56,13 @@ void get_MPU_scaled()
|
||||
//apply offsets (bias) and scale factors from Magneto
|
||||
if(useFullCalibrationMatrix) {
|
||||
for (i = 0; i < 3; i++)
|
||||
temp[i] = (Axyz[i] - calibration.A_B[i]);
|
||||
Axyz[0] = calibration.A_Ainv[0][0] * temp[0] + calibration.A_Ainv[0][1] * temp[1] + calibration.A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = calibration.A_Ainv[1][0] * temp[0] + calibration.A_Ainv[1][1] * temp[1] + calibration.A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = calibration.A_Ainv[2][0] * temp[0] + calibration.A_Ainv[2][1] * temp[1] + calibration.A_Ainv[2][2] * temp[2];
|
||||
temp[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
Axyz[0] = calibration->A_Ainv[0][0] * temp[0] + calibration->A_Ainv[0][1] * temp[1] + calibration->A_Ainv[0][2] * temp[2];
|
||||
Axyz[1] = calibration->A_Ainv[1][0] * temp[0] + calibration->A_Ainv[1][1] * temp[1] + calibration->A_Ainv[1][2] * temp[2];
|
||||
Axyz[2] = calibration->A_Ainv[2][0] * temp[0] + calibration->A_Ainv[2][1] * temp[1] + calibration->A_Ainv[2][2] * temp[2];
|
||||
} else {
|
||||
for (i = 0; i < 3; i++)
|
||||
Axyz[i] = (Axyz[i] - calibration.A_B[i]);
|
||||
Axyz[i] = (Axyz[i] - calibration->A_B[i]);
|
||||
}
|
||||
vector_normalize(Axyz);
|
||||
|
||||
@@ -80,13 +72,13 @@ void get_MPU_scaled()
|
||||
//apply offsets and scale factors from Magneto
|
||||
if(useFullCalibrationMatrix) {
|
||||
for (i = 0; i < 3; i++)
|
||||
temp[i] = (Mxyz[i] - calibration.M_B[i]);
|
||||
Mxyz[0] = calibration.M_Ainv[0][0] * temp[0] + calibration.M_Ainv[0][1] * temp[1] + calibration.M_Ainv[0][2] * temp[2];
|
||||
Mxyz[1] = calibration.M_Ainv[1][0] * temp[0] + calibration.M_Ainv[1][1] * temp[1] + calibration.M_Ainv[1][2] * temp[2];
|
||||
Mxyz[2] = calibration.M_Ainv[2][0] * temp[0] + calibration.M_Ainv[2][1] * temp[1] + calibration.M_Ainv[2][2] * temp[2];
|
||||
temp[i] = (Mxyz[i] - calibration->M_B[i]);
|
||||
Mxyz[0] = calibration->M_Ainv[0][0] * temp[0] + calibration->M_Ainv[0][1] * temp[1] + calibration->M_Ainv[0][2] * temp[2];
|
||||
Mxyz[1] = calibration->M_Ainv[1][0] * temp[0] + calibration->M_Ainv[1][1] * temp[1] + calibration->M_Ainv[1][2] * temp[2];
|
||||
Mxyz[2] = calibration->M_Ainv[2][0] * temp[0] + calibration->M_Ainv[2][1] * temp[1] + calibration->M_Ainv[2][2] * temp[2];
|
||||
} else {
|
||||
for (i = 0; i < 3; i++)
|
||||
Mxyz[i] = (Mxyz[i] - calibration.M_B[i]);
|
||||
Mxyz[i] = (Mxyz[i] - calibration->M_B[i]);
|
||||
}
|
||||
rawMag[0] = Mxyz[0];
|
||||
rawMag[1] = Mxyz[1];
|
||||
@@ -101,7 +93,7 @@ void get_MPU_scaled()
|
||||
// input vectors ax, ay, az and mx, my, mz MUST be normalized!
|
||||
// gx, gy, gz must be in units of radians/second
|
||||
//
|
||||
void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat)
|
||||
void MPU9250Sensor::MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat)
|
||||
{
|
||||
// Vector to hold integral error for Mahony method
|
||||
static float eInt[3] = {0.0, 0.0, 0.0};
|
||||
@@ -188,7 +180,7 @@ void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, fl
|
||||
q[3] = q4 * norm;
|
||||
}
|
||||
|
||||
void performCalibration() {
|
||||
void MPU9250Sensor::startCalibration(int calibrationType) {
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
Serial.println("Gathering raw data for device calibration...");
|
||||
int calibrationSamples = 300;
|
||||
@@ -202,7 +194,7 @@ void performCalibration() {
|
||||
delay(2000);
|
||||
for (int i = 0; i < calibrationSamples; i++)
|
||||
{
|
||||
accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
imu.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
Gxyz[0] += float(gx);
|
||||
Gxyz[1] += float(gy);
|
||||
Gxyz[2] += float(gz);
|
||||
@@ -226,7 +218,7 @@ void performCalibration() {
|
||||
for (int i = 0; i < calibrationSamples; i++)
|
||||
{
|
||||
digitalWrite(CALIBRATING_LED, LOW);
|
||||
accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
imu.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
|
||||
calibrationData[0] = ax;
|
||||
calibrationData[1] = ay;
|
||||
calibrationData[2] = az;
|
||||
89
src/sensor.h
Normal file
89
src/sensor.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef _SENSOR_H_
|
||||
#define _SENSOR_H_ 1
|
||||
|
||||
#include <BNO080.h>
|
||||
#include <MPU9250.h>
|
||||
#include <quat.h>
|
||||
#include <vector3.h>
|
||||
#include "configuration.h"
|
||||
|
||||
class Sensor {
|
||||
public:
|
||||
Sensor() = default;
|
||||
virtual ~Sensor() = default;
|
||||
virtual void motionSetup(DeviceConfig * config) = 0;
|
||||
virtual void motionLoop() = 0;
|
||||
virtual void sendData() = 0;
|
||||
virtual void startCalibration(int calibrationType) = 0;
|
||||
protected:
|
||||
Quat quaternion {};
|
||||
Quat sensorOffset {Quat(Vector3(0, 0, 1), PI / 2.0)};
|
||||
};
|
||||
|
||||
class BNO080Sensor : public Sensor {
|
||||
public:
|
||||
BNO080Sensor() = default;
|
||||
~BNO080Sensor() = default;
|
||||
void motionSetup(DeviceConfig * config) final;
|
||||
void motionLoop() final;
|
||||
void sendData() final;
|
||||
void startCalibration(int calibrationType) final;
|
||||
private:
|
||||
BNO080 imu {};
|
||||
bool newData {false};
|
||||
};
|
||||
|
||||
class MPUSensor : public Sensor {
|
||||
public:
|
||||
MPUSensor() = default;
|
||||
~MPUSensor() = default;
|
||||
protected:
|
||||
MPU9250 imu {};
|
||||
float q[4] {1.0, 0.0, 0.0, 0.0};
|
||||
};
|
||||
|
||||
class MPU6050Sensor : public MPUSensor {
|
||||
public:
|
||||
MPU6050Sensor() = default;
|
||||
~MPU6050Sensor() = default;
|
||||
void motionSetup(DeviceConfig * config) final;
|
||||
void motionLoop() final;
|
||||
void sendData() final;
|
||||
void startCalibration(int calibrationType) final;
|
||||
private:
|
||||
Quaternion rawQuat {};
|
||||
// MPU dmp control/status vars
|
||||
bool dmpReady{false}; // set true if DMP init was successful
|
||||
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
|
||||
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
|
||||
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
|
||||
uint16_t fifoCount; // count of all bytes currently in FIFO
|
||||
uint8_t fifoBuffer[64] {}; // FIFO storage buffer
|
||||
};
|
||||
|
||||
class MPU9250Sensor : public MPUSensor {
|
||||
public:
|
||||
MPU9250Sensor() = default;
|
||||
~MPU9250Sensor() = default;
|
||||
void motionSetup(DeviceConfig * config) final;
|
||||
void motionLoop() final;
|
||||
void sendData() final;
|
||||
void startCalibration(int calibrationType) final;
|
||||
void getMPUScaled();
|
||||
void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat);
|
||||
private:
|
||||
MPU9250 imu {};
|
||||
//raw data and scaled as vector
|
||||
int16_t ax, ay, az;
|
||||
int16_t gx, gy, gz;
|
||||
int16_t mx, my, mz;
|
||||
float Axyz[3] {};
|
||||
float Gxyz[3] {};
|
||||
float Mxyz[3] {};
|
||||
float rawMag[3] {};
|
||||
// Loop timing globals
|
||||
unsigned long now = 0, last = 0; //micros() timers
|
||||
float deltat = 0; //loop time in seconds
|
||||
};
|
||||
|
||||
#endif /* _SENSOR_H_ */
|
||||
@@ -103,6 +103,26 @@ void sendVector(float *const result, int type)
|
||||
}
|
||||
}
|
||||
|
||||
void sendFloat(float const value, int type)
|
||||
{
|
||||
if (Udp.beginPacket(host, port) > 0)
|
||||
{
|
||||
sendType(type);
|
||||
sendPacketNumber();
|
||||
Udp.write(convert_to_chars(value, buf), sizeof(value));
|
||||
if (Udp.endPacket() == 0)
|
||||
{
|
||||
//Serial.print("Write error: ");
|
||||
//Serial.println(Udp.getWriteError());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Serial.print("Write error: ");
|
||||
//Serial.println(Udp.getWriteError());
|
||||
}
|
||||
}
|
||||
|
||||
void sendQuat(Quat *const quaternion, int type)
|
||||
{
|
||||
if (Udp.beginPacket(host, port) > 0)
|
||||
@@ -381,6 +401,10 @@ void setUpWiFi(DeviceConfig * const config) {
|
||||
Udp.begin(port);
|
||||
}
|
||||
|
||||
bool isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
void connectClient()
|
||||
{
|
||||
unsigned long now = millis();
|
||||
@@ -18,6 +18,7 @@
|
||||
#define PACKET_RAW_MAGENTOMETER 9
|
||||
#define PACKET_PING_PONG 10
|
||||
#define PACKET_SERIAL 11
|
||||
#define PACKET_BATTERY_LEVEL 12
|
||||
|
||||
#define PACKET_RECIEVE_HEARTBEAT 1
|
||||
#define PACKET_RECIEVE_VIBRATE 2
|
||||
@@ -37,9 +38,12 @@ void sendQuat(float * const quaternion, int type);
|
||||
void sendQuat(Quat * const quaternion, int type);
|
||||
void sendVector(float * const result, int type);
|
||||
void sendConfig(DeviceConfig * const config, int type);
|
||||
void sendFloat(float const value, int type);
|
||||
void sendRawCalibrationData(int * const data, int type);
|
||||
void setConfigRecievedCallback(configRecievedCallback);
|
||||
void setCommandRecievedCallback(commandRecievedCallback);
|
||||
void setUpWiFi(DeviceConfig * const config);
|
||||
bool isConnected();
|
||||
|
||||
template<typename T> T convert_chars(unsigned char* src);
|
||||
template<typename T> unsigned char* convert_to_chars(T src, unsigned char* target);
|
||||
Reference in New Issue
Block a user