Refactor to simple AHRS implementation with loose coupling to 10DOF and 9DOF boards.
This commit is contained in:
parent
3ecc273f7f
commit
3b15ae25ec
20 changed files with 233 additions and 22952 deletions
38
Adafruit_10DOF.cpp
Executable file
38
Adafruit_10DOF.cpp
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#include "Adafruit_10DOF.h"
|
||||
|
||||
Adafruit_10DOF::Adafruit_10DOF():
|
||||
_accel(30301),
|
||||
_mag(30302),
|
||||
_gyro(30303),
|
||||
_bmp(18001)
|
||||
{}
|
||||
|
||||
bool Adafruit_10DOF::begin() {
|
||||
if (!_accel.begin()) {
|
||||
return false;
|
||||
}
|
||||
if (!_mag.begin()) {
|
||||
return false;
|
||||
}
|
||||
if (!_gyro.begin()) {
|
||||
return false;
|
||||
}
|
||||
if (!_bmp.begin()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Adafruit_Sensor* Adafruit_10DOF::getSensor(sensors_type_t type) {
|
||||
switch (type) {
|
||||
case SENSOR_TYPE_ACCELEROMETER:
|
||||
return &_accel;
|
||||
case SENSOR_TYPE_MAGNETIC_FIELD:
|
||||
return &_mag;
|
||||
case SENSOR_TYPE_GYROSCOPE:
|
||||
return &_gyro;
|
||||
case SENSOR_TYPE_PRESSURE:
|
||||
return &_bmp;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
26
Adafruit_10DOF.h
Executable file
26
Adafruit_10DOF.h
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef __ADAFRUIT_10DOF_H__
|
||||
#define __ADAFRUIT_10DOF_H__
|
||||
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <Adafruit_LSM303_U.h>
|
||||
#include <Adafruit_L3GD20_U.h>
|
||||
#include <Adafruit_BMP085_U.h>
|
||||
#include "Adafruit_Sensor_Set.h"
|
||||
|
||||
// 10DOF board with an accelerometer, magnetometer, gyroscope, and pressure sensor.
|
||||
class Adafruit_10DOF: Adafruit_Sensor_Set
|
||||
{
|
||||
public:
|
||||
Adafruit_10DOF();
|
||||
bool begin();
|
||||
Adafruit_Sensor* getSensor(sensors_type_t type);
|
||||
|
||||
private:
|
||||
Adafruit_LSM303_Accel_Unified _accel;
|
||||
Adafruit_LSM303_Mag_Unified _mag;
|
||||
Adafruit_L3GD20_Unified _gyro;
|
||||
Adafruit_BMP085_Unified _bmp;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
32
Adafruit_9DOF.cpp
Executable file
32
Adafruit_9DOF.cpp
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
#include "Adafruit_9DOF.h"
|
||||
|
||||
Adafruit_9DOF::Adafruit_9DOF():
|
||||
_accel(30301),
|
||||
_mag(30302),
|
||||
_gyro(30303)
|
||||
{}
|
||||
|
||||
bool Adafruit_9DOF::begin() {
|
||||
if (!_accel.begin()) {
|
||||
return false;
|
||||
}
|
||||
if (!_mag.begin()) {
|
||||
return false;
|
||||
}
|
||||
if (!_gyro.begin()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Adafruit_Sensor* Adafruit_9DOF::getSensor(sensors_type_t type) {
|
||||
switch (type) {
|
||||
case SENSOR_TYPE_ACCELEROMETER:
|
||||
return &_accel;
|
||||
case SENSOR_TYPE_MAGNETIC_FIELD:
|
||||
return &_mag;
|
||||
case SENSOR_TYPE_GYROSCOPE:
|
||||
return &_gyro;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
30
Adafruit_9DOF.h
Executable file
30
Adafruit_9DOF.h
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef __ADAFRUIT_9DOF_H__
|
||||
#define __ADAFRUIT_9DOF_H__
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <Adafruit_LSM303_U.h>
|
||||
#include <Adafruit_L3GD20_U.h>
|
||||
#include "Adafruit_Sensor_Set.h"
|
||||
|
||||
// 9DOF board with an accelerometer, magnetometer, and gyroscope.
|
||||
class Adafruit_9DOF: Adafruit_Sensor_Set
|
||||
{
|
||||
public:
|
||||
Adafruit_9DOF();
|
||||
bool begin();
|
||||
Adafruit_Sensor* getSensor(sensors_type_t type);
|
||||
|
||||
private:
|
||||
Adafruit_LSM303_Accel_Unified _accel;
|
||||
Adafruit_LSM303_Mag_Unified _mag;
|
||||
Adafruit_L3GD20_Unified _gyro;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
14
Adafruit_Sensor_Set.h
Normal file
14
Adafruit_Sensor_Set.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __ADAFRUIT_SENSOR_SET_H__
|
||||
#define __ADAFRUIT_SENSOR_SET_H__
|
||||
|
||||
#include <Adafruit_Sensor.h>
|
||||
|
||||
// Interface for a device that has multiple sensors that can be queried by type.
|
||||
class Adafruit_Sensor_Set
|
||||
{
|
||||
public:
|
||||
virtual ~Adafruit_Sensor_Set() {}
|
||||
Adafruit_Sensor* getSensor(sensors_type_t type) { return NULL; }
|
||||
};
|
||||
|
||||
#endif
|
||||
72
Adafruit_Simple_AHRS.cpp
Normal file
72
Adafruit_Simple_AHRS.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include "Adafruit_Simple_AHRS.h"
|
||||
|
||||
// Create a simple AHRS from an explicit accelerometer and magnetometer sensor.
|
||||
Adafruit_Simple_AHRS::Adafruit_Simple_AHRS(Adafruit_Sensor* accelerometer, Adafruit_Sensor* magnetometer):
|
||||
_accel(accelerometer),
|
||||
_mag(magnetometer)
|
||||
{}
|
||||
|
||||
// Create a simple AHRS from a device with multiple sensors.
|
||||
Adafruit_Simple_AHRS::Adafruit_Simple_AHRS(const Adafruit_Sensor_Set& sensors):
|
||||
_accel(sensors.getSensor(SENSOR_TYPE_ACCELEROMETER)),
|
||||
_mag(sensors.getSensor(SENSOR_TYPE_MAGNETIC_FIELD))
|
||||
{}
|
||||
|
||||
// Compute orientation based on accelerometer and magnetometer data.
|
||||
bool Adafruit_Simple_AHRS::getOrientation(sensors_vec_t* orientation) {
|
||||
// Validate input and available sensors.
|
||||
if (orientation == NULL || _accel == NULL || _mag == NULL) return false;
|
||||
|
||||
// Grab an acceleromter and magnetometer reading.
|
||||
sensor_event_t accel_event;
|
||||
_accel.getEvent(&accel_event);
|
||||
sensor_event_t mag_event;
|
||||
_mag.getEvent(&mag_event);
|
||||
|
||||
float const PI_F = 3.14159265F;
|
||||
|
||||
// roll: Rotation around the X-axis. -180 <= roll <= 180
|
||||
// a positive roll angle is defined to be a clockwise rotation about the positive X-axis
|
||||
//
|
||||
// y
|
||||
// roll = atan2(---)
|
||||
// z
|
||||
//
|
||||
// where: y, z are returned value from accelerometer sensor
|
||||
orientation->roll = (float)atan2(accel_event->acceleration.y, accel_event->acceleration.z);
|
||||
|
||||
// pitch: Rotation around the Y-axis. -180 <= roll <= 180
|
||||
// a positive pitch angle is defined to be a clockwise rotation about the positive Y-axis
|
||||
//
|
||||
// -x
|
||||
// pitch = atan(-------------------------------)
|
||||
// y * sin(roll) + z * cos(roll)
|
||||
//
|
||||
// where: x, y, z are returned value from accelerometer sensor
|
||||
if (accel_event->acceleration.y * sin(orientation->roll) + accel_event->acceleration.z * cos(orientation->roll) == 0)
|
||||
orientation->pitch = accel_event->acceleration.x > 0 ? (PI_F / 2) : (-PI_F / 2);
|
||||
else
|
||||
orientation->pitch = (float)atan(-accel_event->acceleration.x / (accel_event->acceleration.y * sin(orientation->roll) + \
|
||||
accel_event->acceleration.z * cos(orientation->roll)));
|
||||
|
||||
// heading: Rotation around the Z-axis. -180 <= roll <= 180
|
||||
// a positive heading angle is defined to be a clockwise rotation about the positive Z-axis
|
||||
//
|
||||
// z * sin(roll) - y * cos(roll)
|
||||
// heading = atan2(--------------------------------------------------------------------------)
|
||||
// x * cos(pitch) + y * sin(pitch) * sin(roll) + z * sin(pitch) * cos(roll))
|
||||
//
|
||||
// where: x, y, z are returned value from magnetometer sensor
|
||||
orientation->heading = (float)atan2(mag_event->magnetic.z * sin(orientation->roll) - mag_event->magnetic.y * cos(orientation->roll), \
|
||||
mag_event->magnetic.x * cos(orientation->pitch) + \
|
||||
mag_event->magnetic.y * sin(orientation->pitch) * sin(orientation->roll) + \
|
||||
mag_event->magnetic.z * sin(orientation->pitch) * cos(orientation->roll));
|
||||
|
||||
|
||||
// Convert angular data to degree
|
||||
orientation->roll = orientation->roll * 180 / PI_F;
|
||||
orientation->pitch = orientation->pitch * 180 / PI_F;
|
||||
orientation->heading = orientation->heading * 180 / PI_F;
|
||||
|
||||
return true;
|
||||
}
|
||||
21
Adafruit_Simple_AHRS.h
Normal file
21
Adafruit_Simple_AHRS.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __ADAFRUIT_SIMPLE_AHRS_H__
|
||||
#define __ADAFRUIT_SIMPLE_AHRS_H__
|
||||
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include "Adafruit_Sensor_Set.h"
|
||||
|
||||
// Simple sensor fusion AHRS using an accelerometer and magnetometer.
|
||||
class Adafruit_Simple_AHRS
|
||||
{
|
||||
public:
|
||||
Adafruit_Simple_AHRS(Adafruit_Sensor* accelerometer, Adafruit_Sensor* magnetometer);
|
||||
Adafruit_Simple_AHRS(const Adafruit_Sensor_Set& sensors);
|
||||
bool getOrientation(sensors_vec_t* orientation);
|
||||
|
||||
private:
|
||||
Adafruit_Sensor* _accel;
|
||||
Adafruit_Sensor* _mag;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
159
L3G.cpp
159
L3G.cpp
|
|
@ -1,159 +0,0 @@
|
|||
#include "L3G.h"
|
||||
#include <Wire.h>
|
||||
#include <math.h>
|
||||
|
||||
// Defines ////////////////////////////////////////////////////////////////
|
||||
|
||||
// The Arduino two-wire interface uses a 7-bit number for the address,
|
||||
// and sets the last bit correctly based on reads and writes
|
||||
#define L3G4200D_ADDRESS_SA0_LOW (0xD0 >> 1)
|
||||
#define L3G4200D_ADDRESS_SA0_HIGH (0xD2 >> 1)
|
||||
#define L3GD20_ADDRESS_SA0_LOW (0xD4 >> 1)
|
||||
#define L3GD20_ADDRESS_SA0_HIGH (0xD6 >> 1)
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
bool L3G::init(byte device, byte sa0)
|
||||
{
|
||||
_device = device;
|
||||
switch (_device)
|
||||
{
|
||||
case L3G4200D_DEVICE:
|
||||
if (sa0 == L3G_SA0_LOW)
|
||||
{
|
||||
address = L3G4200D_ADDRESS_SA0_LOW;
|
||||
return true;
|
||||
}
|
||||
else if (sa0 == L3G_SA0_HIGH)
|
||||
{
|
||||
address = L3G4200D_ADDRESS_SA0_HIGH;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return autoDetectAddress();
|
||||
break;
|
||||
|
||||
case L3GD20_DEVICE:
|
||||
if (sa0 == L3G_SA0_LOW)
|
||||
{
|
||||
address = L3GD20_ADDRESS_SA0_LOW;
|
||||
return true;
|
||||
}
|
||||
else if (sa0 == L3G_SA0_HIGH)
|
||||
{
|
||||
address = L3GD20_ADDRESS_SA0_HIGH;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return autoDetectAddress();
|
||||
break;
|
||||
|
||||
default:
|
||||
return autoDetectAddress();
|
||||
}
|
||||
}
|
||||
|
||||
// Turns on the L3G's gyro and places it in normal mode.
|
||||
void L3G::enableDefault(void)
|
||||
{
|
||||
writeReg(L3G_CTRL_REG1, 0b00001111); // Normal power mode, all axes enabled
|
||||
writeReg(L3G_CTRL_REG4, 0b00100000); // 2000 dps full scale
|
||||
}
|
||||
|
||||
// Writes a gyro register
|
||||
void L3G::writeReg(byte reg, byte value)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Reads a gyro register
|
||||
byte L3G::readReg(byte reg)
|
||||
{
|
||||
byte value;
|
||||
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(address, (byte)1);
|
||||
value = Wire.read();
|
||||
Wire.endTransmission();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Reads the 3 gyro channels and stores them in vector g
|
||||
void L3G::read()
|
||||
{
|
||||
measure();
|
||||
apply_offset();
|
||||
g = g * (0.07 * 3.14159265 / 180);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Private Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
void L3G::measure()
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
// assert the MSB of the address to get the gyro
|
||||
// to do slave-transmit subaddress updating.
|
||||
Wire.write(L3G_OUT_X_L | (1 << 7));
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(address, (byte)6);
|
||||
|
||||
while (Wire.available() < 6);
|
||||
|
||||
uint8_t xlg = Wire.read();
|
||||
uint8_t xhg = Wire.read();
|
||||
uint8_t ylg = Wire.read();
|
||||
uint8_t yhg = Wire.read();
|
||||
uint8_t zlg = Wire.read();
|
||||
uint8_t zhg = Wire.read();
|
||||
|
||||
// combine high and low bytes
|
||||
int16_t gx = (int16_t)(xhg << 8 | xlg);
|
||||
int16_t gy = (int16_t)(yhg << 8 | ylg);
|
||||
int16_t gz = (int16_t)(zhg << 8 | zlg);
|
||||
|
||||
g.x() = gx;
|
||||
g.y() = gy;
|
||||
g.z() = gz;
|
||||
}
|
||||
|
||||
void L3G::apply_offset()
|
||||
{
|
||||
g = g - gyro_offset;
|
||||
}
|
||||
|
||||
void L3G::measureOffsets()
|
||||
{
|
||||
const int sampleCount = 32;
|
||||
for(int i = 0; i < sampleCount-1; i++)
|
||||
{
|
||||
measure();
|
||||
gyro_offset = gyro_offset + g;
|
||||
delay(20);
|
||||
}
|
||||
|
||||
gyro_offset = gyro_offset / sampleCount;
|
||||
}
|
||||
|
||||
|
||||
bool L3G::autoDetectAddress(void)
|
||||
{
|
||||
// try each possible address and stop if reading WHO_AM_I returns the expected response
|
||||
address = L3G4200D_ADDRESS_SA0_LOW;
|
||||
if (readReg(L3G_WHO_AM_I) == 0xD3) return true;
|
||||
address = L3G4200D_ADDRESS_SA0_HIGH;
|
||||
if (readReg(L3G_WHO_AM_I) == 0xD3) return true;
|
||||
address = L3GD20_ADDRESS_SA0_LOW;
|
||||
if (readReg(L3G_WHO_AM_I) == 0xD4) return true;
|
||||
address = L3GD20_ADDRESS_SA0_HIGH;
|
||||
if (readReg(L3G_WHO_AM_I) == 0xD4) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
83
L3G.h
83
L3G.h
|
|
@ -1,83 +0,0 @@
|
|||
#ifndef L3G_h
|
||||
#define L3G_h
|
||||
|
||||
#include <Arduino.h> // for byte data type
|
||||
#include "imuMaths.h"
|
||||
|
||||
// device types
|
||||
|
||||
#define L3G_DEVICE_AUTO 0
|
||||
#define L3G4200D_DEVICE 1
|
||||
#define L3GD20_DEVICE 2
|
||||
|
||||
|
||||
// SA0 states
|
||||
|
||||
#define L3G_SA0_LOW 0
|
||||
#define L3G_SA0_HIGH 1
|
||||
#define L3G_SA0_AUTO 2
|
||||
|
||||
// register addresses
|
||||
|
||||
#define L3G_WHO_AM_I 0x0F
|
||||
|
||||
#define L3G_CTRL_REG1 0x20
|
||||
#define L3G_CTRL_REG2 0x21
|
||||
#define L3G_CTRL_REG3 0x22
|
||||
#define L3G_CTRL_REG4 0x23
|
||||
#define L3G_CTRL_REG5 0x24
|
||||
#define L3G_REFERENCE 0x25
|
||||
#define L3G_OUT_TEMP 0x26
|
||||
#define L3G_STATUS_REG 0x27
|
||||
|
||||
#define L3G_OUT_X_L 0x28
|
||||
#define L3G_OUT_X_H 0x29
|
||||
#define L3G_OUT_Y_L 0x2A
|
||||
#define L3G_OUT_Y_H 0x2B
|
||||
#define L3G_OUT_Z_L 0x2C
|
||||
#define L3G_OUT_Z_H 0x2D
|
||||
|
||||
#define L3G_FIFO_CTRL_REG 0x2E
|
||||
#define L3G_FIFO_SRC_REG 0x2F
|
||||
|
||||
#define L3G_INT1_CFG 0x30
|
||||
#define L3G_INT1_SRC 0x31
|
||||
#define L3G_INT1_THS_XH 0x32
|
||||
#define L3G_INT1_THS_XL 0x33
|
||||
#define L3G_INT1_THS_YH 0x34
|
||||
#define L3G_INT1_THS_YL 0x35
|
||||
#define L3G_INT1_THS_ZH 0x36
|
||||
#define L3G_INT1_THS_ZL 0x37
|
||||
#define L3G_INT1_DURATION 0x38
|
||||
|
||||
|
||||
class L3G
|
||||
{
|
||||
public:
|
||||
imu::Vector g; // gyro angular velocity readings
|
||||
|
||||
bool init(byte device = L3G_DEVICE_AUTO, byte sa0 = L3G_SA0_AUTO);
|
||||
|
||||
void enableDefault(void);
|
||||
|
||||
void writeReg(byte reg, byte value);
|
||||
byte readReg(byte reg);
|
||||
|
||||
void read(void);
|
||||
void measureOffsets();
|
||||
|
||||
private:
|
||||
imu::Vector gyro_offset;
|
||||
byte _device; // chip type (4200D or D20)
|
||||
byte address;
|
||||
|
||||
void measure();
|
||||
void apply_offset();
|
||||
|
||||
bool autoDetectAddress(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
279
LSM303.cpp
279
LSM303.cpp
|
|
@ -1,279 +0,0 @@
|
|||
#include "LSM303.h"
|
||||
#include <Wire.h>
|
||||
#include <math.h>
|
||||
|
||||
// Defines ////////////////////////////////////////////////////////////////
|
||||
|
||||
// The Arduino two-wire interface uses a 7-bit number for the address,
|
||||
// and sets the last bit correctly based on reads and writes
|
||||
#define MAG_ADDRESS (0x3C >> 1)
|
||||
#define ACC_ADDRESS_SA0_A_LOW (0x30 >> 1)
|
||||
#define ACC_ADDRESS_SA0_A_HIGH (0x32 >> 1)
|
||||
|
||||
// Constructors ////////////////////////////////////////////////////////////////
|
||||
|
||||
LSM303::LSM303(void)
|
||||
{
|
||||
// These are just some values for a particular unit; it is recommended that
|
||||
// a calibration be done for your particular unit.
|
||||
m_max.x() = +540;
|
||||
m_max.y() = +500;
|
||||
m_max.z() = 180;
|
||||
|
||||
m_min.x() = -520;
|
||||
m_min.y() = -570;
|
||||
m_min.z() = -770;
|
||||
|
||||
_device = LSM303_DEVICE_AUTO;
|
||||
acc_address = ACC_ADDRESS_SA0_A_LOW;
|
||||
|
||||
io_timeout = 0; // 0 = no timeout
|
||||
did_timeout = false;
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
bool LSM303::timeoutOccurred()
|
||||
{
|
||||
return did_timeout;
|
||||
}
|
||||
|
||||
void LSM303::setTimeout(unsigned int timeout)
|
||||
{
|
||||
io_timeout = timeout;
|
||||
}
|
||||
|
||||
unsigned int LSM303::getTimeout()
|
||||
{
|
||||
return io_timeout;
|
||||
}
|
||||
|
||||
void LSM303::init(byte device, byte sa0_a)
|
||||
{
|
||||
_device = device;
|
||||
switch (_device)
|
||||
{
|
||||
case LSM303DLH_DEVICE:
|
||||
case LSM303DLM_DEVICE:
|
||||
if (sa0_a == LSM303_SA0_A_LOW)
|
||||
acc_address = ACC_ADDRESS_SA0_A_LOW;
|
||||
else if (sa0_a == LSM303_SA0_A_HIGH)
|
||||
acc_address = ACC_ADDRESS_SA0_A_HIGH;
|
||||
else
|
||||
acc_address = (detectSA0_A() == LSM303_SA0_A_HIGH) ? ACC_ADDRESS_SA0_A_HIGH : ACC_ADDRESS_SA0_A_LOW;
|
||||
break;
|
||||
|
||||
case LSM303DLHC_DEVICE:
|
||||
acc_address = ACC_ADDRESS_SA0_A_HIGH;
|
||||
break;
|
||||
|
||||
default:
|
||||
// try to auto-detect device
|
||||
if (detectSA0_A() == LSM303_SA0_A_HIGH)
|
||||
{
|
||||
// if device responds on 0011001b (SA0_A is high), assume DLHC
|
||||
acc_address = ACC_ADDRESS_SA0_A_HIGH;
|
||||
_device = LSM303DLHC_DEVICE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, assume DLH or DLM (pulled low by default on Pololu boards); query magnetometer WHO_AM_I to differentiate these two
|
||||
acc_address = ACC_ADDRESS_SA0_A_LOW;
|
||||
_device = (readMagReg(LSM303_WHO_AM_I_M) == 0x3C) ? LSM303DLM_DEVICE : LSM303DLH_DEVICE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turns on the LSM303's accelerometer and magnetometers and places them in normal
|
||||
// mode.
|
||||
void LSM303::enableDefault(void)
|
||||
{
|
||||
if (_device == LSM303DLHC_DEVICE)
|
||||
{
|
||||
writeAccReg(LSM303_CTRL_REG1_A, 0b01000111); // Normal power mode, all axes enabled, 50 Hz
|
||||
writeAccReg(LSM303_CTRL_REG4_A, 0x28); // 8 g full scale: FS = 10 on DLHC, high resolution output mode
|
||||
}
|
||||
else
|
||||
{
|
||||
writeAccReg(LSM303_CTRL_REG1_A, 0b00100111); // normal power mode, all axes enabled, 50 Hz
|
||||
writeAccReg(LSM303_CTRL_REG4_A, 0b00110000); // 8 g full scale: FS = 11 on DLH, DLM
|
||||
}
|
||||
|
||||
// Enable Magnetometer
|
||||
// 0x00 = 0b00000000
|
||||
// Continuous conversion mode
|
||||
writeMagReg(LSM303_MR_REG_M, 0x00);
|
||||
}
|
||||
|
||||
// Writes an accelerometer register
|
||||
void LSM303::writeAccReg(byte reg, byte value)
|
||||
{
|
||||
Wire.beginTransmission(acc_address);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
last_status = Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Reads an accelerometer register
|
||||
byte LSM303::readAccReg(byte reg)
|
||||
{
|
||||
byte value;
|
||||
|
||||
Wire.beginTransmission(acc_address);
|
||||
Wire.write(reg);
|
||||
last_status = Wire.endTransmission();
|
||||
Wire.requestFrom(acc_address, (byte)1);
|
||||
value = Wire.read();
|
||||
Wire.endTransmission();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Writes a magnetometer register
|
||||
void LSM303::writeMagReg(byte reg, byte value)
|
||||
{
|
||||
Wire.beginTransmission(MAG_ADDRESS);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
last_status = Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Reads a magnetometer register
|
||||
byte LSM303::readMagReg(int reg)
|
||||
{
|
||||
byte value;
|
||||
|
||||
// if dummy register address (magnetometer Y/Z), use device type to determine actual address
|
||||
if (reg < 0)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case LSM303_OUT_Y_H_M:
|
||||
reg = (_device == LSM303DLH_DEVICE) ? LSM303DLH_OUT_Y_H_M : LSM303DLM_OUT_Y_H_M;
|
||||
break;
|
||||
case LSM303_OUT_Y_L_M:
|
||||
reg = (_device == LSM303DLH_DEVICE) ? LSM303DLH_OUT_Y_L_M : LSM303DLM_OUT_Y_L_M;
|
||||
break;
|
||||
case LSM303_OUT_Z_H_M:
|
||||
reg = (_device == LSM303DLH_DEVICE) ? LSM303DLH_OUT_Z_H_M : LSM303DLM_OUT_Z_H_M;
|
||||
break;
|
||||
case LSM303_OUT_Z_L_M:
|
||||
reg = (_device == LSM303DLH_DEVICE) ? LSM303DLH_OUT_Z_L_M : LSM303DLM_OUT_Z_L_M;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Wire.beginTransmission(MAG_ADDRESS);
|
||||
Wire.write(reg);
|
||||
last_status = Wire.endTransmission();
|
||||
Wire.requestFrom(MAG_ADDRESS, 1);
|
||||
value = Wire.read();
|
||||
Wire.endTransmission();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void LSM303::setMagGain(magGain value)
|
||||
{
|
||||
Wire.beginTransmission(MAG_ADDRESS);
|
||||
Wire.write(LSM303_CRB_REG_M);
|
||||
Wire.write((byte) value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Reads the 3 accelerometer channels and stores them in vector a
|
||||
void LSM303::readAcc(void)
|
||||
{
|
||||
Wire.beginTransmission(acc_address);
|
||||
// assert the MSB of the address to get the accelerometer
|
||||
// to do slave-transmit subaddress updating.
|
||||
Wire.write(LSM303_OUT_X_L_A | (1 << 7));
|
||||
last_status = Wire.endTransmission();
|
||||
Wire.requestFrom(acc_address, (byte)6);
|
||||
|
||||
unsigned int millis_start = millis();
|
||||
did_timeout = false;
|
||||
while (Wire.available() < 6) {
|
||||
if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout) {
|
||||
did_timeout = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte xla = Wire.read();
|
||||
byte xha = Wire.read();
|
||||
byte yla = Wire.read();
|
||||
byte yha = Wire.read();
|
||||
byte zla = Wire.read();
|
||||
byte zha = Wire.read();
|
||||
|
||||
// combine high and low bytes, then shift right to discard lowest 4 bits (which are meaningless)
|
||||
// GCC performs an arithmetic right shift for signed negative numbers, but this code will not work
|
||||
// if you port it to a compiler that does a logical right shift instead.
|
||||
a.x() = ((int16_t)(xha << 8 | xla)) >> 4;
|
||||
a.y() = ((int16_t)(yha << 8 | yla)) >> 4;
|
||||
a.z() = ((int16_t)(zha << 8 | zla)) >> 4;
|
||||
|
||||
const float accel_scale = 0.0039;
|
||||
a = a*accel_scale;
|
||||
}
|
||||
|
||||
// Reads the 3 magnetometer channels and stores them in vector m
|
||||
void LSM303::readMag(void)
|
||||
{
|
||||
Wire.beginTransmission(MAG_ADDRESS);
|
||||
Wire.write(LSM303_OUT_X_H_M);
|
||||
last_status = Wire.endTransmission();
|
||||
Wire.requestFrom(MAG_ADDRESS, 6);
|
||||
|
||||
unsigned int millis_start = millis();
|
||||
did_timeout = false;
|
||||
while (Wire.available() < 6) {
|
||||
if (io_timeout > 0 && ((unsigned int)millis() - millis_start) > io_timeout) {
|
||||
did_timeout = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte xhm = Wire.read();
|
||||
byte xlm = Wire.read();
|
||||
|
||||
byte yhm, ylm, zhm, zlm;
|
||||
|
||||
|
||||
// DLM, DLHC: register address for Z comes before Y
|
||||
zhm = Wire.read();
|
||||
zlm = Wire.read();
|
||||
yhm = Wire.read();
|
||||
ylm = Wire.read();
|
||||
|
||||
|
||||
// combine high and low bytes
|
||||
m.x() = (int16_t)(xhm << 8 | xlm);
|
||||
m.y() = (int16_t)(yhm << 8 | ylm);
|
||||
m.z() = (int16_t)(zhm << 8 | zlm);
|
||||
}
|
||||
|
||||
// Reads all 6 channels of the LSM303 and stores them in the object variables
|
||||
void LSM303::read(void)
|
||||
{
|
||||
readAcc();
|
||||
readMag();
|
||||
}
|
||||
|
||||
// Private Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
byte LSM303::detectSA0_A(void)
|
||||
{
|
||||
Wire.beginTransmission(ACC_ADDRESS_SA0_A_LOW);
|
||||
Wire.write(LSM303_CTRL_REG1_A);
|
||||
last_status = Wire.endTransmission();
|
||||
Wire.requestFrom(ACC_ADDRESS_SA0_A_LOW, 1);
|
||||
if (Wire.available())
|
||||
{
|
||||
Wire.read();
|
||||
return LSM303_SA0_A_LOW;
|
||||
}
|
||||
else
|
||||
return LSM303_SA0_A_HIGH;
|
||||
}
|
||||
153
LSM303.h
153
LSM303.h
|
|
@ -1,153 +0,0 @@
|
|||
#ifndef LSM303_h
|
||||
#define LSM303_h
|
||||
|
||||
#include <Arduino.h> // for byte data type
|
||||
#include "imuMaths.h"
|
||||
// device types
|
||||
|
||||
#define LSM303DLH_DEVICE 0
|
||||
#define LSM303DLM_DEVICE 1
|
||||
#define LSM303DLHC_DEVICE 2
|
||||
#define LSM303_DEVICE_AUTO 3
|
||||
|
||||
// SA0_A states
|
||||
|
||||
#define LSM303_SA0_A_LOW 0
|
||||
#define LSM303_SA0_A_HIGH 1
|
||||
#define LSM303_SA0_A_AUTO 2
|
||||
|
||||
// register addresses
|
||||
|
||||
#define LSM303_CTRL_REG1_A 0x20
|
||||
#define LSM303_CTRL_REG2_A 0x21
|
||||
#define LSM303_CTRL_REG3_A 0x22
|
||||
#define LSM303_CTRL_REG4_A 0x23
|
||||
#define LSM303_CTRL_REG5_A 0x24
|
||||
#define LSM303_CTRL_REG6_A 0x25 // DLHC only
|
||||
#define LSM303_HP_FILTER_RESET_A 0x25 // DLH, DLM only
|
||||
#define LSM303_REFERENCE_A 0x26
|
||||
#define LSM303_STATUS_REG_A 0x27
|
||||
|
||||
#define LSM303_OUT_X_L_A 0x28
|
||||
#define LSM303_OUT_X_H_A 0x29
|
||||
#define LSM303_OUT_Y_L_A 0x2A
|
||||
#define LSM303_OUT_Y_H_A 0x2B
|
||||
#define LSM303_OUT_Z_L_A 0x2C
|
||||
#define LSM303_OUT_Z_H_A 0x2D
|
||||
|
||||
#define LSM303_FIFO_CTRL_REG_A 0x2E // DLHC only
|
||||
#define LSM303_FIFO_SRC_REG_A 0x2F // DLHC only
|
||||
|
||||
#define LSM303_INT1_CFG_A 0x30
|
||||
#define LSM303_INT1_SRC_A 0x31
|
||||
#define LSM303_INT1_THS_A 0x32
|
||||
#define LSM303_INT1_DURATION_A 0x33
|
||||
#define LSM303_INT2_CFG_A 0x34
|
||||
#define LSM303_INT2_SRC_A 0x35
|
||||
#define LSM303_INT2_THS_A 0x36
|
||||
#define LSM303_INT2_DURATION_A 0x37
|
||||
|
||||
#define LSM303_CLICK_CFG_A 0x38 // DLHC only
|
||||
#define LSM303_CLICK_SRC_A 0x39 // DLHC only
|
||||
#define LSM303_CLICK_THS_A 0x3A // DLHC only
|
||||
#define LSM303_TIME_LIMIT_A 0x3B // DLHC only
|
||||
#define LSM303_TIME_LATENCY_A 0x3C // DLHC only
|
||||
#define LSM303_TIME_WINDOW_A 0x3D // DLHC only
|
||||
|
||||
#define LSM303_CRA_REG_M 0x00
|
||||
#define LSM303_CRB_REG_M 0x01
|
||||
#define LSM303_MR_REG_M 0x02
|
||||
|
||||
#define LSM303_OUT_X_H_M 0x03
|
||||
#define LSM303_OUT_X_L_M 0x04
|
||||
#define LSM303_OUT_Y_H_M -1 // The addresses of the Y and Z magnetometer output registers
|
||||
#define LSM303_OUT_Y_L_M -2 // are reversed on the DLM and DLHC relative to the DLH.
|
||||
#define LSM303_OUT_Z_H_M -3 // These four defines have dummy values so the library can
|
||||
#define LSM303_OUT_Z_L_M -4 // determine the correct address based on the device type.
|
||||
|
||||
#define LSM303_SR_REG_M 0x09
|
||||
#define LSM303_IRA_REG_M 0x0A
|
||||
#define LSM303_IRB_REG_M 0x0B
|
||||
#define LSM303_IRC_REG_M 0x0C
|
||||
|
||||
#define LSM303_WHO_AM_I_M 0x0F // DLM only
|
||||
|
||||
#define LSM303_TEMP_OUT_H_M 0x31 // DLHC only
|
||||
#define LSM303_TEMP_OUT_L_M 0x32 // DLHC only
|
||||
|
||||
#define LSM303DLH_OUT_Y_H_M 0x05
|
||||
#define LSM303DLH_OUT_Y_L_M 0x06
|
||||
#define LSM303DLH_OUT_Z_H_M 0x07
|
||||
#define LSM303DLH_OUT_Z_L_M 0x08
|
||||
|
||||
#define LSM303DLM_OUT_Z_H_M 0x05
|
||||
#define LSM303DLM_OUT_Z_L_M 0x06
|
||||
#define LSM303DLM_OUT_Y_H_M 0x07
|
||||
#define LSM303DLM_OUT_Y_L_M 0x08
|
||||
|
||||
#define LSM303DLHC_OUT_Z_H_M 0x05
|
||||
#define LSM303DLHC_OUT_Z_L_M 0x06
|
||||
#define LSM303DLHC_OUT_Y_H_M 0x07
|
||||
#define LSM303DLHC_OUT_Y_L_M 0x08
|
||||
|
||||
class LSM303
|
||||
{
|
||||
public:
|
||||
|
||||
imu::Vector a; // accelerometer readings
|
||||
imu::Vector m; // magnetometer readings
|
||||
imu::Vector m_max; // maximum magnetometer values, used for calibration
|
||||
imu::Vector m_min; // minimum magnetometer values, used for calibration
|
||||
|
||||
byte last_status; // status of last I2C transmission
|
||||
|
||||
// HEX = BIN RANGE GAIN X/Y/Z GAIN Z
|
||||
// DLH (DLM/DLHC) DLH (DLM/DLHC)
|
||||
// 0x20 = 0b00100000 ±1.3 1055 (1100) 950 (980) (default)
|
||||
// 0x40 = 0b01000000 ±1.9 795 (855) 710 (760)
|
||||
// 0x60 = 0b01100000 ±2.5 635 (670) 570 (600)
|
||||
// 0x80 = 0b10000000 ±4.0 430 (450) 385 (400)
|
||||
// 0xA0 = 0b10100000 ±4.7 375 (400) 335 (355)
|
||||
// 0xC0 = 0b11000000 ±5.6 320 (330) 285 (295)
|
||||
// 0xE0 = 0b11100000 ±8.1 230 (230) 205 (205)
|
||||
enum magGain { magGain_13 = 0x20, magGain_19 = 0x40, magGain_25 = 0x60, magGain_40 = 0x80,
|
||||
magGain_47 = 0xA0, magGain_56 = 0xC0, magGain_81 = 0xE0
|
||||
};
|
||||
|
||||
LSM303(void);
|
||||
|
||||
void init(byte device = LSM303_DEVICE_AUTO, byte sa0_a = LSM303_SA0_A_AUTO);
|
||||
byte getDeviceType(void) {
|
||||
return _device;
|
||||
}
|
||||
|
||||
void enableDefault(void);
|
||||
|
||||
void writeAccReg(byte reg, byte value);
|
||||
byte readAccReg(byte reg);
|
||||
void writeMagReg(byte reg, byte value);
|
||||
byte readMagReg(int reg);
|
||||
|
||||
void setMagGain(magGain value);
|
||||
|
||||
void readAcc(void);
|
||||
void readMag(void);
|
||||
void read(void);
|
||||
|
||||
void setTimeout(unsigned int timeout);
|
||||
unsigned int getTimeout(void);
|
||||
bool timeoutOccurred(void);
|
||||
|
||||
private:
|
||||
byte _device; // chip type (DLH, DLM, or DLHC)
|
||||
byte acc_address;
|
||||
unsigned int io_timeout;
|
||||
bool did_timeout;
|
||||
|
||||
byte detectSA0_A(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
14
README.md
14
README.md
|
|
@ -1,14 +0,0 @@
|
|||
Adafruit_AHRS
|
||||
=============
|
||||
|
||||
AHRS (Altitude and Heading Reference System) example for Adafruit's 9DOF and 10DOF breakouts.
|
||||
|
||||
This code is based on the GPL licensed AHRS code from Firetail UAV Systems, available at: http://www.camelsoftware.com/firetail/blog/uncategorized/quaternion-based-ahrs-using-altimu-10-arduino/, which was itself based on the work of David Grayson at http://blog.davidegrayson.com/2012/11/orientation-sensing-with-raspberry-pi.html
|
||||
|
||||
Using This Code
|
||||
===============
|
||||
This sample code can produce Euler angle, North-East-Down (NED) direction cosine matrix, or Quaternion output using the following commands:
|
||||
|
||||
- “output_euler\n” : Switches the output to euler angles (heading, pitch & roll in degrees). This is the default output.
|
||||
- “output_mat\n” : Switches output to a north-east-down (NED) direction cosine matrix.
|
||||
- “output_quat\n” : Switches output to quaternion.
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
/*
|
||||
AltIMU-10 Quaternion based AHRS
|
||||
www.camelsoftware.com/firetail
|
||||
Copyright (C) 2013 Samuel Cowen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Set the baud rate to 115200 to use this sketch, and adjust the
|
||||
output units by entering one of the following commands in the
|
||||
Serial Monitor and clicking the "send" button:
|
||||
|
||||
"output_euler" - Euler angle output (default value)
|
||||
"output_mat" - NED cosine matrix output
|
||||
"output_quat" - Quaternion output
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <EEPROM.h>
|
||||
#include "L3G.h"
|
||||
#include "LSM303.h"
|
||||
#include "imuMaths.h"
|
||||
|
||||
#define OUTPUT_EULER 0 // heading, pitch, roll
|
||||
#define OUTPUT_MATRIX 1 // a north-east-down rotation matrix
|
||||
#define OUTPUT_QUATERNION 2 // a quaternion!
|
||||
|
||||
L3G l3g;
|
||||
LSM303 lsm303;
|
||||
imu::Quaternion rotation_buf;
|
||||
imu::Quaternion offset;
|
||||
|
||||
long delta_t;
|
||||
|
||||
bool warmup = true;
|
||||
long start_millis;
|
||||
|
||||
//float alt = 0;
|
||||
//float temp = 0;
|
||||
|
||||
int output_mode = OUTPUT_EULER;
|
||||
bool auto_output = true;
|
||||
|
||||
int mag_cal_counter;
|
||||
|
||||
int write_offset(imu::Quaternion* value)
|
||||
{
|
||||
const byte* p = (const byte*)value;
|
||||
int i;
|
||||
for (i = 0; i < sizeof(imu::Quaternion); i++)
|
||||
EEPROM.write(i, *p++);
|
||||
return i;
|
||||
}
|
||||
|
||||
int read_offset(imu::Quaternion* value)
|
||||
{
|
||||
byte* p = (byte*)value;
|
||||
int i;
|
||||
for (i = 0; i < sizeof(imu::Quaternion); i++)
|
||||
*p++ = EEPROM.read(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
int write_mag_cal(imu::Vector min, imu::Vector max)
|
||||
{
|
||||
byte* p = (byte*)&min;
|
||||
int i;
|
||||
int ret;
|
||||
int pos = sizeof(imu::Quaternion);
|
||||
for(i = pos; i < pos + sizeof(imu::Vector); i++)
|
||||
EEPROM.write(i, *p++);
|
||||
ret = i;
|
||||
|
||||
p = (byte*)&max;
|
||||
pos = sizeof(imu::Quaternion) + sizeof(imu::Vector);
|
||||
for(i = pos; i < pos + sizeof(imu::Vector); i++)
|
||||
EEPROM.write(i, *p++);
|
||||
|
||||
return i + ret;
|
||||
}
|
||||
|
||||
int read_mag_cal(imu::Vector* min, imu::Vector* max)
|
||||
{
|
||||
byte* p = (byte*)min;
|
||||
int i;
|
||||
int ret;
|
||||
int pos = sizeof(imu::Quaternion);
|
||||
for(i = pos; i < pos + sizeof(imu::Vector); i++)
|
||||
*p++ = EEPROM.read(i);
|
||||
ret = i;
|
||||
|
||||
p = (byte*)&max;
|
||||
pos = sizeof(imu::Quaternion) + sizeof(imu::Vector);
|
||||
for(i = pos; i < pos + sizeof(imu::Vector); i++)
|
||||
*p++ = EEPROM.read(i);
|
||||
|
||||
return i + ret;
|
||||
}
|
||||
|
||||
|
||||
void output(imu::Quaternion rotation)
|
||||
{
|
||||
switch(output_mode)
|
||||
{
|
||||
case OUTPUT_EULER:
|
||||
{
|
||||
imu::Vector euler;
|
||||
euler = rotation.toEuler();
|
||||
euler.toDegrees();
|
||||
|
||||
Serial.print(F("euler: "));
|
||||
Serial.print(euler.x());
|
||||
Serial.print(F(" "));
|
||||
Serial.print(euler.y());
|
||||
Serial.print(F(" "));
|
||||
Serial.println(euler.z());
|
||||
}
|
||||
break;
|
||||
case OUTPUT_MATRIX:
|
||||
{
|
||||
imu::Matrix m = rotation.toMatrix();
|
||||
Serial.print(F("matrix: "));
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i <= 2; i++)
|
||||
{
|
||||
for(j = 0; j <= 2; j++)
|
||||
{
|
||||
Serial.print(m.cell(i, j)); Serial.print(" ");
|
||||
}
|
||||
}
|
||||
Serial.print(F("\n"));
|
||||
}
|
||||
break;
|
||||
case OUTPUT_QUATERNION:
|
||||
{
|
||||
Serial.print(F("quat: "));
|
||||
Serial.print(rotation.w());
|
||||
Serial.print(F(" "));
|
||||
Serial.print(rotation.x());
|
||||
Serial.print(F(" "));
|
||||
Serial.print(rotation.y());
|
||||
Serial.print(F(" "));
|
||||
Serial.println(rotation.z());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Serial.print("alt: ");
|
||||
//Serial.println(alt);
|
||||
//Serial.print("temp: ");
|
||||
//Serial.println(temp);
|
||||
}
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.setTimeout(1);
|
||||
Serial.println(F("Adafruit 9/10DOF AHRS"));
|
||||
Serial.println(F("www.CamelSoftware.com"));
|
||||
delta_t = millis();
|
||||
|
||||
Wire.begin();
|
||||
l3g.init();
|
||||
l3g.enableDefault();
|
||||
l3g.measureOffsets();
|
||||
lsm303.init();
|
||||
lsm303.enableDefault();
|
||||
//lps331.init();
|
||||
//lps331.enableDefault();
|
||||
Serial.println(F("Done"));
|
||||
|
||||
mag_cal_counter = 0;
|
||||
|
||||
start_millis = millis();
|
||||
//read_offset(&offset);
|
||||
|
||||
if(abs(offset.magnitude() - 1) >= 0.1 || isnan(offset.magnitude())) //if installation offset isn't a valid unit quaternion
|
||||
{
|
||||
imu::Quaternion qz; //set it to a unit quaternion with no rotation (no offset)
|
||||
offset = qz;
|
||||
}
|
||||
|
||||
//read_mag_cal(&lsm303.m_min, &lsm303.m_max);
|
||||
|
||||
imu::Vector acc;
|
||||
imu::Vector mag;
|
||||
|
||||
lsm303.readAcc();
|
||||
lsm303.readMag();
|
||||
|
||||
acc = lsm303.a;
|
||||
mag = lsm303.m;
|
||||
|
||||
imu::Vector down = acc.invert();
|
||||
imu::Vector east = down.cross(mag);
|
||||
imu::Vector north = east.cross(down);
|
||||
|
||||
down.normalize();
|
||||
east.normalize();
|
||||
north.normalize();
|
||||
|
||||
imu::Matrix m;
|
||||
m.vector_to_row(north, 0);
|
||||
m.vector_to_row(east, 1);
|
||||
m.vector_to_row(down, 2);
|
||||
|
||||
rotation_buf.fromMatrix(m);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
double dt = millis() - delta_t;
|
||||
if(dt < 20)
|
||||
return;
|
||||
|
||||
dt /= 1000;
|
||||
if(warmup)
|
||||
{
|
||||
if((millis() - start_millis) > 200)
|
||||
{
|
||||
warmup = false;
|
||||
}
|
||||
}
|
||||
|
||||
delta_t = millis();
|
||||
|
||||
imu::Vector ang_vel;
|
||||
imu::Vector acc;
|
||||
imu::Vector mag;
|
||||
|
||||
l3g.read();
|
||||
|
||||
ang_vel = l3g.g;
|
||||
|
||||
lsm303.readAcc();
|
||||
lsm303.readMag();
|
||||
|
||||
acc = lsm303.a;
|
||||
mag = lsm303.m;
|
||||
|
||||
if(mag_cal_counter) //if a mag cal is underway
|
||||
{
|
||||
mag_cal_counter++; //increment counter
|
||||
if(mag_cal_counter > 3000) //if counter has reached 3000, mag cal has been running for 60 seconds
|
||||
{
|
||||
mag_cal_counter = 0; //stop the mag cal
|
||||
write_mag_cal(lsm303.m_min, lsm303.m_max);
|
||||
}
|
||||
|
||||
if(mag.x() > lsm303.m_max.x())
|
||||
lsm303.m_max.x() = mag.x();
|
||||
if(mag.y() > lsm303.m_max.y())
|
||||
lsm303.m_max.y() = mag.y();
|
||||
if(mag.z() > lsm303.m_max.z())
|
||||
lsm303.m_max.z() = mag.z();
|
||||
|
||||
if(mag.x() < lsm303.m_min.x())
|
||||
lsm303.m_max.x() = mag.x();
|
||||
if(mag.y() < lsm303.m_min.y())
|
||||
lsm303.m_max.y() = mag.y();
|
||||
if(mag.z() < lsm303.m_min.z())
|
||||
lsm303.m_max.z() = mag.z();
|
||||
}
|
||||
|
||||
|
||||
imu::Vector correction;
|
||||
|
||||
if(abs(acc.magnitude()-1) <= 0.15)
|
||||
{
|
||||
float correction_strength = 0.0005;
|
||||
if(warmup)
|
||||
correction_strength = 0.01;
|
||||
|
||||
imu::Vector down = acc.invert();
|
||||
imu::Vector east = down.cross(mag);
|
||||
imu::Vector north = east.cross(down);
|
||||
|
||||
down.normalize();
|
||||
east.normalize();
|
||||
north.normalize();
|
||||
|
||||
imu::Matrix rotationMatrix = rotation_buf.toMatrix();
|
||||
correction = (
|
||||
north.cross(rotationMatrix.row_to_vector(0)) +
|
||||
east.cross(rotationMatrix.row_to_vector(1)) +
|
||||
down.cross(rotationMatrix.row_to_vector(2))
|
||||
) * correction_strength;
|
||||
}
|
||||
|
||||
imu::Vector w;
|
||||
w = ang_vel + correction;
|
||||
|
||||
imu::Quaternion q(1, w.x()*dt/2.0, w.y()*dt/2.0, w.z()*dt/2.0);
|
||||
|
||||
rotation_buf = rotation_buf*q;
|
||||
rotation_buf.normalize();
|
||||
|
||||
imu::Quaternion rotation = offset.conjugate()*rotation_buf;
|
||||
rotation.normalize();
|
||||
|
||||
//alt = (0.1 * lps331.pressureToAltitudeFeet(lps331.readPressureInchesHg())) + (0.9) * alt;
|
||||
//temp = (0.1 * lps331.readTemperatureC()) + (0.9) * temp;
|
||||
|
||||
if(auto_output)
|
||||
output(rotation);
|
||||
|
||||
|
||||
char buffer[20];
|
||||
memset(buffer, '\0', 20);
|
||||
Serial.readBytesUntil('\n', buffer, 20);
|
||||
|
||||
if(strncmp(buffer, "output_euler", 12) == 0)
|
||||
output_mode= OUTPUT_EULER;
|
||||
|
||||
if(strncmp(buffer, "output_mat", 11) == 0)
|
||||
output_mode = OUTPUT_MATRIX;
|
||||
|
||||
if(strncmp(buffer, "output_quat", 12) == 0)
|
||||
output_mode = OUTPUT_QUATERNION;
|
||||
|
||||
if(strncmp(buffer, "auto_on", 7) == 0)
|
||||
auto_output = true;
|
||||
|
||||
if(strncmp(buffer, "auto_off", 8) == 0)
|
||||
auto_output = false;
|
||||
|
||||
if(strncmp(buffer, "read", 4) == 0)
|
||||
output(rotation);
|
||||
|
||||
|
||||
if(strncmp(buffer, "set_offset", 6) == 0)
|
||||
{
|
||||
offset = rotation_buf;
|
||||
write_offset(&offset);
|
||||
}
|
||||
if(strncmp(buffer, "mag_cal", 7) == 0)
|
||||
{
|
||||
mag_cal_counter = 1;
|
||||
lsm303.m_max = imu::Vector();
|
||||
lsm303.m_min = imu::Vector();
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,97 +0,0 @@
|
|||
import processing.serial.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.Toolkit;
|
||||
import processing.opengl.*;
|
||||
import saito.objloader.*;
|
||||
|
||||
float roll = 0.0F; // Drawn along X axis in Processing
|
||||
float pitch = 0.0F; // Drawn along Z axis in Processing
|
||||
float yaw = 0.0F; // Drawn along Y axis in Processing
|
||||
|
||||
OBJModel model;
|
||||
Serial port;
|
||||
String buffer = "";
|
||||
|
||||
void setup()
|
||||
{
|
||||
size( 400, 400, OPENGL);
|
||||
frameRate(30);
|
||||
model = new OBJModel(this);
|
||||
model.load("airplane.obj");
|
||||
model.scale(3.5);
|
||||
|
||||
// ToDo: Check for errors, this will fail with no serial device present
|
||||
String ttyPort = Serial.list()[0];
|
||||
port = new Serial(this, ttyPort, 115200);
|
||||
port.bufferUntil('\n');
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
background(128, 128, 128);
|
||||
|
||||
// Set a new co-ordinate space
|
||||
pushMatrix();
|
||||
|
||||
// Get some light in here
|
||||
lights();
|
||||
|
||||
// Displace objects from 0,0
|
||||
translate(200, 200, 0);
|
||||
|
||||
// Rotate shapes around the X/Y/Z axis (values in radians, 0..Pi*2)
|
||||
rotateX(radians(roll));
|
||||
rotateZ(radians(pitch));
|
||||
rotateY(radians(yaw));
|
||||
|
||||
pushMatrix();
|
||||
rotateY(PI/2);
|
||||
noStroke();
|
||||
model.draw();
|
||||
popMatrix();
|
||||
|
||||
// Roll Axis (X in Processing)
|
||||
stroke(255, 0, 0);
|
||||
box(250, 1, 1);
|
||||
text("Roll (X)", 85, -10, 0);
|
||||
|
||||
// Pitch Axis (Z in Processing)
|
||||
stroke(0, 255, 0);
|
||||
box(1, 1, 200);
|
||||
text("Pitch (Y)", -25, -10, 100);
|
||||
|
||||
// Yaw Axis (Y in Processing)
|
||||
stroke(0, 0, 255);
|
||||
box(1, 50, 1);
|
||||
text("Yaw (Z)", -25, -35, 0);
|
||||
|
||||
// Render legend in normal XYZ space
|
||||
popMatrix();
|
||||
stroke(255);
|
||||
text("Roll (X):", 10, 20);
|
||||
text(roll, 75, 20);
|
||||
text("Pitch (Y):", 10, 35);
|
||||
text(pitch, 75, 35);
|
||||
text("Yaw (Z):", 10, 50);
|
||||
text(yaw, 75, 50);
|
||||
|
||||
// Render incoming text from the Arduino sketch
|
||||
text("Incoming Data: " + buffer, 10, 385);
|
||||
}
|
||||
|
||||
void serialEvent(Serial p)
|
||||
{
|
||||
String incoming = p.readString();
|
||||
if ((incoming.length() > 8))
|
||||
{
|
||||
String[] list = split(incoming, " ");
|
||||
if ( (list.length > 0) && (list[0].equals("euler:")) )
|
||||
{
|
||||
roll = float(list[1]);
|
||||
pitch = float(list[2]);
|
||||
yaw = float(list[3]);
|
||||
buffer = incoming;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#
|
||||
## Alias OBJ Material File
|
||||
# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited
|
||||
|
||||
newmtl airplane_01_Body1
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.254902 0.278431 0.286275
|
||||
Ks 0.330000 0.330000 0.330000
|
||||
map_Kd airplane/airplane_01_Body1.jpg
|
||||
|
||||
newmtl FrontColor
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 1.000000 1.000000 1.000000
|
||||
Ks 0.330000 0.330000 0.330000
|
||||
|
||||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
Before Width: | Height: | Size: 504 KiB |
384
imuMaths.cpp
384
imuMaths.cpp
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
AltIMU-10 Quaternion based AHRS
|
||||
www.camelsoftware.com/firetail
|
||||
Copyright (C) 2013 Samuel Cowen
|
||||
|
||||
The latest version of the IMU Maths library will always be available at
|
||||
http://sourceforge.net/p/firetail/code/ci/master/tree/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "imuMaths.h"
|
||||
|
||||
using namespace imu;
|
||||
|
||||
double max(double a, double b)
|
||||
{
|
||||
if(a >= b)
|
||||
return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
Vector::Vector()
|
||||
{
|
||||
_x = _y = _z = 0;
|
||||
}
|
||||
|
||||
Vector::Vector(double a, double b, double c)
|
||||
{
|
||||
_x = a;
|
||||
_y = b;
|
||||
_z = c;
|
||||
}
|
||||
|
||||
double Vector::magnitude()
|
||||
{
|
||||
double res = (_x*_x) + (_y*_y) + (_z*_z);
|
||||
if((res > 1.001) || (res < 0.999))
|
||||
return sqrt(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void Vector::normalize()
|
||||
{
|
||||
double mag = magnitude();
|
||||
if(mag != 0)
|
||||
{
|
||||
_x = _x/mag;
|
||||
_y = _y/mag;
|
||||
_z = _z/mag;
|
||||
}
|
||||
}
|
||||
|
||||
Vector Vector::cross(Vector vec)
|
||||
{
|
||||
Vector ret;
|
||||
ret._x = (_y * vec._z) - (_z * vec._y);
|
||||
ret._y = (_z * vec._x) - (_x * vec._z);
|
||||
ret._z = (_x * vec._y) - (_y * vec._x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Vector::scale(double scalar)
|
||||
{
|
||||
Vector ret;
|
||||
ret.x() = _x * scalar;
|
||||
ret.y() = _y * scalar;
|
||||
ret.z() = _z * scalar;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Vector::toDegrees()
|
||||
{
|
||||
_x *= 180/M_PI;
|
||||
_y *= 180/M_PI;
|
||||
_z *= 180/M_PI;
|
||||
}
|
||||
|
||||
void Vector::toRadians()
|
||||
{
|
||||
_x *= M_PI/180;
|
||||
_y *= M_PI/180;
|
||||
_z *= M_PI/180;
|
||||
}
|
||||
|
||||
|
||||
double Vector::dot(Vector vec)
|
||||
{
|
||||
return (_x * vec._x) + (_y * vec._y) + (_z * vec._z);
|
||||
}
|
||||
|
||||
Vector Vector::operator + (Vector vec)
|
||||
{
|
||||
Vector ret;
|
||||
|
||||
ret.x() = _x + vec._x;
|
||||
ret.y() = _y + vec._y;
|
||||
ret.z() = _z + vec._z;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Vector::operator - (Vector vec)
|
||||
{
|
||||
Vector ret;
|
||||
|
||||
ret.x() = _x - vec._x;
|
||||
ret.y() = _y - vec._y;
|
||||
ret.z() = _z - vec._z;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Vector::operator * (double scalar)
|
||||
{
|
||||
return scale(scalar);
|
||||
}
|
||||
|
||||
Vector Vector::operator / (double scalar)
|
||||
{
|
||||
Vector ret;
|
||||
ret.x() = _x / scalar;
|
||||
ret.y() = _y / scalar;
|
||||
ret.z() = _z / scalar;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Matrix::Matrix()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
for(i = 0; i <= 2; i++)
|
||||
for(j = 0; j <= 2; j++)
|
||||
{
|
||||
if(i == j)
|
||||
_cell[i][j] = 1.0;
|
||||
else
|
||||
_cell[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Matrix Matrix::operator + (Matrix m)
|
||||
{
|
||||
Matrix ret;
|
||||
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
for(i = 0; i <= 2; i++)
|
||||
for(j = 0; j <= 2; j++)
|
||||
ret._cell[i][j] = _cell[i][j] + m._cell[i][j];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix Matrix::operator - (Matrix m)
|
||||
{
|
||||
Matrix ret;
|
||||
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
for(i = 0; i <= 2; i++)
|
||||
for(j = 0; j <= 2; j++)
|
||||
ret._cell[i][j] = _cell[i][j] - m._cell[i][j];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix Matrix::operator * (double scalar)
|
||||
{
|
||||
Matrix ret;
|
||||
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
for(i = 0; i <= 2; i++)
|
||||
for(j = 0; j <= 2; j++)
|
||||
ret._cell[i][j] = _cell[i][j] * scalar;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matrix Matrix::operator * (Matrix m)
|
||||
{
|
||||
Matrix ret;
|
||||
|
||||
uint8_t i;
|
||||
for(i = 0; i <= 2; i++)
|
||||
{
|
||||
uint8_t j;
|
||||
for(j = 0; j <= 2; j++)
|
||||
{
|
||||
//so for each cell in the output
|
||||
Vector row = row_to_vector(i);
|
||||
Vector column = m.col_to_vector(j);
|
||||
ret._cell[i][j] = row.dot(column);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Matrix::col_to_vector(uint8_t rn)
|
||||
{
|
||||
Vector ret;
|
||||
ret.x() = _cell[0][rn];
|
||||
ret.y() = _cell[1][rn];
|
||||
ret.z() = _cell[2][rn];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Matrix::row_to_vector(uint8_t rn)
|
||||
{
|
||||
Vector ret;
|
||||
ret.x() = _cell[rn][0];
|
||||
ret.y() = _cell[rn][1];
|
||||
ret.z() = _cell[rn][2];
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Matrix::vector_to_row(Vector v, int row)
|
||||
{
|
||||
_cell[row][0] = v.x();
|
||||
_cell[row][1] = v.y();
|
||||
_cell[row][2] = v.z();
|
||||
}
|
||||
|
||||
double& Matrix::operator ()(int row, int col)
|
||||
{
|
||||
return _cell[row][col];
|
||||
}
|
||||
|
||||
double& Matrix::cell(int row, int col)
|
||||
{
|
||||
return _cell[row][col];
|
||||
}
|
||||
|
||||
|
||||
Quaternion::Quaternion()
|
||||
{
|
||||
_w = 1.0;
|
||||
_x = _y = _z = 0.0;
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(double iw, double i_x, double i_y, double i_z)
|
||||
{
|
||||
_w = iw;
|
||||
_x = i_x;
|
||||
_y = i_y;
|
||||
_z = i_z;
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(double iw, Vector vec)
|
||||
{
|
||||
_w = iw;
|
||||
_x = vec.x();
|
||||
_y = vec.y();
|
||||
_z = vec.z();
|
||||
}
|
||||
|
||||
double Quaternion::magnitude()
|
||||
{
|
||||
double res = (_w*_w) + (_x*_x) + (_y*_y) + (_z*_z);
|
||||
if((res > 1.001) || (res < 0.999)) //only normalise if it needs to be done - saves a call to sqrt()
|
||||
return sqrt(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Quaternion::normalize()
|
||||
{
|
||||
double mag = magnitude();
|
||||
if(mag == 0) //avoid divide b_y _zero
|
||||
return;
|
||||
_w = _w/mag;
|
||||
_x = _x/mag;
|
||||
_y = _y/mag;
|
||||
_z = _z/mag;
|
||||
}
|
||||
|
||||
Quaternion Quaternion::operator * (Quaternion q)
|
||||
{
|
||||
Quaternion ret;
|
||||
ret._w = ((_w*q._w) - (_x*q._x) - (_y*q._y) - (_z*q._z));
|
||||
ret._x = ((_w*q._x) + (_x*q._w) + (_y*q._z) - (_z*q._y));
|
||||
ret._y = ((_w*q._y) - (_x*q._z) + (_y*q._w) + (_z*q._x));
|
||||
ret._z = ((_w*q._z) + (_x*q._y) - (_y*q._x) + (_z*q._w));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void Quaternion::fromMatrix(Matrix m)
|
||||
{
|
||||
float tr = m(0, 0) + m(1, 1) + m(2, 2);
|
||||
|
||||
float S = 0.0;
|
||||
if (tr > 0)
|
||||
{
|
||||
S = sqrt(tr+1.0) * 2;
|
||||
_w = 0.25 * S;
|
||||
_x = (m(2, 1) - m(1, 2)) / S;
|
||||
_y = (m(0, 2) - m(2, 0)) / S;
|
||||
_z = (m(1, 0) - m(0, 1)) / S;
|
||||
}
|
||||
else if ((m(0, 0) < m(1, 1))&(m(0, 0) < m(2, 2)))
|
||||
{
|
||||
S = sqrt(1.0 + m(0, 0) - m(1, 1) - m(2, 2)) * 2;
|
||||
_w = (m(2, 1) - m(1, 2)) / S;
|
||||
_x = 0.25 * S;
|
||||
_y = (m(0, 1) + m(1, 0)) / S;
|
||||
_z = (m(0, 2) + m(2, 0)) / S;
|
||||
}
|
||||
else if (m(1, 1) < m(2, 2))
|
||||
{
|
||||
S = sqrt(1.0 + m(1, 1) - m(0, 0) - m(2, 2)) * 2;
|
||||
_w = (m(0, 2) - m(2, 0)) / S;
|
||||
_x = (m(0, 1) + m(1, 0)) / S;
|
||||
_y = 0.25 * S;
|
||||
_z = (m(1, 2) + m(2, 1)) / S;
|
||||
}
|
||||
else
|
||||
{
|
||||
S = sqrt(1.0 + m(2, 2) - m(0, 0) - m(1, 1)) * 2;
|
||||
_w = (m(1, 0) - m(0, 1)) / S;
|
||||
_x = (m(0, 2) + m(2, 0)) / S;
|
||||
_y = (m(1, 2) + m(2, 1)) / S;
|
||||
_z = 0.25 * S;
|
||||
}
|
||||
}
|
||||
|
||||
Matrix Quaternion::toMatrix()
|
||||
{
|
||||
Matrix ret;
|
||||
ret.cell(0, 0) = 1-(2*(_y*_y))-(2*(_z*_z));
|
||||
ret.cell(0, 1) = (2*_x*_y)-(2*_w*_z);
|
||||
ret.cell(0, 2) = (2*_x*_z)+(2*_w*_y);
|
||||
|
||||
ret.cell(1, 0) = (2*_x*_y)+(2*_w*_z);
|
||||
ret.cell(1, 1) = 1-(2*(_x*_x))-(2*(_z*_z));
|
||||
ret.cell(1, 2) = (2*(_y*_z))-(2*(_w*_x));
|
||||
|
||||
ret.cell(2, 0) = (2*(_x*_z))-(2*_w*_y);
|
||||
ret.cell(2, 1) = (2*_y*_z)+(2*_w*_x);
|
||||
ret.cell(2, 2) = 1-(2*(_x*_x))-(2*(_y*_y));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector Quaternion::toEuler()
|
||||
{
|
||||
Vector ret;
|
||||
double sqw = _w*_w;
|
||||
double sqx = _x*_x;
|
||||
double sqy = _y*_y;
|
||||
double sqz = _z*_z;
|
||||
|
||||
ret.x() = atan2(2.0*(_x*_y+_z*_w),(sqx-sqy-sqz+sqw));
|
||||
ret.y() = asin(-2.0*(_x*_z-_y*_w)/(sqx+sqy+sqz+sqw));
|
||||
ret.z() = atan2(2.0*(_y*_z+_x*_w),(-sqx-sqy+sqz+sqw));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Quaternion Quaternion::conjugate()
|
||||
{
|
||||
Quaternion q;
|
||||
q.w() = _w;
|
||||
q.x() = -_x;
|
||||
q.y() = -_y;
|
||||
q.z() = -_z;
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
135
imuMaths.h
135
imuMaths.h
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
AltIMU-10 Quaternion based AHRS
|
||||
www.camelsoftware.com/firetail
|
||||
Copyright (C) 2013 Samuel Cowen
|
||||
|
||||
The latest version of the IMU Maths library will always be available at
|
||||
http://sourceforge.net/p/firetail/code/ci/master/tree/
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IMUMATH_H
|
||||
#define IMUMATH_H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace imu
|
||||
{
|
||||
|
||||
class Vector
|
||||
{
|
||||
public:
|
||||
Vector();
|
||||
Vector(double _x, double _y, double _z);
|
||||
|
||||
double magnitude(); //return the magnitude of the vector
|
||||
void normalize(); //normalise the vector
|
||||
|
||||
Vector cross(Vector vec);
|
||||
double dot(Vector vec);
|
||||
|
||||
Vector scale(double scalar);
|
||||
|
||||
void toDegrees();
|
||||
void toRadians();
|
||||
|
||||
double &x() {
|
||||
return _x;
|
||||
}
|
||||
double &y() {
|
||||
return _y;
|
||||
}
|
||||
double &z() {
|
||||
return _z;
|
||||
}
|
||||
|
||||
Vector operator + (Vector);
|
||||
Vector operator - (Vector);
|
||||
Vector invert() {
|
||||
return Vector(-_x, -_y, -_z);
|
||||
}
|
||||
|
||||
Vector operator * (double scalar);
|
||||
Vector operator / (double scalar);
|
||||
|
||||
private:
|
||||
double _x, _y, _z;
|
||||
};
|
||||
|
||||
|
||||
class Matrix
|
||||
{
|
||||
public:
|
||||
Matrix();
|
||||
|
||||
Matrix operator + (Matrix);
|
||||
Matrix operator - (Matrix);
|
||||
Matrix operator * (double scalar);
|
||||
Matrix operator * (Matrix m);
|
||||
|
||||
double& operator ()(int row, int col);
|
||||
double& cell(int row, int col);
|
||||
|
||||
Vector row_to_vector(uint8_t rn);
|
||||
Vector col_to_vector(uint8_t cn);
|
||||
|
||||
void vector_to_row(Vector v, int row);
|
||||
|
||||
|
||||
protected:
|
||||
double _cell[3][3]; //x, y
|
||||
};
|
||||
|
||||
|
||||
class Quaternion
|
||||
{
|
||||
public:
|
||||
Quaternion();
|
||||
Quaternion(double iw, double ix, double iy, double iz);
|
||||
Quaternion(double w, Vector vec);
|
||||
|
||||
double& w() {
|
||||
return _w;
|
||||
}
|
||||
double& x() {
|
||||
return _x;
|
||||
}
|
||||
double& y() {
|
||||
return _y;
|
||||
}
|
||||
double& z() {
|
||||
return _z;
|
||||
}
|
||||
|
||||
double magnitude();
|
||||
void normalize();
|
||||
Quaternion conjugate();
|
||||
|
||||
void fromMatrix(Matrix m);
|
||||
Matrix toMatrix();
|
||||
Vector toEuler();
|
||||
|
||||
Quaternion operator * (Quaternion q);
|
||||
|
||||
private:
|
||||
double _w, _x, _y, _z;
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in a new issue