概述
在实验的第二部分,面包板上同时建立了五个传感器,每个模块都建立了一个文件,并为每个模块创建了一个类别。另外另一个用途QT Designer设计生成的一个界面类。在主文件中实例化5个传感器类,创建5个子线程同时测量和显示5个传感器,主线程实时显示整个界面。整个实验用python编写和使用界面代码编写pyqt5库。
一、mpu6050模块
1、整体思路 使用树莓派IIC与mpu通信6050,读取mpu6050姿态角信息,因为mpu6050偏航角需要通过磁力计准确测量,所以本实验只测量mpu6050的俯仰角(pitch)和翻滚角(roll)。考虑到树莓派的运行内存,mpu采用简单的一阶互补滤波计算6050姿态角。Mpu6050类应该有计算方法pitch和roll并返回。 2、实验步骤 ①接线
树莓派 | mpu6050 |
---|---|
3.3V | VCC |
GND | GND |
P02 | SDA |
P03 | SCL |
②代码编写 一级互补滤波原理: mpu6050读取的加速度计和陀螺仪的原始值各有优缺点,只能通过陀螺仪或加速度计来计算pitch角和roll角,可以通过下面的式子将加速度计和陀螺仪的数据都融合到姿态角的计算当中: Angle = kacc_angle (1-k)(last_acc_angle gy*dt) 其中,Angle 为最终得到的角度,acc_angle计算这个周期加速度的角度,last_acc_angle计算上一个采样周期加速度的角度,gy为陀螺仪计算的角加速度,dt采样周期,k占比系数。dt测量越小,准确。
import smbus # 导入I2C的SMBus模块 from time import sleep # 导入延时函数 import math class Mpu6050_Class(): def __init__(self): # MPU 6050 初始化工作 # 一些MPU6050寄存器及其地址 self.PWR_MGMT_1 = 0x6B self.SMPLRT_DIV = 0x19 self.CONFIG = 0x1A self.GYRO_CONFIG = 0x1B self.INT_ENABLE = 0x38 self.ACCEL_XOUT_H = 0x3B self.ACCEL_YOUT_H = 0x3D self.ACCEL_ZOUT_H = 0x3F self.GYRO_XOUT_H = 0x43 self.GYRO_YOUT_H = 0x45 self.GYRO_ZOUT_H = 0x47 self.makerobo_bus = smbus.SMBus(1) # 或bus = smbus.SMBus(0)旧版本板 self.makerobo_Device_Address = 0x68 # MPU6050设备地址 # 写入抽样速率寄存器 self.makerobo_bus.write_byte_data(self.makerobo_Device_Address, self.SMPLRT_DIV, 7) # 写入电源管理寄存器 self.makerobo_bus.write_byte_data(self.makerobo_Device_Address, self.PWR_MGMT_1, 1)
# 写入配置寄存器
self.makerobo_bus.write_byte_data(self.makerobo_Device_Address, self.CONFIG, 0)
# 写入陀螺配置寄存器
self.makerobo_bus.write_byte_data(self.makerobo_Device_Address, self.GYRO_CONFIG, 24)
# 写中断使能寄存器
self.makerobo_bus.write_byte_data(self.makerobo_Device_Address, self.INT_ENABLE, 1)
# 读取MPU6050数据寄存器
def makerobo_read_raw_data(self,addr):
# 加速度值和陀螺值为16位
high = self.makerobo_bus.read_byte_data(self.makerobo_Device_Address, addr)
low = self.makerobo_bus.read_byte_data(self.makerobo_Device_Address, addr + 1)
# 连接更高和更低的值
value = ((high << 8) | low)
# 从mpu6050获取有符号值
if (value > 32768):
value = value - 65536
return value
last_pitch=0
last_roll=0
def get_data(self):
# 读取加速度计原始值
acc_x = self.makerobo_read_raw_data(self.ACCEL_XOUT_H)
acc_y = self.makerobo_read_raw_data(self.ACCEL_YOUT_H)
acc_z = self.makerobo_read_raw_data(self.ACCEL_ZOUT_H)
# 读陀螺仪原始值
gyro_x = self.makerobo_read_raw_data(self.GYRO_XOUT_H)
gyro_y = self.makerobo_read_raw_data(self.GYRO_YOUT_H)
gyro_z = self.makerobo_read_raw_data(self.GYRO_ZOUT_H)
# 全刻度范围+/- 250度/℃,根据灵敏度刻度系数
self.ax = acc_x / 16384.0
self.ay = acc_y / 16384.0
self.az = acc_z / 16384.0
self.gx = gyro_x / 131.0
self.gy = gyro_y / 131.0
self.gz = gyro_z / 131.0
#########################################一阶互补滤波姿态角解算###############################
''' k=0.1 dt=0.5#dt为采样周期,0.01说明要10ms执行一次数据采集 acc_pitch=math.atan(self.ax/self.az)*57.2974 acc_roll=math.atan(self.ay/self.az)*57.2974 self.pitch=k*acc_pitch+(1-k)*(self.last_pitch+self.gx*dt) self.last_pitch=self.pitch self.roll=k*acc_roll+(1-k)*(self.last_roll+self.gy*dt) self.last_roll=self.roll '''
acc_pitch=math.atan(self.ax/self.az)*57.2974
acc_roll=math.atan(self.ay/self.az)*57.2974
self.pitch=acc_pitch
self.roll=acc_roll
''' if __name__ == "__main__": fle_mpu6050 = Mpu6050_Class() #print("%.2f"%fle_mpu6050.Ax) while True: # 打印出MPU相关信息 fle_mpu6050.get_data() print("pitch=%.2f" % fle_mpu6050.pitch, "\troll=%.2f" % fle_mpu6050.roll) sleep(0.01) # 延时10ms '''
3、测试用例结果 ①模块水平于桌面放置: 模块水平放置时,俯仰角理论值为0,翻滚角理论值为0,图中pitch和roll均与理论值相近。 ②模块垂直于桌面放置: 模块垂直放置时,俯仰角理论值为90°,翻滚角理论值为0,图中pitch和roll均与理论值相近。 ③模块与桌面成45°角放置: 模块与桌面成45°角放置时,俯仰角理论值为0,翻滚角理论值为45°,图中pitch和roll均与理论值相近。
二、HC-SR04超声波模块
1、整体思路 HC-SR04应该有一个方法计算距离并返回。 2、实验步骤 ①接线
树莓派 | HC-SR04模块 |
---|---|
+5V | VCC |
GND | GND |
P05 | Trig |
P06 | Echo |
②代码编写
import RPi.GPIO as GPIO
import time
makerobo_TRIG = 29 # 超声波模块Tring控制管脚
makerobo_ECHO = 31 # 超声波模块Echo控制管脚
class HC_SR04_Class():
def __init__(self): # MPU 6050 初始化工作
GPIO.setmode(GPIO.BOARD) # 采用实际的物理管脚给GPIO口
GPIO.setwarnings(False) # 忽略GPIO操作注意警告
GPIO.setup(makerobo_TRIG, GPIO.OUT) # Tring设置为输出模式
GPIO.setup(makerobo_ECHO, GPIO.IN) # Echo设置为输入模式
# 超声波计算距离函数
def ur_disMeasure(self):
GPIO.output(makerobo_TRIG, 0) # 开始起始
time.sleep(0.000002) # 延时2us
GPIO.output(makerobo_TRIG, 1) # 超声波启动信号,延时10us
time.sleep(0.00001) # 发出超声波脉冲
GPIO.output(makerobo_TRIG, 0) # 设置为低电平
while GPIO.input(makerobo_ECHO) == 0: # 等待回传信号
us_a = 0
us_time1 = time.time() # 获取当前时间
while GPIO.input(makerobo_ECHO) == 1: # 回传信号截止信息
us_a = 1
us_time2 = time.time() # 获取当前时间
us_during = us_time2 - us_time1 # 转换微秒级的时间
# 声速在空气中的传播速度为340m/s, 超声波要经历一个发送信号和一个回波信息,
# 计算公式如下所示:
return us_during * 340 / 2 * 100 # 求出距离
# 资源释放函数
def destroy(self):
GPIO.cleanup() # 释放资源
''' # 程序入口 if __name__ == "__main__": Hardware_HC_SR04=HC_SR04_Class() while True: us_dis = Hardware_HC_SR04.ur_disMeasure() # 获取超声波计算距离 print(us_dis, 'cm') # 打印超声波距离值 print('') time.sleep(0.3) # 延时300ms '''
3、测试用例结果 ①距离10cm: 由上面两张图片可知误差不超过1cm ②距离20cm: 由上面两张图片可知误差不超过1cm
三、3mm双色LED模块
1、整体思路 双色LED类应该能够向外提供两个方法,分别设置红灯和绿灯的PWM。 2、实验步骤 ①接线
树莓派 | 3mm双色LED模块 |
---|---|
GND | 负极(-) |
P17 | 红色灯正极(中间引脚) |
P18 | 绿色灯正极 |
②代码编写 编写两个函数分别调整两个灯的亮度。
import RPi.GPIO as GPIO
import time
class Double_LED_Class:
def __init__(self): # double_led 初始化工作
makerobo_pins = (11, 12) # PIN管脚字典
GPIO.setmode(GPIO.BOARD) # 采用实际的物理管脚给GPIO口
GPIO.setwarnings(False) # 去除GPIO口警告
GPIO.setup(makerobo_pins, GPIO.OUT) # 设置Pin模式为输出模式
GPIO.output(makerobo_pins, GPIO.LOW) # 设置Pin管脚为低电平(0V)关闭LED
self.p_R = GPIO.PWM(makerobo_pins[0], 2000) # 设置频率为2KHz
self.p_G = GPIO.PWM(makerobo_pins[1], 2000) # 设置频率为2KHz
# 初始化占空比为0(led关闭)
self.p_R.start(0)
self.p_G.start(0)
def makerobo_pwm_map(self,x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
def makerobo_set_red_Color(self,col): # 例如:col = 0x1122
# 把0-255的范围同比例缩小到0-100之间
R_val = self.makerobo_pwm_map(col, 0, 255, 0, 100)
self.p_R.ChangeDutyCycle(R_val) # 改变占空比
def makerobo_set_green_Color(self,col): # 例如:col = 0x1122
# 把0-255的范围同比例缩小到0-100之间
G_val = self.makerobo_pwm_map(col, 0, 255, 0, 100)
self.p_G.ChangeDutyCycle(G_val) # 改变占空比
# 释放资源
def makerobo_destroy(self):
self.p_G.stop()
self.p_R.stop()
GPIO.output(self.makerobo_pins, GPIO.LOW) # 关闭所有LED
GPIO.cleanup() # 释放资源
3、测试用例结果 3s后变为下面的图片 程序中首先亮红灯,然后用time.sleep(3)延时了3s,再关闭红灯,亮绿灯,测试结果与理论相符。
四、DS18B20测温模块
1、整体思路 DS18B20类应该能够向外提供获取温度的方法。 2、实验步骤 ①接线
树莓派 | DS18B20 |
---|---|
GND | 负极(-) |
+5V | 正极(中间引脚) |
P04 | 输出引脚(S) |
②代码编写 由于DS18B20是在一个文件中读取温度值,为了DS18B20能较为快速地读取温度值,所以在get_temperature方法中不频繁打开和关闭文件,在初始化中打开文件,不再关闭文件,但是每次DS18B20刷新文件内容,如果不执行关闭重开文件的操作的话,文件的光标指针将会指向换行符,导致该方法只能执行一次,所以每次在进入该方法之前先定位光标到69的位置,69由下图得出:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # ----湖南创乐博智能科技有限公司---- # 文件名:25_ds18b20.py # 版本:V2.0 # author: zhulin # 说明: DS18B20数字
温度传感器实验 # 注意事项:DS18B20有唯一的地址,一般为28-XXXXXX ##################################################### import os class DS18B20_Class(): def __init__(self): for i in os.listdir('/sys/bus/w1/devices'): if i == '28-0317039468ff': self.makerobo_ds18b20 = i # ds18b20存放在ds18b20地址 makerobo_location = '/sys/bus/w1/devices/' + self.makerobo_ds18b20 + '/w1_slave' # 保存ds18b20地址信息 self.makerobo_tfile = open(makerobo_location) # 打开ds18b20 def get_temperature(self): self.makerobo_tfile.seek(69,0) makerobo_text = self.makerobo_tfile.read() # 读取到温度值 temperature = float(makerobo_text) temperature = temperature / 1000 return temperature ''' # 程序入口 if __name__ == '__main__': DS18B20=DS18B20_Class() while True: temperature=DS18B20.get_temperature() print("温度:%.2f\u00b0C"%temperature) '''
3、测试用例结果 ①手指不触碰DS18B20 ②手指触碰DS18B20 由上面两图可知,手指触碰DS18B20后测量到的温度逐渐升高,与理论相符。
五、KEYES金属触摸传感器模块
1、整体思路 当有东西触摸到金属触摸传感器时,金属触摸传感器的A0引脚会输出一个模拟电压,当不触碰传感器时A0输出的模拟电压为金属触摸传感器的VCC,当触碰传感器时A0输出的模拟电压低于金属触摸传感器的VCC。由于树莓派没有ADC模块,所以使用ADS1115模块进行ADC采集。使用Adafruit_ADS1x15库对ADS1115模块进行操作。 2、实验步骤 ①接线
树莓派 | ADS1115 |
---|---|
+3.3V | VCC |
GND | GND |
GND | ADDR |
P02 | SDA |
P03 | SCL |
ADS1115 | KEYES金属触摸传感器 |
---|---|
VCC | VCC |
GND | GND |
A0(通道0,总共四个通道) | A0 |
ADS1115和mpu6050共用一个IIC,但是可以对模块的地址来区分,ADS1115和mpu6050两个子线程同时开启时,会产生卡顿现象。另外,ADS1115的ADDR接GND时ADS1115的IIC地址为0x48。同时接ADS1115和mpu6050时可以读到如下图所示两个设备: ②代码编写
import Adafruit_ADS1x15
import time
class KEYES_Class():
def __init__(self):
self.GAIN = 1
self.adc1 = Adafruit_ADS1x15.ADS1115(address=0x48)
def get_voltage(self):
voltage=self.adc1.read_adc(0, gain=self.GAIN, data_rate=128)
voltage=voltage*4.096*2/65535
return voltage
''' # 程序入口 if __name__ == '__main__': KEYES=KEYES_Class() while True: voltage=KEYES.get_voltage() print("电压:%.2fV"%voltage) '''
3、测试用例结果 ①手指不触碰传感器上的金属 金属触摸传感器的VCC与树莓派的+3.3V相连,用万用表测量金属触摸传感器的VCC引脚结果如上面右图所示,测量值3.23与理论值3.22相近 ②手指触碰传感器上的金属 当手指触摸金属传感器时,A0引脚的电压变小,与理论相符。
六、界面类
利用QT Designer设计界面如下:
mpu6050:用mpu6050获得的数据画一个三维图像来显示姿态角,而三维图像用groupBox组件来呈现。 HC-SR04:与mpu6050类似,用HC-SR04获得的数据画一个三维图像来显示距离,而三维图像用groupBox组件来呈现。 3mm双色LED:用两条滑动条来控制两个灯的PWM,范围为0~255。 DS18B20:用一个TextLabel来接受采集的温度值并显示。 金属触摸模块:用一个TextLabel来接受采集的电压值并显示。
最右下角为退出主界面的控件,另外每一个模块都配套了一个按钮,用于打开或关闭线程,防止树莓派运行卡死。 用PYUIC扩展工具可以生成python代码,从而可以在Window端设计界面,将代码拷贝到树莓派上运行,而树莓派不需要安装QT Designer。生成的代码如下:
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(924, 769) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox.setGeometry(QtCore.QRect(580, 720, 341, 32)) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.mpu6050_groupBox = QtWidgets.QGroupBox(Dialog) self.mpu6050_groupBox.setGeometry(QtCore.QRect(36, 106, 421, 381)) self.mpu6050_groupBox.setTitle("") self.mpu6050_groupBox.setObjectName("mpu6050_groupBox") self.red_horizontalSlider = QtWidgets.QSlider(Dialog) self.red_horizontalSlider.setGeometry(QtCore.QRect(110, 590, 160, 22)) self.red_horizontalSlider.setMaximum(255) self.red_horizontalSlider.setOrientation(QtCore.Qt.Horizontal) self.red_horizontalSlider.setObjectName("red_horizontalSlider") self.double_red_led_label = QtWidgets.QLabel(Dialog) self.double_red_led_label.setGeometry(QtCore.QRect(156, 620, 72, 15)) font = QtGui.QFont() font.setFamily("黑体") self.double_red_led_label.setFont(font) self.double_red_led_label.setObjectName("double_red_led_label") self.HC_SR04_groupBox = QtWidgets.QGroupBox(Dialog) self.HC_SR04_groupBox.setGeometry(QtCore.QRect(486, 106, 411, 381)) self.HC_SR04_groupBox.setTitle("") self.HC_SR04_groupBox.setObjectName("HC_SR04_groupBox") self.mpu6050_label = QtWidgets.QLabel(Dialog) self.mpu6050_label.setGeometry(QtCore.QRect(195, 492, 191, 21)) font = QtGui.QFont() font.setFamily("黑体") self.mpu6050_label.setFont(font) self.mpu6050_label.setObjectName("mpu6050_label") self.HC_SR04_label = QtWidgets.QLabel(Dialog) self.HC_SR04_label.setGeometry(QtCore.QRect(615, 492, 231, 21)) font = QtGui.QFont() font.setFamily("黑体") self.HC_SR04_label.setFont(font) self.HC_SR04_label.setObjectName("HC_SR04_label") self.mpu6050_pushButton = QtWidgets.QPushButton(Dialog) self.mpu6050_pushButton.setGeometry(QtCore.QRect(205, 522, 93, 28)) self.mpu6050_pushButton.setObjectName("mpu6050_pushButton") self.HC_SR04_pushButton = QtWidgets.QPushButton(Dialog) self.HC_SR04_pushButton.setGeometry(QtCore.QRect(655, 522, 93, 28)) self.HC_SR04_pushButton.setObjectName("HC_SR04_pushButton") self.double_led_pushButton = QtWidgets.QPushButton(Dialog) self.double_led_pushButton.setGeometry(QtCore.QRect(136, 650, 93, 28)) self.double_led_pushButton.setObjectName("double_led_pushButton") self.DS18B20_label = QtWidgets.QLabel(Dialog) self.DS18B20_label.setGeometry(QtCore.QRect(492, 586, 131, 20)) font = QtGui.QFont() font.setFamily("黑体") self.DS18B20_label.setFont(font) self.DS18B20_label.setObjectName("DS18B20_label") self.DS18B20_label2 = QtWidgets.QLabel(Dialog) self.DS18B20_label2.setGeometry(QtCore.QRect(341, 586, 181, 21)) font = QtGui.QFont() font.setFamily("黑体") self.DS18B20_label2.setFont(font) self.DS18B20_label2.setObjectName("DS18B20_label2") self.DS18B20_pushButton = QtWidgets.QPushButton(Dialog) self.DS18B20_pushButton.setGeometry(QtCore.QRect(426, 650, 93, 28)) self.DS18B20_pushButton.setObjectName("DS18B20_pushButton") self.KEYES_label_2 = QtWidgets.QLabel(Dialog) self.KEYES_label_2.setGeometry(QtCore.QRect(676, 590, 72, 15)) font = QtGui.QFont() font.setFamily("黑体") self.KEYES_label_2.setFont(font) self.KEYES_label_2 标签:
p492传感器