最通俗易懂的sklearn机器学习入门实践之根据温度和湿度判断天气(Python+Arduino)

最近上手了DHT11湿度模块以及DS18B20测温模块,于是想着能不能用温度和湿度这两个数据做些什么。我们知道:

温度与湿度在一定程度上和天气有关,下雨时,湿度会高一些;晴天时,湿度会略低一些

因此我们可以根据这一点来判断天气,下面是这次实践的步骤:

  1. 使用Arduino获取温度与湿度数据
  2. 使用正则表达式清洗数据并保存到MySQL
  3. 构建SVM分类器并训练模型
  4. 把模型应用到Arduino实时检测天气上

在这里插入图片描述

使用Arduino获取温度与湿度数据

获取数据首先需要一些硬件:
  1. Arduino UNO开发板(其他型号的也ok)
  2. DS18B20测温模块
  3. DHT11湿度模块(自带测温模块,但是精度没有DS18B20高)

如果没有的话,我们也可以通过网络爬虫获取一些信息:
抓取中国天气网当前时段所有城市的天气数据(python+xpath)

有了硬件以后我们便可以开始了,首先是连线:

湿度模块

测温模块

在这里插入图片描述
连线方式很简单,首先GND表示接地,接到开发板上任意的GND即可

VCC可以理解为电源,开发板上有3.3V和5V两种,接到哪一个都行

剩下的就是一些数据传输的接口了:
在这里插入图片描述
这里有一排接口,随便接哪个都OK的(请自行忽略那个帽子掉了的杜邦线)

接下来我们打开Arduino的IDE,先导入两个资源包:

#include <DallasTemperature.h>
#include <dht11.h>

没有资源包?!哈哈哈,正常正常,来这里下载就好了:
在这里插入图片描述
什么?怎么打开?别着急,在这:
在这里插入图片描述
啥?下载不了?!出现以下报错:
在这里插入图片描述
这时我们可以去GitHub上下载,在GitHub上fork这个项目,然后转存到码云的仓库下载,亲测有用

下面我们把实现功能的代码填进去:

导入资源包及定义引脚
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4      //1-wire数据总线连接在IO4
OneWire oneWire(ONE_WIRE_BUS);       //声明
DallasTemperature sensors(&oneWire); //声明
#include <dht11.h>       //引用dht11库文件,使得下面可以调用相关参数
#define DHT11PIN 2   //定义温湿度针脚号为2号引脚
dht11 DHT11;      //实例化一个对象
初始化设置
void setup(void)
{
  Serial.begin(9600);
  Serial.println("");
  sensors.begin(); //初始化总线
  sensors.setWaitForConversion(false); //设置为非阻塞模式
  pinMode(DHT11PIN,OUTPUT); //定义湿度模块输出口
}
循环体
unsigned long previousMillis = 0; //毫秒时间记录
const long interval = 5000;       //时间间隔
void loop(void)
{
  //以下段落相当于每秒读取前次温度,并发起新一次温度转换
  unsigned long currentMillis = millis();         //读取当前时间
  int chk = DHT11.read(DHT11PIN);                 //将读取到的值赋给chk
  int tem=(float)DHT11.temperature;               //将温度值赋值给tem
  int hum=(float)DHT11.humidity;                   //将湿度值赋给hum
  if (currentMillis - previousMillis >= interval) //如果和前次时间大于等于时间间隔
  {
    previousMillis = currentMillis; //更新时间记录

    float tempC = sensors.getTempCByIndex(0); //获取索引号0的传感器摄氏温度数据
    if (tempC != DEVICE_DISCONNECTED_C)       //如果获取到的温度正常
    {
      Serial.print("temputer:");
      Serial.print(tempC);
      Serial.print("℃");
      Serial.print(",");
    }
    sensors.requestTemperatures(); //发起新的温度转换
//    Serial.print("Tempeature:");                        //打印出Tempeature:
//    Serial.println(tem);                                     //打印温度结果
    Serial.print("humidity:");                            //打印出Humidity:
    Serial.print(hum);                                     //打印出湿度结果
    Serial.print("%");                                  //打印出%
  }
  delay(5000);
}

下面是输出结果:
在这里插入图片描述
很好,现在已经可以获取温度和湿度数据了!

