2015-08-20 20:47:05 +00:00
// Package mpu6050 allows interfacing with InvenSense mpu6050 barometric pressure sensor. This sensor
2015-08-16 23:33:34 +00:00
// has the ability to provided compensated temperature and pressure readings.
2016-07-13 22:42:25 +00:00
package mpu
2015-08-16 23:33:34 +00:00
import (
2015-12-27 21:11:21 +00:00
"../linux-mpu9150/mpu"
2015-08-20 20:47:05 +00:00
"log"
2015-12-27 23:19:39 +00:00
"math"
2015-12-27 21:11:21 +00:00
"time"
2016-07-15 01:38:15 +00:00
"errors"
2015-08-16 23:33:34 +00:00
)
//https://www.olimex.com/Products/Modules/Sensors/MOD-MPU6050/resources/RM-MPU-60xxA_rev_4.pdf
const (
2015-12-27 21:11:21 +00:00
pollDelay = 98 * time . Millisecond // ~10Hz
2015-08-16 23:33:34 +00:00
)
// MPU6050 represents a InvenSense MPU6050 sensor.
type MPU6050 struct {
2016-07-13 20:36:33 +00:00
poll time . Duration
2015-08-16 23:33:34 +00:00
2015-08-20 20:47:05 +00:00
started bool
2015-08-16 23:33:34 +00:00
2015-12-27 21:11:21 +00:00
pitch float64
roll float64
2015-08-16 23:33:34 +00:00
2015-12-27 21:11:21 +00:00
// Calibration variables.
calibrated bool
2015-08-20 20:47:05 +00:00
pitch_history [ ] float64
roll_history [ ] float64
pitch_resting float64
roll_resting float64
2015-08-16 23:33:34 +00:00
2015-12-27 21:11:21 +00:00
// For tracking heading (mixing GPS track and the gyro output).
2015-12-31 07:10:10 +00:00
heading float64 // Current heading.
gps_track float64 // Last reading directly from the gyro for comparison with current heading.
gps_track_valid bool
heading_correction float64
2015-09-25 03:50:56 +00:00
2015-08-20 20:47:05 +00:00
quit chan struct { }
2015-08-16 23:33:34 +00:00
}
// New returns a handle to a MPU6050 sensor.
2016-07-14 00:49:07 +00:00
func NewMPU6050 ( ) ( * MPU6050 , error ) {
2016-07-13 20:36:33 +00:00
n := & MPU6050 { poll : pollDelay }
2016-07-15 01:38:15 +00:00
if err := n . startUp ( ) ; err != nil {
return nil , err
}
2016-07-14 00:49:07 +00:00
return n , nil
2015-08-16 23:33:34 +00:00
}
2016-07-13 20:36:33 +00:00
func ( d * MPU6050 ) startUp ( ) error {
2016-01-11 16:16:25 +00:00
mpu_sample_rate := 10 // 10 Hz read rate of hardware IMU
yaw_mix_factor := 0 // must be zero if no magnetometer
2016-07-15 01:38:15 +00:00
err := mpu . InitMPU ( mpu_sample_rate , yaw_mix_factor )
if err != 0 {
return errors . New ( "MPU6050 Error: couldn't start MPU" )
}
2015-08-21 08:12:21 +00:00
2015-08-16 23:33:34 +00:00
d . pitch_history = make ( [ ] float64 , 0 )
d . roll_history = make ( [ ] float64 , 0 )
d . started = true
2016-07-13 20:36:33 +00:00
d . run ( )
2015-08-16 23:33:34 +00:00
return nil
}
2015-12-27 21:11:21 +00:00
/ *
2015-08-16 23:33:34 +00:00
func ( d * MPU6050 ) calibrate ( ) {
//TODO: Error checking to make sure that the histories are extensive enough to be significant.
//TODO: Error checking to do continuous calibrations.
pitch_adjust := float64 ( 0 )
for _ , v := range d . pitch_history {
pitch_adjust = pitch_adjust + v
}
pitch_adjust = pitch_adjust / float64 ( len ( d . pitch_history ) )
d . pitch_resting = pitch_adjust
roll_adjust := float64 ( 0 )
for _ , v := range d . roll_history {
roll_adjust = roll_adjust + v
}
roll_adjust = roll_adjust / float64 ( len ( d . roll_history ) )
d . roll_resting = roll_adjust
2015-08-20 20:47:05 +00:00
log . Printf ( "calibrate: pitch %f, roll %f\n" , pitch_adjust , roll_adjust )
2015-09-25 03:50:56 +00:00
d . calibrated = true
2015-08-16 23:33:34 +00:00
}
2015-12-27 21:11:21 +00:00
* /
2015-08-16 23:33:34 +00:00
2015-12-27 21:11:21 +00:00
func normalizeHeading ( h float64 ) float64 {
for h < float64 ( 0.0 ) {
h = h + float64 ( 360.0 )
2015-08-16 23:33:34 +00:00
}
2015-12-27 21:11:21 +00:00
for h >= float64 ( 360.0 ) {
h = h - float64 ( 360.0 )
2015-08-16 23:33:34 +00:00
}
2015-12-27 21:11:21 +00:00
return h
2015-08-16 23:33:34 +00:00
}
2015-12-27 21:11:21 +00:00
func ( d * MPU6050 ) getMPUData ( ) {
2015-12-27 23:19:39 +00:00
pr , rr , hr , err := mpu . ReadMPU ( )
// Convert from radians to degrees.
pitch := float64 ( pr ) * ( float64 ( 180.0 ) / math . Pi )
2015-12-31 07:10:10 +00:00
roll := float64 ( - rr ) * ( float64 ( 180.0 ) / math . Pi )
2015-12-27 23:19:39 +00:00
heading := float64 ( hr ) * ( float64 ( 180.0 ) / math . Pi )
if heading < float64 ( 0.0 ) {
heading = float64 ( 360.0 ) + heading
}
2015-08-16 23:33:34 +00:00
2015-12-27 21:11:21 +00:00
if err == nil {
2015-12-27 23:19:39 +00:00
d . pitch = pitch
d . roll = roll
2015-08-16 23:33:34 +00:00
2015-12-31 07:10:10 +00:00
// Heading is raw value off the IMU. Without mag compass fusion, need to apply correction bias.
// Amount of correction is set by ResetHeading() -- doesn't necessarily have to be based off GPS.
d . heading = normalizeHeading ( ( heading - d . heading_correction ) )
2015-12-27 21:11:21 +00:00
} else {
2015-12-27 23:19:39 +00:00
// log.Printf("mpu6050.calculatePitchAndRoll(): mpu.ReadMPU() err: %s\n", err.Error())
2015-08-21 08:12:21 +00:00
}
2015-08-16 23:33:34 +00:00
}
// Temperature returns the current temperature reading.
2016-07-13 20:36:33 +00:00
func ( d * MPU6050 ) Pitch ( ) ( float64 , error ) {
return ( d . pitch - d . pitch_resting ) , nil
2015-08-16 23:33:34 +00:00
}
2016-07-13 20:36:33 +00:00
func ( d * MPU6050 ) Roll ( ) ( float64 , error ) {
return ( d . roll - d . roll_resting ) , nil
2015-08-21 08:12:21 +00:00
}
2016-07-13 20:36:33 +00:00
func ( d * MPU6050 ) Heading ( ) ( float64 , error ) {
return d . heading , nil
}
func ( d * MPU6050 ) run ( ) {
time . Sleep ( d . poll )
2015-08-16 23:33:34 +00:00
go func ( ) {
d . quit = make ( chan struct { } )
2016-07-13 20:36:33 +00:00
timer := time . NewTicker ( d . poll )
2015-12-27 21:11:21 +00:00
// calibrateTimer := time.NewTicker(1 * time.Minute)
2015-08-16 23:33:34 +00:00
for {
select {
case <- timer . C :
2015-12-27 21:11:21 +00:00
d . getMPUData ( )
// case <-calibrateTimer.C:
// d.calibrate()
// calibrateTimer.Stop()
2015-08-16 23:33:34 +00:00
case <- d . quit :
2015-12-27 23:19:39 +00:00
mpu . CloseMPU ( )
2015-08-16 23:33:34 +00:00
return
}
}
} ( )
return
}
2015-08-21 08:12:21 +00:00
// Set heading from a known value (usually GPS true heading).
2015-12-31 07:10:10 +00:00
func ( d * MPU6050 ) ResetHeading ( newHeading float64 , gain float64 ) {
if gain < 0.001 { // sanitize our inputs!
gain = 0.001
} else if gain > 1 {
gain = 1
}
old_hdg := d . heading // only used for debug log report
//newHeading = float64(30*time.Now().Minute()) // demo input for testing
newHeading = normalizeHeading ( newHeading ) // sanitize the inputs
// By applying gain factor, this becomes a 1st order function that slowly converges on solution.
// Time constant is poll rate over gain. With gain of 0.1, convergence to +/-2 deg on a 180 correction difference is about 4 sec; 0.01 converges in 45 sec.
hdg_corr_bias := float64 ( d . heading - newHeading ) // desired adjustment to heading_correction
if hdg_corr_bias > 180 {
hdg_corr_bias = hdg_corr_bias - 360
} else if hdg_corr_bias < - 180 {
hdg_corr_bias = hdg_corr_bias + 360
}
hdg_corr_bias = hdg_corr_bias * gain
d . heading_correction = normalizeHeading ( d . heading_correction + hdg_corr_bias )
log . Printf ( "Adjusted heading. Old: %f Desired: %f Adjustment: %f New: %f\n" , old_hdg , newHeading , hdg_corr_bias , d . heading - hdg_corr_bias )
2015-08-21 08:12:21 +00:00
}
2015-08-16 23:33:34 +00:00
// Close.
func ( d * MPU6050 ) Close ( ) {
if d . quit != nil {
d . quit <- struct { } { }
}
2015-08-20 20:47:05 +00:00
}
2016-07-13 22:42:25 +00:00
func ( d * MPU6050 ) MagHeading ( ) ( float64 , error ) { return 0 , nil }
func ( d * MPU6050 ) SlipSkid ( ) ( float64 , error ) { return 0 , nil }
func ( d * MPU6050 ) RateOfTurn ( ) ( float64 , error ) { return 0 , nil }
func ( d * MPU6050 ) GLoad ( ) ( float64 , error ) { return 0 , nil }