一、研究背景
在一次体育课上,教师说通过反复进行摸高训练,可提升运动能力,如爆发力、弹跳力等,促进身体发育,改善健康,还能增强自信、培养毅力。但学校没有摸高的设备,就在网上查询摸高设备或器材,通过查询,有各种各样的摸高设备,有的可以播报摸高个数。结合学校开展开源硬件社团活动,开始构思能不能对摸高设备进行智能化设计。通过进一步查阅资料,目前的摸高设备是固定高度的,摸高高度需要测量,没有摸高数据处理功能,也没有存储数据并对数据进行对比,无法为用户提供详细的运动评估。
二、研究目的
本研究旨在开发一种创新的基于物联网的智能摸高装置,实现精准的摸高高度检测、全面的数据处理与记录、便捷的物联网数据传输以及智能化的语音交互控制。通过整合多种先进技术,为用户提供准确、高效且便捷的摸高运动体验,帮助用户更好地了解自身运动状况,科学制定运动计划,促进健康生活方式的养成。
三、研究内容
(一)高度检测技术融合
学习超声波传感器的工作原理,优化其测量算法,提高超声波检测摸高高度的精度和稳定性。设计并搭建激光二极管与光敏传感器的检测系统,确定合理的激光发射与光敏传感器布局,实现精准的摸高高度判断和次数统计。
(二)数据处理与记录
开发基于 ESP32 S3 单片机的数据处理程序,对超声波和激光二极管与光敏传感器的检测得到的数据进行实时融合与分析,计算每次摸高的高度。
建立数据存储机制,实现对所有摸高数据的自动记录,并设计算法计算平均高度、最高高度和摸高次数等关键指标。
(三)物联网数据传输
研究 ESP32 S3 模块与 blynk 服务器及手机 APP 的通信协议,实现摸高数据的实时、稳定传输。在手机 APP 端开发用户界面,实现历史数据对比、数据图表展示等功能,方便用户直观了解自身运动情况。
(四)语音控制功能
集成语音识别模块,使其能够准确识别用户的语音指令,控制摸高装置的开始与结束。优化语音控制的响应速度和准确性,提升用户在运动场景中的使用体验。
(五)文字转语音功能
选用合适的文字转语音模块,将摸高数据转换为语音信号,实现高度、次数等数据信息的实时播报。调整语音播报的语速、语调,提高用户对播报信息的接收效果。
四、研究方法
文献研究法:查阅国内外关于超声波检测、激光检测、物联网技术、语音识别与合成等方面的文献资料,了解相关技术的研究现状和发展趋势,为项目开发提供理论支持。
实验研究法:搭建实验平台,对超声波传感器、激光二极管与光敏传感器的检测性能进行测试,优化传感器的参数和布局。同时,对语音识别模块、文字转语音模块以及物联网数据传输功能进行实验验证,通过多次实验收集数据,分析实验结果,改进系统性能。
系统设计与开发法:根据研究目标和需求,进行智能摸高装置的系统设计,包括硬件选型与电路设计、软件编程与算法开发等。采用模块化设计思想,将整个系统分为检测模块、数据处理模块、物联网传输模块、语音控制模块和文字转语音模块,分别进行开发与调试,最后进行系统集成与优化。
五、研究过程
(一)准备摸高支架、单片机、传感器
选择和购买摸高支架,选择和购买ESP32 S3 开发板及相应传感器,部分器材借用社团活动室的器材。选择支架主要考虑有底座、可伸缩的竖杆做为支撑、横杆用于搭建相应摸高器材。
(二)设计并搭建激光二极管与光敏传感器的检测
1.设计3D打印图,支撑激光二极管与光敏传感器一一对应的布局。测量激光二极管与光敏传感器的尺寸,设计间隔2CM一组激光二极管与光敏传感器,形成激光光栅。制作过程中,发现多数激光二极管发射的激光方向不完全一致,有一定的偏差,需要调整激光偏角,才能对射至光敏传感器上。通过调整,制作成8组激光二极管激光射至光敏传感器,从下往上进行排序,依次是1、2、3...8。
2.测试激光二极管与光敏传感器数据。激光光栅制作完成后,通过编程测试光敏传感器数值变化。
8组激光二极管正极接入ESP32 S3 开发板5V电源端口,负极接负极端口。8组光敏传感器分别接入ESP32 S3 开发板 IO6、IO7、IO8、IO9、IO10、IO11、IO12、IO13号管脚,完成电路连接。接下来进行测试光敏传感器数值变化。
最初方案:刚开始考虑使用光敏传感器数字信号值,当摸高时,哪一组光敏传感器数字信号值变化,就推算摸高的位置。调整光敏传感器的灵敏程度,把激光射到光敏传感器上的数字值存入数组中,当摸高挡住激光时,相应的光敏传感器数字信号值发生变化,从而推算摸高高度。经测试,这种方案会导致光敏传感器感应激光变化的速度比较慢,不能满足摸高装置的需求。经团队成员反复沟通与查阅资料,考虑使用红外避障传感器、超声波测试,因这些传感发射红外或超声波时有一个角度,会导致测试数据有偏差,不采用。在这个问题上困扰好几周,有队员提出可以使用光敏传感器模拟信号进行处理吗?
改进方案:使用光敏传感器模拟信号值变化推算摸高的位置。把激光射到光敏传感器上,读取光敏传感器模拟信号值存入到数组中,发现每个光敏传感器模拟信号值都不一样,当摸高挡住激光时,如何判断哪一组激光被挡住呢?经测试,用最下方一组光敏传感器模拟信号值发生变化时,判断摸高已发生,然后采集当前8组光敏传感器模拟信号值,与之前激光射到光敏传感器上的模拟信号值存入的数组的值进行对比分析,判断摸高的位置。发现用光敏传感器模拟信号值的反应速度比光敏传感器数字信号值快了很多。
经查阅资料发现,模拟信号无需经过复杂的转换和处理过程,从传感器输出后可以直接被一些具有模拟输入接口的电路或设备接收和处理,在这种情况下能快速地反映光照的实时变化,而数字输出的光敏传感器内部通常要先将传感器感知到的光信号转换为模拟电信号,然后再通过模数转换电路将模拟信号转换为数字信号。这个模数转换过程以及数字信号处理电路中的一些逻辑判断和处理步骤,如设置阈值、进行数字滤波等,都需要一定的时间来完成,从而可能会引入额外的延迟。
3.通过超声波传感器推算真实摸高高度
前面通过光敏传感器模拟值的变化可以判断摸高挡住哪几组激光,但不能推算真实摸高高度。在激光光栅同样高度的位置增加超声波测量到地面的距离,真实摸高距离就是超声波测量距离减去摸高激光光栅的位置。超声波trig接ESP32 S3 开发板IO3号管脚、超声波echo接ESP32 S3 开发板IO46号管脚。
(三)语音识别控制摸高开始与结束
前面描述过,摸高需要先通过超声波传感器测量到地面的距离,就需要有摸高的启动,同时思考摸高结束后需要产生什么样的数据。
通过讨论和设计,摸高的启动时,先进行超声波传感器测量到地面的距离,激光二极管发射激光,把光敏传感器模拟值存入数组。接下来就可以进行摸高训练,把每次摸高的数据存入数组中,方便后面进行处理。训练结束后,通过刚才的数组计算摸高平均高度,统计摸高次数和摸高最高值等数据信息。
摸高的启动与结束最初使用按钮控制,使用按钮不怎么方便。后来使用离线语音识别开发板SU-03T,通过SDK源码修改唤醒词和命令词,通过语音识别的方式发送相应编码至ESP32 S3 开发板的TX、RX端口,实现控制摸高的启动和结束。离线语音识别开发板SU-03T的TX端口接ESP32 S3 开发板RX(IO1端口),离线语音识别开发板SU-03T的RX端口接ESP32 S3 开发板TX(IO2端口)
(四)通过语音合成模块对摸高数据进行播报
摸高过程中,需要语音播报摸高的次数及高度,在摸高结束后平均高度、次数、最高值等数据信息需要语音播报。通过队员讨论,使用语音合成模块实现文字转语音功能进行播报。语音合成模块的TX端口接ESP32 S3 开发板RX(IO4端口),语音合成模块的RX端口接ESP32 S3 开发板TX(IO5端口)
(五)Blynk手机APP端呈现摸高数据,实现历史数据对比
通过 ESP32 S3 物联网功能,实现摸高数据的实时、稳定传输,把摸高结束的数据传至blynk 服务器及手机 APP 端。在手机 APP 端开发用户界面,实现历史数据对比、数据图表展示等功能,方便用户直观了解自身运动情况。
六、测试结果
模拟真实使用场景,进行摸高测试,高度检测方面,超声波传感器与激光二极管和光敏传感器组合检测的高度数据高度吻合,测量精度达到预期目标,误差在2CM范围内。
数据处理与记录功能正常,系统能够准确计算并记录每次摸高的高度、平均高度、最高高度和摸高次数等数据。
物联网数据传输稳定,手机 APP 能够实时接收并展示摸高数据,历史数据对比和图表展示功能运行良好。
语音控制功能准确,用户的语音指令能够及时被识别并执行,文字转语音模块也能清晰播报摸高数据。
七、研究结论
本项目成功开发出基于物联网的智能摸高装置,实现了预期的各项功能。通过多种技术的融合,该装置能够精准检测摸高高度,高效处理与记录数据,并通过物联网实现数据传输与智能控制。实验结果表明,装置性能良好,具有较高的实用价值。然而,仍存在一些需要改进的地方,如进一步提高检测精度、增加人脸识别方式记录多用户摸高数据等。未来研究可针对这些问题进行深入探索,推动智能摸高装置的不断完善与发展。
6.附件清单2:视频.mp4 点击下载
程序代码#include <SoftwareSerial.h>
#define BLYNK_PRINT Serial
#include <QHHCV2.h>
SoftwareSerial mySerial4(5,4);
QHHCV2 MyVoice;
#include <HardwareSerial.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
int znyy[]={0, 0, 0, 0};
volatile int appanniu;
volatile float mogaozuigao;
volatile float pingjun;
volatile float dangqiangaodu;
volatile float mogaogaodu;
volatile float mogaocishu;
volatile int Bfl;
volatile int Bcl;
volatile int cv;
volatile int Hlen;
float HH[9999]={};
volatile float Da;
volatile float d;
volatile float s;
volatile float L;
int listl[8]={};
int list[8]={};
float listh[8]={};
volatile int btt;
volatile int n;
volatile int m;
volatile float h;
volatile int Bc;
volatile int Bf;
volatile int ksmgbb;
volatile int cwl;
volatile int cw;
volatile int QiangZhiJieShu;
char auth[] = "329h45DVlH5rmt4QVrj43tA3KGLggWHQ";
char ssid[] = "VCNL";
char pass[] = "wkz123456";
int anniu;
BlynkTimer timer;
float checkdistance_3_46() {
digitalWrite(3, LOW);
delayMicroseconds(2);
digitalWrite(3, HIGH);
delayMicroseconds(10);
digitalWrite(3, LOW);
float distance = pulseIn(46, HIGH) / 58.00;
delay(10);
return distance;
}
BLYNK_WRITE(V0) {
anniu = param.asInt();
appanniu = anniu;
}
void myTimerEvent1() {
Blynk.virtualWrite(V1, dangqiangaodu);
Blynk.virtualWrite(V2, mogaocishu);
Blynk.virtualWrite(V3, mogaozuigao);
Blynk.virtualWrite(V4, pingjun);
Blynk.virtualWrite(V5, Bc);
Blynk.virtualWrite(V6, Bf);
Blynk.virtualWrite(V7, mogaogaodu);
}
void setup(){
mySerial4.begin(9600);
MyVoice.begin(mySerial4);
Serial1.begin(9600,SERIAL_8N1,2,1);
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"请调整高度");
appanniu = 0;
mogaozuigao = 0;
pingjun = 0;
dangqiangaodu = 0;
mogaogaodu = 0;
mogaocishu = 0;
Bfl = 0;
Bcl = 0;
cv = 150;
Hlen = 9999;
Da = 0;
d = 0;
s = 15;
L = 2;
btt = 0;
n = 6;
m = 13;
h = 0;
Bc = 0;
Bf = 0;
ksmgbb = 0;
cwl = 0;
cw = 0;
QiangZhiJieShu = 0;
Serial.begin(9600);
pinMode(3, OUTPUT);
pinMode(46, INPUT);
Blynk.begin(auth, ssid, pass,IPAddress(121,43,62,50),8080);
timer.setInterval(500L, myTimerEvent1);
}
void loop(){
//app传入
//上传服务器或app
//Bfl,Bcl开关上一步
//cv光亮差
//HH[]摸高记录
//Da所有时刻高中的最值高
//d时刻高
//s超声波到最低光敏的高
//L光敏间距
//listl[],list[],listh[]光敏默认亮度,当前亮度,所在高度
//btt单次摸高结束判断
//h存储超声波测量的距离
//Bc,Bf开关
while (Serial1.available()) {
for (int i = 0; i <= 3; i = i + (1)) {
znyy[i] = XXXXXXXXXXXXXXXXXXX();
}
}
Serial.println(String(znyy[0]) + String("-") + String(znyy[1]) + String("-") + String(znyy[2]) + String("-") + String(znyy[3]));
if (znyy[2] == 1) {
Bc = 1;
} else if (znyy[2] == 2) {
Bc = 0;
} else if (znyy[2] == 3) {
Bf = 1;
}
if (Bc == 1) {
if (Bcl == 0) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"我已经准备好了");
}
h = checkdistance_3_46();
Serial.println(h);
for (int i = (0); i <= (m - n); i = i + (1)) {
listh[i] = i * L + (h - s);
}
String t = "";
for (int i = (0); i <= (sizeof(listh)/sizeof(listh[0]) - 1); i = i + (1)) {
t = String(t) + String(",") + String(listh[i]);
}
Serial.println(t);
t = "";
Serial.println("--------------------------------");
for (int i = (n); i <= (m); i = i + (1)) {
listl[(i - n)] = analogRead(i);
}
for (int i = (0); i <= (sizeof(listl)/sizeof(listl[0]) - 1); i = i + (1)) {
t = String(t) + String(",") + String(listl[i]);
}
Serial.println(t);
t = "";
ksmgbb = 1;
}
if (h != 0) {
if (h < 100) {
cw = 1;
Bc = 1;
if (cwl == 0) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"出错了,离的太进了,请暂时远离,正在重新准备");
}
} else if (h > 400) {
cw = 1;
Bc = 1;
if (cwl == 0) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"出错了,超声波超出范围,正在重新准备");
}
} else if (h >= 100 && h <= 400) {
cw = 0;
}
}
cwl = cw;
if (Bc 0 && (btt 0 && h != 0)) {
if (ksmgbb == 1) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+String("当前高度") + String(h) + String("厘米,请开始摸高吧"));
dangqiangaodu = h;
}
ksmgbb = 0;
for (int i = (n); i <= (m); i = i + (1)) {
list[(i - n)] = analogRead(i);
}
String t = "";
for (int i = (0); i <= (sizeof(list)/sizeof(list[0]) - 1); i = i + (1)) {
t = String(t) + String(",") + String(list[i]);
}
Serial.println(t);
t = "";
for (int i = (0); i <= (sizeof(list)/sizeof(list[0]) - 1); i = i + (1)) {
if (list[i] - listl[i] > cv) {
list[i] = 1;
} else {
list[i] = 0;
}
}
for (int i = (0); i <= (sizeof(list)/sizeof(list[0]) - 1); i = i + (1)) {
t = String(t) + String(",") + String(list[i]);
}
Serial.println(t);
t = "";
for (int i = (0); i <= (sizeof(list)/sizeof(list[0]) - 1); i = i + (1)) {
if (list[i] == 1 && listh[i] > d) {
d = listh[i];
}
}
if (d > Da) {
Da = d;
}
if (d == 0 && Da != 0) {
btt = 1;
Serial.println("摸高结束");
}
Serial.println(String("m高,") + String(d));
d = 0;
Serial.println(String("最高") + String(Da));
Serial.println(String("len=") + String(sizeof(HH)/sizeof(HH[0])));
}
if (Bc 0 && btt 1) {
Serial.println("后勤");
int len = 1;
for (int i = (0); i <= (sizeof(HH)/sizeof(HH[0]) - 1); i = i + (1)) {
if (HH[i] != 0) {
len = i + 2;
}
}
HH[(len - 1)] = Da;
Serial.println(String("第") + String(len) + String("摸高") + String(Da) + String("厘米"));
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+String("第") + String(len) + String("摸高") + String(Da) + String("厘米"));
mogaocishu = len;
mogaogaodu = Da;
Da = 0;
btt = 0;
Serial.println(String("len=") + String(len));
if (len == Hlen + 1) {
QiangZhiJieShu = 1;
Bf = 0;
}
}
if (Bf == 1) {
Serial.println("hello");
int len = 1;
for (int i = (0); i <= (sizeof(HH)/sizeof(HH[0]) - 1); i = i + (1)) {
if (HH[i] != 0) {
len = i + 2;
}
if (HH[i] != 0) {
len = i + 1;
}
}
float s = 0;
float zuigao = 0;
for (int i = (0); i <= (len - 1); i = i + (1)) {
s = s + HH[i];
if (HH[i] > zuigao) {
zuigao = HH[i];
}
}
s = s / len;
Serial.println(String("共") + String(len) + String("次摸高") + String("最高") + String(zuigao) + String("厘米") + String("平均") + String(s) + String("厘米"));
mogaozuigao = zuigao;
pingjun = s;
int hz = HH[0];
if (hz != 0) {
if (QiangZhiJieShu == 0) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+String("共") + String(len) + String("次摸高") + String("最高") + String(zuigao) + String("厘米") + String("平均") + String(s) + String("厘米"));
} else {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+String("摸高记录已满,正在结束摸高,摸高已结束共") + String(len) + String("次摸高") + String("最高") + String(zuigao) + String("厘米") + String("平均") + String(s) + String("厘米"));
QiangZhiJieShu = 0;
}
} else {
if (h == 0) {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"还没有开始准备呢,先开始准备吧");
} else {
MyVoice.HC(CODING_UTF8,String("[v10][s5][te50] ")+"还没有开始摸高呢,先开始摸高吧");
}
}
Bf = 0;
znyy[2] = 0;
for (int i = (0); i <= (len - 1); i = i + (1)) {
HH[i] = 0;
}
}
Serial.println(String("HH[0]=") + String(HH[0]));
Bcl = Bc;
Serial.println(String("app按钮") + String(appanniu));
}
源码