使用正则表达式清洗数据并保存到MySQL

这时我们需要用到python,先来读取以下串口的数据:
在这里插入图片描述

import serial

serialPort = "COM6"  # 串口
baudRate = 9600  # 波特率
ser = serial.Serial(serialPort, baudRate, timeout=0.5)
print("参数设置:串口=%s ,波特率=%d" % (serialPort, baudRate))

while True:
    str = ser.readline().decode('utf-8')
    if str.strip()!='':
        print(str)

这里设置了一个条件判断,不为空行时才输出,不然输出时会有很多空行

输出时,带有一些文字,这时我们用正则表达式处理,把文字去掉,只留数值加上符号:
在这里插入图片描述
先把逗号前面的内容捕获,然后把temputer以及冒号出去,最后把最后面的逗号也除去即可:

temputer = re.findall("[\S]+,",str)
temputer = re.sub(r'temputer:', "",temputer[0])
temputer = re.sub(r',', "",temputer)

湿度的处理方法同上:

humidity = re.findall("humidity:[\S]+",str)
humidity = re.sub(r'humidity:', "", humidity[0])

下面我们只需要把这些数据存到数据库即可,先进入MySQL建个表:

CREATE TABLE `rain` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `temputer` CHAR(10) DEFAULT NULL,
  `humidity` CHAR(10) DEFAULT NULL,
  `updata_time` CHAR(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) 

设置4个属性,分别是id,温度,湿度以及更新时间,下面我们用python把数据存进去:

连接数据库
connection = pymysql.connect(host='localhost',
                            port=3306,
                            user='root',
                            password='root',
                            db='arduino_1',
                            charset='utf8')
cursor = connection.cursor()
print("已成功连接至数据库!")
存入数据
sql = 'insert into table1(temputer,humidity,updata_time)VALUES(%s,%s,%s)'
cursor.execute(sql,[temputer,humidity,updata_time])
connection.commit()
#connection.close()

connection.close()请自行放在循环体外,数据全部存入后再执行该条语句

来看下数据表:
在这里插入图片描述
因为是每5秒获取一次数据,所以变化不大,但是如果放一天,还是能看出变化的:

SELECT * FROM table1 GROUP BY table1.`humidity`

在这里插入图片描述
可以看出,从早上到中午,温度越来越高,而湿度是越来越低

构建SVM分类器并训练模型

这里我们需要用到一个机器学习库sklearn:

import numpy as np
from sklearn import svm
from sklearn import model_selection

创建它很简单:

def classifier():
    # SVM分类器构建
    clf = svm.SVC(C=0.5,  #误差项惩罚系数,默认值是1
                  kernel='linear',  #线性核 kenrel="rbf":高斯核
                  decision_function_shape='ovr')  #决策函数
    return clf

难就难在我们怎么导入数据?

这里我手动对数据做了处理,添加上了标签:
在这里插入图片描述
数据量太大,就不全部展示了哈哈哈,简单介绍一下,这里有四个标签:

  • Sunny
  • Cloudy
  • Rainy
  • Snowy

为了便于数据加载,这里我们把标签转换成数值:

def weather_type(s):
    # 将字符串转为整型,便于数据加载
    weather = {b'Sunny':0, b'Cloudy':1, b'Rainy':2, b'Snowy':3}
    return weather[s]

下面我们处理好的数据读取进来:

def load_data():
    #加载数据
    data_path = r'F:\csdn\arduino判断天气\weather_data.csv'#数据文件的路径
    data = np.loadtxt(open(data_path,'r',encoding='utf-8'),
                      delimiter=',', #数据分隔符
                      converters={3:weather_type}) #将第4列使用函数weather_type进行转换
    # print(data)
    # print(data.shape)
    #数据分割
    x, y = np.split(data, #要切分的数组
                    (3,), #沿轴切分的位置,第3列开始往后为y
                    axis=1) #代表纵向分割,按列分割
    x = x[:, 1:3]   #在X中我们取前两列作为特征,为了后面的可视化。x[:,0:4]代表第一维(行)全取,第二维(列)取0~2
    # print(x)
    x_train,x_test,y_train,y_test=model_selection.train_test_split(x,              #所要划分的样本特征集
                                                                   y,              #所要划分的样本结果
                                                                   random_state=1, #随机数种子
                                                                   test_size=0.3)  #测试样本占比
    # print(y_train)
    return x_train,x_test,y_train,y_test

