树莓派mpu6050_姿态估计
MPU6050是世界上第一款也是唯一一款专为智能手机、平板电脑和可穿戴传感器的低功耗、低成本和高性能要求而设计的6轴运动跟踪设备。
它集成了3轴MEMS陀螺仪,3轴MEMS加速度计,以及一个可扩展的数字运动处理器 DMP( DigitalMotion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。扩展之后就可以通过其 I2C或SPI接口输出一个9轴的信号( SPI接口仅在MPU-6000可用)。 MPU-60X0也可以通过其I2C接口连接非惯性的数字传感器,比如压力传感器。
接线及测试
GND —- GND VCC —- 接树莓派 5V
SDA —- I2C 数据 SCL —- I2C 时钟
测试
sudo i2cdetect -y 1
接线没错的话,可以看到MPU6050 地址是 0x68
安装依赖包
pip install mpu6050-raspberrypi
python-smbus或python3-smbus
包,根据你的 Python 版本。
使用
- 获取温度
get_temp()
- 获取加速度计范围
def set_accel_range(self, accel_range): # 设置加速度计范围 ACCEL_RANGE_2G = 0x00 ACCEL_RANGE_4G = 0x08 ACCEL_RANGE_8G = 0x10 ACCEL_RANGE_16G = 0x18
- 读取加速度计设置的范围
def read_accel_range(self, raw = False):...
# 返回: 2, 4, 8, 16
# 返回 -1 时,说明出了问题
- 读取加速度计数据
def get_accel_data(self, g = False):... return {'x': x, 'y': y, 'z': z}
- 设置陀螺仪范围
def set_gyro_range(self, gyro_range):... ACCEL_SCALE_MODIFIER_2G = 16384.0 ACCEL_SCALE_MODIFIER_4G = 8192.0 ACCEL_SCALE_MODIFIER_8G = 4096.0 ACCEL_SCALE_MODIFIER_16G = 2048.0
- 读取陀螺仪的范围
def read_gyro_range(self, raw = False):... # 如果raw为真,它将从GIRO配置寄存器返回原始值 # 如果rar为False,它将返回250、500、1000、2000或1。如果返回值等于-1,那么就有问题了。神经网络
- 读取陀螺仪数据
def get_gyro_data(self):... return {'x': x, 'y': y, 'z': z} # 获取并返回陀螺仪的X、Y和Z值。 # 返回adictionary中的读取值。
- 返回所有数值
def get_all_data(self): """Reads and returns all the available data.""" temp = self.get_temp() accel = self.get_accel_data() gyro = self.get_gyro_data() return [accel, gyro, temp]
tuoluoyi.py
from mpu6050 import mpu6050 sensor = mpu6050(0x68) accelerometer_data = sensor.get_all_data()
输出
[{'y': 8.86812294921875, 'x': -3.414131567382812, 'z': 1.6017209106445311},
{'y': 0.8931297709923665, 'x': -1.6870229007633588, 'z': 0.22137404580152673},
25.941764705882356]
将数据传输到电脑,电脑分析轨迹
树莓派
#coding:utf-8
import datetime
import math
import time
from mpu6050 import mpu6050
from math import cos, sin
import numpy as np
import matplotlib.pyplot as plt
from socket import *
import struct
import numpy
Kp = 100 # 比例增益控制加速度计/磁强计的收敛速度
Ki = 0.002 # 积分增益控制陀螺仪偏差的收敛速度
halft = 0.001 # 采样周期的一半
# 传感器框架相对于辅助框架的四元数(初始化四元数的值)
q0 = 1
q1 = 0
q2 = 0
q3 = 0
# 由Ki缩放的积分误差项(初始化)
exInt = 0
eyInt = 0
ezInt = 0
mpu = mpu6050(0x68)
# [{'y': 8.86812294921875, 'x': -3.414131567382812, 'z': 1.6017209106445311}, {'y': 0.8931297709923665, 'x': -1.6870229007633588, 'z': 0.22137404580152673}, 25.941764705882356]
class Quadrotor():
def __init__(self, x=0, y=0, z=0, roll=0, pitch=0, yaw=0, size=1, show_animation=True):
self.p1 = np.array([size / 2, 0, 0, 1]).T
self.p2 = np.array([-size / 2, 0, 0, 1]).T
self.p3 = np.array([0, size / 2, 0, 1]).T
self.p4 = np.array([0, -size / 2, 0, 1]).T
self.x_data = []
self.y_data = []
self.z_data = []
self.show_animation = show_animation
if self.show_animation:
plt.ion()
fig = plt.figure()
self.ax = fig.add_subplot(111, projection='3d')
self.update_pose(x, y, z, roll, pitch, yaw)
def update_pose(self, x, y, z, roll, pitch, yaw):
self.x = x
self.y = y
self.z = z
self.roll = roll
self.pitch = pitch
self.yaw = yaw
self.x_data.append(x)
self.y_data.append(y)
self.z_data.append(z)
if self.show_animation:
self.plot()
def transformation_matrix(self):
x = self.x
y = self.y
z = self.z
roll = self.roll
pitch = self.pitch
yaw = self.yaw
return np.array(
[[cos(yaw) * cos(pitch), -sin(yaw) * cos(roll) + cos(yaw) * sin(pitch) * sin(roll), sin(yaw) * sin(roll) + cos(yaw) * sin(pitch) * cos(roll), x],
[sin(yaw) * cos(pitch), cos(yaw) * cos(roll) + sin(yaw) * sin(pitch) *
sin(roll), -cos(yaw) * sin(roll) + sin(yaw) * sin(pitch) * cos(roll), y],
[-sin(pitch), cos(pitch) * sin(roll), cos(pitch) * cos(yaw), z]
])
def plot(self):
T = self.transformation_matrix()
p1_t = np.matmul(T, self.p1)
p2_t = np.matmul(T, self.p2)
p3_t = np.matmul(T, self.p3)
p4_t = np.matmul(T, self.p4)
plt.cla()
self.ax.plot([p1_t[0], p2_t[0], p3_t[0], p4_t[0]],
[p1_t[1], p2_t[1], p3_t[1], p4_t[1]],
[p1_t[2], p2_t[2], p3_t[2], p4_t[2]], 'k.')
self.ax.plot([p1_t[0], p2_t[0]], [p1_t[1], p2_t[1]],
[p1_t[2], p2_t[2]], 'r-')
self.ax.plot([p3_t[0], p4_t[0]], [p3_t[1], p4_t[1]],
[p3_t[2], p4_t[2]], 'r-')
self.ax.plot(self.x_data, self.y_data, self.z_data, 'b:')
plt.xlim(-5, 5)
plt.ylim(-5, 5)
self.ax.set_zlim(0, 10)
plt.pause(0.1)
# while True:
def cal_integral(x, y):
import scipy
from scipy.integrate import simps # 用于计算积分
integrals = []
for i in range(len(y)): # 计算梯形的面积,由于是累加,所以是切片"i+1"
integrals.append(1/2*scipy.integrate.trapz(y[:i + 1], x[:i + 1]))
return integrals
def cal_tiem(a,s,lt,v=0):
a=a*gravitational_acceleration
ct=time.perf_counter()
v=v+a*(ct-lt)
s+=a*(ct-lt)**2/2
lt=ct
print("时间:",ct-lt,"加速度:",a,"速度:",v,"位置",s)
return lt,s,v
def KalmanFilter(z, n_iter=20):
# 这里是假设A=1,H=1的情况
# intial parameters
sz = (n_iter,) # size of array
# Q = 1e-5 # process variance
Q = 1e-6 # process variance
# allocate space for arrays
xhat = numpy.zeros(sz) # a posteri estimate of x
P = numpy.zeros(sz) # a posteri error estimate
xhatminus = numpy.zeros(sz) # a priori estimate of x
Pminus = numpy.zeros(sz) # a priori error estimate
K = numpy.zeros(sz) # gain or blending factor
R = 0.1 ** 2 # estimate of measurement variance, change to see effect
# intial guesses
xhat[0] = 0.0
P[0] = 1.0
A = 1
H = 1
for k in range(1, n_iter):
# time update
xhatminus[k] = A * xhat[k - 1] # X(k|k-1) = AX(k-1|k-1) + BU(k) + W(k),A=1,BU(k) = 0
Pminus[k] = A * P[k - 1] + Q # P(k|k-1) = AP(k-1|k-1)A' + Q(k) ,A=1
# measurement update
K[k] = Pminus[k] / (Pminus[k] + R) # Kg(k)=P(k|k-1)H'/[HP(k|k-1)H' + R],H=1
xhat[k] = xhatminus[k] + K[k] * (z[k] - H * xhatminus[k]) # X(k|k) = X(k|k-1) + Kg(k)[Z(k) - HX(k|k-1)], H=1
P[k] = (1 - K[k] * H) * Pminus[k] # P(k|k) = (1 - Kg(k)H)P(k|k-1), H=1
return xhat
HOST ='192.168.3.108'
PORT = 12345
BUFFSIZE=2048
ADDR = (HOST,PORT)
tctimeClient = socket(AF_INET,SOCK_STREAM)
tctimeClient.connect(ADDR) #发送 tctimeClient.send("发送内容".encode) reciverdata=tctimeClient.recv(bufsize).decode()
position=[0,0,0]
Vv=[0,0,0]
gravitational_acceleration=9.793#武汉
lt = time.perf_counter()
while True:
aaxx=[]
aayy=[]
aazz=[]
for i in range(50):
accel = mpu.get_accel_data() # 加速度
gyro = mpu.get_gyro_data() # 角速度
aax = accel['x'] # 原始加速度x轴数据
aay = accel['y'] # 原始加速度y轴数据
aaz = accel['z'] # 原始加速度z轴数据
print("x轴加速度",aax)
aaxx.append(aax)
aayy.append(aay)
aazz.append(aaz-1)
ggx = gyro['x'] # 原始角速度x轴数据
ggy = gyro['y'] # 原始角速度y轴数据
ggz = gyro['z'] # 原始角速度z轴数据
# 把csv的字符串转化为float型
ax = float(aax)
ay = float(aay)
az = float(aaz)
gx = float(ggx)
gy = float(ggy)
gz = float(ggz)
# 单元化
norm = math.sqrt(ax * ax + ay * ay + az * az)
ax = ax / norm
ay = ay / norm
az = az / norm
# 估计方向的重力
vx = 2 * (q1 * q3 - q0 * q2)
vy = 2 * (q0 * q1 + q2 * q3)
vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3
# 错误的领域和方向传感器测量参考方向之间的交叉乘积的总和
ex = (ay * vz - az * vy)
ey = (az * vx - ax * vz)
ez = (ax * vy - ay * vx)
# 积分误差比例积分增益
exInt += ex * Ki
eyInt += ey * Ki
ezInt += ez * Ki
# 调整后的陀螺仪测量
gx += Kp * ex + exInt
gy += Kp * ey + eyInt
gz += Kp * ez + ezInt
# 整合四元数
q0 += (-q1 * gx - q2 * gy - q3 * gz) * halft
q1 += (q0 * gx + q2 * gz - q3 * gy) * halft
q2 += (q0 * gy - q1 * gz + q3 * gx) * halft
q3 += (q0 * gz + q1 * gy - q2 * gx) * halft
# 正常化四元数
norm = math.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3)
q0 /= norm
q1 /= norm
q2 /= norm
q3 /= norm
# 获取欧拉角 pitch、roll、yaw
pitch = math.asin(-2 * q1 * q3 + 2 * q0 * q2) * 57.3
roll = math.atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2 * q2 + 1) * 57.3
yaw = math.atan2(2 * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * 57.3
# 打印姿态角信息以及把姿态角数据写到txt文本中
print("pry:%.3f,%.3f,%.3f" % (pitch, roll, yaw))
# aaxx=KalmanFilter(aaxx)
# aayy=KalmanFilter(aayy)
# aazz=KalmanFilter(aazz)
# print(cal_integral(at,aaxall))
lt,position[0],Vv[0]=cal_tiem(sum(aaxx)/len(aaxx), position[0], lt, Vv[0])
lt,position[1],Vv[1]=cal_tiem(sum(aayy)/len(aayy), position[1], lt, Vv[1])
lt,position[2],Vv[2]=cal_tiem(sum(aazz)/len(aazz), position[2], lt, Vv[2])
str = struct.pack("ffffff", pitch, roll,yaw,position[0]*100,position[1]*100,position[2]*100)
tctimeClient.send(str)
服务器
import struct
from math import cos, sin
import numpy as np
import matplotlib.pyplot as plt
from socket import *
host = '192.168.3.108'
port = 12345
buffsize = 24
ADDR = (host,port)
tctime = socket(AF_INET,SOCK_STREAM)
tctime.bind(ADDR)
tctime.listen(3)
print('Wait for connection ...')
tctimeClient,addr = tctime.accept()
print("Connection from :",addr)
class Quadrotor():
def __init__(self, x=0, y=0, z=0, roll=0, pitch=0, yaw=0, size=1, show_animation=True):
self.p1 = np.array([size / 2, 0, 0, 1]).T
self.p2 = np.array([-size / 2, 0, 0, 1]).T
self.p3 = np.array([0, size / 2, 0, 1]).T
self.p4 = np.array([0, -size / 2, 0, 1]).T
self.x_data = []
self.y_data = []
self.z_data = []
self.show_animation = show_animation
if self.show_animation:
plt.ion()
fig = plt.figure()
self.ax = fig.add_subplot(111, projection='3d')
self.update_pose(x, y, z, roll, pitch, yaw)
def update_pose(self, x, y, z, roll, pitch, yaw):
self.x = x
self.y = y
self.z = z
self.roll = roll
self.pitch = pitch
self.yaw = yaw
self.x_data.append(x)
self.y_data.append(y)
self.z_data.append(z)
if self.show_animation:
self.plot()
def transformation_matrix(self):
x = self.x
y = self.y
z = self.z
roll = self.roll
pitch = self.pitch
yaw = self.yaw
return np.array(
[[cos(yaw) * cos(pitch), -sin(yaw) * cos(roll) + cos(yaw) * sin(pitch) * sin(roll), sin(yaw) * sin(roll) + cos(yaw) * sin(pitch) * cos(roll), x],
[sin(yaw) * cos(pitch), cos(yaw) * cos(roll) + sin(yaw) * sin(pitch) *
sin(roll), -cos(yaw) * sin(roll) + sin(yaw) * sin(pitch) * cos(roll), y],
[-sin(pitch), cos(pitch) * sin(roll), cos(pitch) * cos(yaw), z]
])
def plot(self):
T = self.transformation_matrix()
p1_t = np.matmul(T, self.p1)
p2_t = np.matmul(T, self.p2)
p3_t = np.matmul(T, self.p3)
p4_t = np.matmul(T, self.p4)
plt.cla()
self.ax.plot([p1_t[0], p2_t[0], p3_t[0], p4_t[0]],
[p1_t[1], p2_t[1], p3_t[1], p4_t[1]],
[p1_t[2], p2_t[2], p3_t[2], p4_t[2]], 'k.')
self.ax.plot([p1_t[0], p2_t[0]], [p1_t[1], p2_t[1]],
[p1_t[2], p2_t[2]], 'r-')
self.ax.plot([p3_t[0], p4_t[0]], [p3_t[1], p4_t[1]],
[p3_t[2], p4_t[2]], 'r-')
self.ax.plot(self.x_data, self.y_data, self.z_data, 'b:')
plt.xlim(-500, 500)
plt.ylim(-500, 500)
self.ax.set_zlim(0, 300)
plt.pause(0.001)
q = Quadrotor(x=5, y=5, z=5, roll=0,pitch=0, yaw=0, size=100, show_animation=True)
for i in range(50000):
data = struct.unpack('ffffff', tctimeClient.recv(buffsize))
# print(data)
# str = struct.pack("fff", pitch, roll,yaw)
if i%50==0:
q.update_pose(data[3],data[4],data[5],data[1],data[0],data[2])
q.plot()
print(data)
# [{'y': 8.86812294921875, 'x': -3.414131567382812, 'z': 1.6017209106445311}, {'y': 0.8931297709923665, 'x': -1.6870229007633588, 'z': 0.22137404580152673}, 25.941764705882356]