简介
前一段时间买了一块来自艾迈斯欧司朗dToF传感器TMF8821, 这个Time of flying sensor 支持测量多个Zone (区域),即可以获取到九个在被测平面上的区域的距离。 通过对距离的计算等(被测平面不光滑,比如说手)那么我们可以简单的实现一个粗略的手势识别即识别出手的上移, 下,左右,停止,接近和远离。 所以重点在于如何对距离进行计算。 那么在这篇文章中我将会对如何使用Machine learning的方法来进行介绍和实
现,通过阅读本篇文章你可以收获机器学习的相关知识和如何来使用SK-learn来训练一个你自己的模型。因此如果对数据来源不感兴趣的朋友们可以直接看数据的处理阶段(收集,清洗)训练和部署的过程。不需要看如何驱动TOF相关的。
物料清单
1- 树莓派4B
2- TMF8821
正文
首先,我们要处理的是如何将TOF传感器给驱动起来, 由于我们使用的是树莓派因此可以直接使用官方提供的python的库进行驱动。 首先打开树莓派来创建一个文件夹用于创建工程文件和虚拟环境。
mkdir tof
接着进入到文件夹中
cd tof
由于树莓派没办法直接用系统自带的pip安装包,所以我们需要创建一个虚拟环境。
python -m venv [虚拟环境名称,如下所示] python -m venv venv
接下来我们需要激活这个虚拟环境
source venv/bin/activate
之后我们便可以在这个虚拟环境安装tof的相关驱动
pip install tmf882x-driver
当驱动安装成功之后我们便可以来驱动这个tof传感器了。
在我们真正开始之前需要确保树莓派已经开启了I2C的外设。
raspi-config
在下述界面中开启I2C并且重启树莓派。
将Tof传感器接到树莓派的I2C1上。 下图为树莓派的引脚定义。
(图片来源:https://pinout.vvzero.com/)
接下来我们打开电脑的vscode并且安装远程插件。插件商店中搜索remote把这几个插件都装上方便连接远程服务器开发。
点击左下角的连接按钮,然后选择连接到主机,选择添加新的主机,并且添加连接信息。
vscode 会要求输入密码
在输入远程主机的密码后我们即可成功连接到远程主机。此时我们便可以打开之前创建的Tof目录。
我们可以创建一个python文件,输入下面的代码来测试Tof是否正常工作。
from smbus2 import SMBus from tmf882x import TMF882x with SMBus(1) as bus: device = TMF882x(bus=bus) device.enable() measurement = device.measure() print(measurement)
如果正常工作,输出那么将会与下面的数据类似。
此时我们可以看到,我们已经成功的拿到了上述的不同区域的距离信息。 接下来我们将进行数据的收集。我们知道,如果需要判断被测物体的运动方向的话至少需要两帧数据。 也就是18个数据点。 通过第一帧的数据和第二帧的数据进行计算然后可以算出被测物体的移动方向(被测物体需要凹凸不平)。
因此我们需要格式化程序的输出,使其每次输出9个点的数据。到控制台,然后在程序的正常运行期间。通过手的上,下,左,右,接近,原理,静止来采集实际的传感器数据。 这是一个非常繁琐的过程。 同时。我们需要记录对应数据的标签。即上下所有等。
我这里一共创建了两个文件 文件1 是training.txt 用于记录所有的距离信息数据。 如下图所示。
另一个则是每两行数据对应的标签信息。如下图所示
我这里使用 0 - 6 来标记不同的状态。各个状态如下所示。
0 静止
1 靠近
2 远离
3 右
4 左
5 下
6 上
Supervised learning(监督学习) 简介
Supervised learning是机器学习中的一种学习方式,它利用标注数据进行训练。在监督学习中,模型的训练过程是基于输入数据(特征)和相应的目标输出(标签或结果)进行的。模型通过学习这些输入和输出之间的映射关系,来预测未知数据的标签。
监督学习的两种主要任务:
分类(Classification):
目标是预测离散的标签或类别。
例如,垃圾邮件分类(判断电子邮件是否为垃圾邮件)或手写数字识别。
输出通常是一个类别标签,如“垃圾邮件”或“非垃圾邮件”。
回归(Regression):
目标是预测连续的数值结果。
例如,房价预测或股市趋势预测。
输出通常是一个连续的数值,如房屋价格或股价。
我们在这里的任务是一个分类问题。 根据输入的向量的不同特征来分类出被测物体的移动方向。 在这里我选择了使用scikit-learn 来训练我们的分类模型。 我这里选择的模型是使用随机森林。
Scikit-learn 安装
我们只需要在上述的虚拟环境中直接输入下面的命令即可成功安装SK-learn
pip3 install -U scikit-learn
使用下面的代码来训练我们的模型。
import numpy as np from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_splitimport joblib # 加载输入向量 def load_X(filename): with open(filename, 'r') as file: lines = file.readlines() X = [] for i in range(0, len(lines), 2): first_line = list(map(int, lines[i].strip().split())) second_line = list(map(int, lines[i + 1].strip().split())) sample = first_line + second_line X.append(sample) return np.array(X) # 加载分类标签 def load_Y(filename): with open(filename, 'r') as file: Y = [int(line.strip()) for line in file.readlines()] return np.array(Y) training_file = 'training.txt' tag_file = 'tag.txt' #加载文件 X = load_X(training_file)Y = load_Y(tag_file) #打印形状 print("Shape of X:", X.shape) print("Shape of Y:", Y.shape) #分割训练集和测试集 X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=50)print(Y_test) print("Shape of X_train:", X_train.shape)print("Shape of X_test:", X_test.shape) #创建模型 clf = RandomForestClassifier(n_estimators=10, random_state=42) #训练 clf = clf.fit(X_train, Y_train) #保存模型 model_filename = 'random_forest_model.pkl' joblib.dump(clf, model_filename) print(f"Model saved to {model_filename}") loaded_clf = joblib.load(model_filename)print("Loaded model:", loaded_clf) #进行预测 predictions = loaded_clf.predict(X_test)print("Predictions on test data:", predictions)print("True labels for test data:", Y_test) #统计准确率 accuracy = np.mean(predictions == Y_test)print(f"Accuracy on test data: {accuracy:.2f}")
当上述代码成功运行的时候,那么便可以生成我们的测试模型。 程序输出如下图所示。
看起来正确率挺高的。 接下来我们可以使用下面的代码来加载我们的模型来使用训练好的模型进行推理。并且每次读取tof的距离然后转换成向量输入到模型进行推理。
import numpy as np from smbus2 import SMBus from tmf882x import TMF882x import joblib # 用于保存和加载模型 import time # 加载模型 model_filename = 'random_forest_model.pkl' loaded_clf = joblib.load(model_filename) print("Loaded model:", loaded_clf) # 读取并提取测量的距离值 def get_distances(): with SMBus(1) as bus: device = TMF882x(bus=bus) device.enable() measurement = device.measure() # 提取 'distance' 值 distances = [result.distance for result in measurement.results if result.confidence > 0] return distances while True: # 获取两次测量的距离数据 distances_1 = get_distances() distances_2 = get_distances() # 确保每次测量数据有 9 个有效值 if len(distances_1) == 9 and len(distances_2) == 9: # 将两次测量数据组合成一个特征向量 feature_vector = distances_1 + distances_2 print("Feature vector:", feature_vector) # 使用模型进行预测 prediction = loaded_clf.predict([feature_vector]) print(f"Prediction: {prediction[0]}") else: print("Each measurement must have exactly 9 valid distance values.")
程序输出结果如下:
手势识别到此结束。
反思
目前看来虽然模型的精度看起来还不错,但是在部分的方向上会出现过模型过拟合的情况。 在部分的方向上又会出现欠拟合的情况。 这次训练一共是使用了700条数据, 数据量还是偏小并且没有涵盖很多方面。造成了虽然测试的时候模型精度很好,但是在实际识别的时候准确度欠佳的问题。解决办法是获取更多更高精度的数据并且进行模型的重新训练。但是由于数据的收集是一个非常耗时的过程。这一部分内容只能有时间等到下载再做尝试了。
参考内容链接
(SK-learn 官网, 如何安装SK-learn)https://scikit-learn.org/stable/install.html
(随机森林) https://scikit-learn.org/stable/modules/ensemble.html
附件