有了数据以后,就可以开始训练了:

def train(clf,x_train,y_train):
    clf.fit(x_train,         #训练集特征向量
            y_train.ravel()) #训练集目标值

计算一下各项指标:

def show_accuracy(test, result, tip):
    acc = test.ravel() == result.ravel()
    print('%s Accuracy:%.3f' %(tip, np.mean(acc)))

def print_accuracy(clf,x_train,y_train,x_test,y_test):
    #分别打印训练集和测试集的准确率  score(x_train,y_train):表示输出x_train,y_train在模型上的准确率
    print('trianing prediction:%.3f' %(clf.score(x_train, y_train)))
    print('test data prediction:%.3f' %(clf.score(x_test, y_test)))
    #原始结果与预测结果进行对比   predict()表示对x_train样本进行预测,返回样本类别
    show_accuracy(clf.predict(x_train), y_train, 'traing data')
    show_accuracy(clf.predict(x_test), y_test, 'testing data')

最后在主函数里整体调用:

def main():
    clf = classifier()
    x_train,x_test,y_train,y_test = load_data()[0],load_data()[1],load_data()[2],load_data()[3]
    train(clf,x_train,y_train)
    print_accuracy(clf,x_train,y_train,x_test,y_test)

if __name__ == '__main__':
    main()

来看一下准确率:
在这里插入图片描述
接近100%了,看来模型效果不错,下面我们来实际测试一下

把模型应用到Arduino实时检测天气上

def main():
    clf = classifier()
    x_train,x_test,y_train,y_test = load_data()[0],load_data()[1],load_data()[2],load_data()[3]
    train(clf,x_train,y_train)
    print_accuracy(clf,x_train,y_train,x_test,y_test)
    data_test = (-5.23,70) #input temputer and humidity
    data_test = np.array(data_test).reshape(1, -1)
    result = ['Sunny', 'Cloudy', 'Rainy', 'Snowy']
    test = clf.predict(data_test)
    print(50*"-")
    print("The current weather is {}".format(result[int(test[0])]))

if __name__ == '__main__':
    main()

在这里插入图片描述
这是最基础版的,下面我们把Arduino返回的数据导入:

def main():
    serialPort = "COM6"  # 串口
    baudRate = 9600  # 波特率
    ser = serial.Serial(serialPort, baudRate, timeout=0.5)
    # print("参数设置:串口=%s ,波特率=%d" % (serialPort, baudRate))
    clf = classifier()
    x_train,x_test,y_train,y_test = load_data()[0],load_data()[1],load_data()[2],load_data()[3]
    train(clf,x_train,y_train)
    print_accuracy(clf,x_train,y_train,x_test,y_test)
    while True:
        str = ser.readline().decode('utf-8')
        if str.strip()!='':
        	print(50*"-")
            print(str)
            temputer = re.findall("[\S]+,",str)
            temputer = re.sub(r'temputer:', "",temputer[0])
            temputer = re.sub(r'℃,', "",temputer)
            humidity = re.findall("humidity:[\S]+",str)
            humidity = re.sub(r'humidity:', "", humidity[0])
            humidity = re.sub(r'%', "", humidity)
            data_test = (temputer,humidity) #input temputer and humidity
            data_test = np.array(data_test).reshape(1, -1)
            result = ['Sunny', 'Cloudy', 'Rainy', 'Snowy']
            test = clf.predict(data_test)
            print("The current weather is {}".format(result[int(test[0])]))
    ser.close()

if __name__ == '__main__':
    main()

结合了前面几部分的内容:
在这里插入图片描述
这样便可以实时地输出天气结果了!

准确率?现在外面可是在下雨噢!不信?加我微信,我拍视频给你看哈哈哈

顺带提一句,最近自己在做的公众号,名字叫"情感卫生间",公众号内搭建了智能对话机器人"小柒",最近工作压力大?不如找"小柒"聊聊?"小柒"可是个有温暖的人工智能噢!
在这里插入图片描述
另外,如需本次实践所需数据,可添加公众号管理员免费获取!

发布了60 篇原创文章 · 获赞 123 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/zbp_12138/article/details/104902372