Skip to content

Commit d0dec8f

Browse files
committed
add support library for MPU925x 10-DOF IMU sensor
fixes fossasia#59
1 parent f05e3c8 commit d0dec8f

File tree

2 files changed

+200
-1
lines changed

2 files changed

+200
-1
lines changed

PSL/Peripherals.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@ def __captureStart__(self,address,location,sample_length,total_samples,tg):
383383
"""
384384
Blocking call that starts fetching data from I2C sensors like an oscilloscope fetches voltage readings
385385
You will then have to call `__retrievebuffer__` to fetch this data, and `__dataProcessor` to process and return separate channels
386-
387386
refer to `capture` if you want a one-stop solution.
388387
389388
.. tabularcolumns:: |p{3cm}|p{11cm}|

PSL/SENSORS/MPU925x.py

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# -*- coding: utf-8; mode: python; indent-tabs-mode: t; tab-width:4 -*-
2+
from numpy import int16,std
3+
from Kalman import KalmanFilter
4+
5+
def connect(route,**args):
6+
return MPU925x(route,**args)
7+
8+
class MPU925x():
9+
'''
10+
Mandatory members:
11+
GetRaw : Function called by Graphical apps. Must return values stored in a list
12+
NUMPLOTS : length of list returned by GetRaw. Even single datapoints need to be stored in a list before returning
13+
PLOTNAMES : a list of strings describing each element in the list returned by GetRaw. len(PLOTNAMES) = NUMPLOTS
14+
name : the name of the sensor shown to the user
15+
params:
16+
A dictionary of function calls(single arguments only) paired with list of valid argument values. (Primitive. I know.)
17+
These calls can be used for one time configuration settings
18+
19+
'''
20+
INT_PIN_CFG = 0x37
21+
GYRO_CONFIG = 0x1B
22+
ACCEL_CONFIG = 0x1C
23+
GYRO_SCALING= [131,65.5,32.8,16.4]
24+
ACCEL_SCALING=[16384,8192,4096,2048]
25+
AR=3
26+
GR=3
27+
NUMPLOTS=7
28+
PLOTNAMES = ['Ax','Ay','Az','Temp','Gx','Gy','Gz']
29+
ADDRESS = 0x68
30+
AK8963_ADDRESS =0x0C
31+
AK8963_CNTL = 0x0A
32+
name = 'Accel/gyro'
33+
def __init__(self,I2C,**args):
34+
self.I2C=I2C
35+
self.ADDRESS = args.get('address',self.ADDRESS)
36+
self.name = 'Accel/gyro'
37+
self.params={'powerUp':None,'setGyroRange':[250,500,1000,2000],'setAccelRange':[2,4,8,16],'KalmanFilter':[.01,.1,1,10,100,1000,10000,'OFF']}
38+
self.setGyroRange(2000)
39+
self.setAccelRange(16)
40+
'''
41+
try:
42+
self.I2C.configI2C(400e3)
43+
except:
44+
pass
45+
'''
46+
self.powerUp()
47+
self.K=None
48+
49+
50+
51+
def KalmanFilter(self,opt):
52+
if opt=='OFF':
53+
self.K=None
54+
return
55+
noise=[[]]*self.NUMPLOTS
56+
for a in range(500):
57+
vals=self.getRaw()
58+
for b in range(self.NUMPLOTS):noise[b].append(vals[b])
59+
60+
self.K=[None]*7
61+
for a in range(self.NUMPLOTS):
62+
sd = std(noise[a])
63+
self.K[a] = KalmanFilter(1./opt, sd**2)
64+
65+
def getVals(self,addr,bytes):
66+
vals = self.I2C.readBulk(self.ADDRESS,addr,bytes)
67+
return vals
68+
69+
def powerUp(self):
70+
self.I2C.writeBulk(self.ADDRESS,[0x6B,0])
71+
72+
def setGyroRange(self,rs):
73+
self.GR=self.params['setGyroRange'].index(rs)
74+
self.I2C.writeBulk(self.ADDRESS,[self.GYRO_CONFIG,self.GR<<3])
75+
76+
def setAccelRange(self,rs):
77+
self.AR=self.params['setAccelRange'].index(rs)
78+
self.I2C.writeBulk(self.ADDRESS,[self.ACCEL_CONFIG,self.AR<<3])
79+
80+
def getRaw(self):
81+
'''
82+
This method must be defined if you want GUIs to use this class to generate
83+
plots on the fly.
84+
It must return a set of different values read from the sensor. such as X,Y,Z acceleration.
85+
The length of this list must not change, and must be defined in the variable NUMPLOTS.
86+
87+
GUIs will generate as many plots, and the data returned from this method will be appended appropriately
88+
'''
89+
vals=self.getVals(0x3B,14)
90+
if vals:
91+
if len(vals)==14:
92+
raw=[0]*7
93+
for a in range(3):raw[a] = 1.*int16(vals[a*2]<<8|vals[a*2+1])/self.ACCEL_SCALING[self.AR]
94+
for a in range(4,7):raw[a] = 1.*int16(vals[a*2]<<8|vals[a*2+1])/self.GYRO_SCALING[self.GR]
95+
raw[3] = int16(vals[6]<<8|vals[7])/340. + 36.53
96+
if not self.K:
97+
return raw
98+
else:
99+
for b in range(self.NUMPLOTS):
100+
self.K[b].input_latest_noisy_measurement(raw[b])
101+
raw[b]=self.K[b].get_latest_estimated_measurement()
102+
return raw
103+
104+
else:
105+
return False
106+
else:
107+
return False
108+
109+
def getAccel(self):
110+
'''
111+
Return a list of 3 values for acceleration vector
112+
113+
'''
114+
vals=self.getVals(0x3B,6)
115+
ax=int16(vals[0]<<8|vals[1])
116+
ay=int16(vals[2]<<8|vals[3])
117+
az=int16(vals[4]<<8|vals[5])
118+
return [ax/65535.,ay/65535.,az/65535.]
119+
120+
def getTemp(self):
121+
'''
122+
Return temperature
123+
'''
124+
vals=self.getVals(0x41,6)
125+
t=int16(vals[0]<<8|vals[1])
126+
return t/65535.
127+
128+
def getGyro(self):
129+
'''
130+
Return a list of 3 values for angular velocity vector
131+
132+
'''
133+
vals=self.getVals(0x43,6)
134+
ax=int16(vals[0]<<8|vals[1])
135+
ay=int16(vals[2]<<8|vals[3])
136+
az=int16(vals[4]<<8|vals[5])
137+
return [ax/65535.,ay/65535.,az/65535.]
138+
139+
def getMag(self):
140+
'''
141+
Return a list of 3 values for magnetic field vector
142+
143+
'''
144+
vals=self.I2C.readBulk(self.AK8963_ADDRESS,0x03,7) #6+1 . 1(ST2) should not have bit 4 (0x8) true. It's ideally 16 . overflow bit
145+
ax=int16(vals[0]<<8|vals[1])
146+
ay=int16(vals[2]<<8|vals[3])
147+
az=int16(vals[4]<<8|vals[5])
148+
if not vals[6]&0x08:return [ax/65535.,ay/65535.,az/65535.]
149+
else: return None
150+
151+
152+
def WhoAmI(self):
153+
'''
154+
Returns the ID .
155+
It is 71 for MPU9250 .
156+
'''
157+
v = self.I2C.readBulk(self.ADDRESS,0x75,1)[0]
158+
if v not in [0x71,0x73]:return 'Error %s'%hex(v)
159+
160+
161+
if v==0x73:return 'MPU9255 %s'%hex(v)
162+
elif v==0x71:return 'MPU9250 %s'%hex(v)
163+
164+
165+
def WhoAmI_AK8963(self):
166+
'''
167+
Returns the ID fo magnetometer AK8963 if found.
168+
It should be 0x48.
169+
'''
170+
self.initMagnetometer()
171+
v= self.I2C.readBulk(self.AK8963_ADDRESS,0,1) [0]
172+
if v==0x48:return 'AK8963 at %s'%hex(v)
173+
else: return 'AK8963 not found. returned :%s'%hex(v)
174+
175+
def initMagnetometer(self):
176+
'''
177+
For MPU925x with integrated magnetometer.
178+
It's called a 10 DoF sensor, but technically speaking ,
179+
the 3-axis Accel , 3-Axis Gyro, temperature sensor are integrated in one IC, and the 3-axis magnetometer is implemented in a
180+
separate IC which can be accessed via an I2C passthrough.
181+
182+
Therefore , in order to detect the magnetometer via an I2C scan, the passthrough must first be enabled on IC#1 (Accel,gyro,temp)
183+
'''
184+
self.I2C.writeBulk(self.ADDRESS,[self.INT_PIN_CFG,0x22]) #I2C passthrough
185+
self.I2C.writeBulk(self.AK8963_ADDRESS,[self.AK8963_CNTL,0]) #power down mag
186+
self.I2C.writeBulk(self.AK8963_ADDRESS,[self.AK8963_CNTL,(1<<4)|6]) #mode (0=14bits,1=16bits) <<4 | (2=8Hz , 6=100Hz)
187+
188+
189+
190+
if __name__ == "__main__":
191+
from PSL import sciencelab
192+
I = sciencelab.connect()
193+
A = connect(I.I2C)
194+
t,x,y,z = I.I2C.capture(A.ADDRESS,0x43,6,5000,1000,'int')
195+
#print (t,x,y,z)
196+
from pylab import *
197+
plot(t,x)
198+
plot(t,y)
199+
plot(t,z)
200+
show()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy