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.
package mpu6050
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"
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 {
Poll time . Duration
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.
2015-12-27 21:11:21 +00:00
func New ( ) * MPU6050 {
n := & MPU6050 { Poll : pollDelay }
2015-08-16 23:33:34 +00:00
n . StartUp ( )
return n
}
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
mpu . InitMPU ( mpu_sample_rate , yaw_mix_factor )
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
d . Run ( )
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.
func ( d * MPU6050 ) PitchAndRoll ( ) ( float64 , float64 ) {
return ( d . pitch - d . pitch_resting ) , ( d . roll - d . roll_resting )
}
2015-08-21 08:12:21 +00:00
func ( d * MPU6050 ) Heading ( ) float64 {
return d . heading
}
2015-08-16 23:33:34 +00:00
func ( d * MPU6050 ) Run ( ) {
2015-12-27 23:19:39 +00:00
time . Sleep ( d . Poll )
2015-08-16 23:33:34 +00:00
go func ( ) {
d . quit = make ( chan struct { } )
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
}