本帖最后由 zoey不种土豆 于 2024-11-20 17:15 编辑
想象一下,仅需在空中挥挥手,就可以轻松控制灯的开关。这不仅为日常生活增添了便利,还为智能家居技术带来了更高的互动性和趣味性。手势识别的技术有很多种,在这个项目中,我们将利用两个光敏电阻,通过检测手在不同位置的光遮挡情况,实现手势控制。
元件清单
硬件连接
按如下连线图连接。
示例代码
样例代码:
- int ledPin = 13; // 定义LED引脚为13
- int lightSensor1 = 0; // 定义左侧光传感器引脚为A0
- int lightSensor2 = 1; // 定义右侧光传感器引脚为A1
-
- int cali1 = 0; // 左侧光传感器的校准变量
- int cali2 = 0; // 右侧光传感器的校准变量
-
- bool isOn = false; // LED状态控制变量
-
- void setup() {
- Serial.begin(9600); // 初始化串行通信
-
- pinMode(ledPin, OUTPUT); // 将LED引脚设置为输出
-
- // 校准光传感器:读取10次数据并平均作为环境光基线
- for (int i = 0; i < 10; i++) {
- cali1 += analogRead(lightSensor1);
- cali2 += analogRead(lightSensor2);
- delay(500);
- }
- cali1 = (cali1 / 10);
- cali2 = (cali2 / 10);
-
- Serial.print("环境光: ");
- Serial.print(cali1);
- Serial.print(" , ");
- Serial.println(cali2);
-
- // 通过LED闪烁两次来指示程序的开始
- for (int i = 0; i < 2; i++) {
- digitalWrite(ledPin, HIGH);
- delay(1000);
- digitalWrite(ledPin, LOW);
- delay(1000);
- }
-
- Serial.println("开始");
- }
- void loop() {
- int sensorVal1 = analogRead(lightSensor1);
- // 读取左侧光传感器的值
- int sensorVal2 = analogRead(lightSensor2);
- // 读取右侧光传感器的值
- int threshold = 30; // 设置手势检测阈值
- Serial.print("L: ");
- Serial.print(sensorVal1);
- Serial.print(" R: ");
- Serial.println(sensorVal2);
- // 根据传感器值确定手势方向并控制LED
- if ((sensorVal1 > (cali1 + threshold)) && !(sensorVal2 > (cali2 + threshold))) {
- isOn = true;
- }
- if (!(sensorVal1 > (cali1 + threshold)) && (sensorVal2 > (cali2 + threshold))) {
- isOn = false;
- }
- if (isOn) {
- digitalWrite(ledPin, HIGH); // 打开LED
- Serial.println("打开");
- } else {
- digitalWrite(ledPin, LOW); // 关闭LED
- Serial.println("关闭");
- }
- delay(100); // 延迟100毫秒以避免频繁读取
- }
复制代码
请将装置放置在光线稳定且无阴影遮挡的环境中。上传程序后,将自动进行5秒钟的校准,检测当前光线值。校准完成后,LED会闪烁两次,指示程序启动。向左挥手可打开灯光,向右挥手可关闭灯光。如果效果不理想,可以参考串口打印的读数,调整阈值设置。
代码回顾
在程序开始时,定义LED和两个光敏电阻的引脚。然后,创建两个名为cali的变量来记录光敏电阻的初始读数。
- int cali1 = 0;
- int cali2 = 0;
复制代码
接下来,我们介绍一种新的变量类型,bool,它代表布尔变量。它只有两个可能的值:true(真)和false(假),通常用于表示条件的真实性或开关的状态。在这里,我们定义一个名为isOn的布尔变量来记录LED当前的开/关状态。 复制代码
光敏电阻的读数会根据其位置和一天中的时间而变化。因此,在程序的设置阶段,我们需要执行自动校准以获得传感器的基线读数。 - for (int i = 0; i < 10; i++) {
- cali1 += analogRead(lightSensor1);
- cali2 += analogRead(lightSensor2);
- delay(500);
- }
复制代码
在For循环中,光敏电阻的模拟值被读取了10次,每次读取之间间隔500毫秒,并将结果累加到变量cali1和cali2中。 具体来说,这个循环的目的是为了对光敏电阻进行校准,以获取在特定环境下(即程序开始时)它们的基础读数。通过多次读取并取平均值,可以减少因单次读数误差或环境光线的短暂变化而导致的误差。 之后,将累加得到的总读数除以10,即可得到两个光敏电阻的校准值(即基线读数),这些值将用于后续的比较和判断,以确定LED的开/关状态。 - cali1 = (cali1 / 10);
- cali2 = (cali2 / 10);
复制代码
循环结束后,计算左侧和右侧光敏电阻的平均值。这些平均值将作为后续光线比较和决策的校准基线值。 一旦完成校准,将平均值打印到串行监视器上,并闪烁LED两次以指示程序的开始。 在loop函数中,我们将比较光线的值并控制LED的开关。 首先,设置两个变量来记录光敏电阻的实时读数。 - int sensorVal1 = analogRead(lightSensor1);
- int sensorVal2 = analogRead(lightSensor2);
复制代码
从之前关于光敏电阻的课程中,我们了解到较高的读数对应于较暗的光线条件。当手势发生时,投射到光敏电阻不同部分的阴影会导致其读数上升。因此,我们可以通过监控两侧光敏电阻读数的变化来区分左右挥手的手势。调整阈值变量可以让我们改进这个检测过程的灵敏度。 复制代码
当左侧光敏电阻的读数超过其基线值加上阈值时,表示左侧有遮挡物。 - if (sensorVal1 > (cali1 + threshold))
复制代码
然而,仅依赖左侧读数是不够的;它需要与右侧光敏电阻的读数相结合来进行准确评估。 当左侧被遮挡而右侧没有时,它表示一个从右向左挥动的手势。 - if ((sensorVal1 > (cali1 + threshold)) && !(sensorVal2 > (cali2 + threshold)))
复制代码
在这行代码中,! 作为逻辑非运算符,用于反转内部表达式的值。&& 作为逻辑与运算符,检查两个表达式是否同时为真。如果条件满足,isOn 被设置为 true,从而打开 LED。否则,isOn 被设置为 false。 复制代码
最后,我们使用 isOn 的当前值来决定是否打开或关闭 LED。在这里,if (isOn) 是 if (isOn == true) 的简写形式。 - if (isOn) {
- digitalWrite(ledPin, HIGH);
- Serial.println("Turn On");
- } else {
- digitalWrite(ledPin, LOW);
- Serial.println("Turn Off");
- }
复制代码
示例代码
下一课:
|