【Follow me第二季第1期】创意任务二:害怕声音的纸章鱼~
[复制链接]
这个任务就是要用开发板识别环境中的声音大小,然后控制舵机作出相应的动作。至于章鱼哥其实用3D打印实现是一个不错的选择,网上有很多这样的模型,直接切片软件里找个立方体布尔一下,挖一个能放下舵机的位置就可以了,然后用绳子之类的很好改装,都不需要自己建模。奈何暑期我不在实验室用不了,所以就自己用传单的纸折了一个🤦。
第一步,纸章鱼哥的制作以及控制结构制作
基本就是折个章鱼(这个是Splatoon3里的章鱼角色~),头部贴到快递纸板上,背面用回形针+风筝线拉到两个触角上,给纸板打个洞挂到舵机上
第二步,识别环境中的音量
这里先制作了一个音量指示器,开发板会根据环境音量大小进行亮灯,#include <Adafruit_CircuitPlayground.h>中已经封装了相应功能,调用CircuitPlayground.mic.soundPressureLevel(10)即可获取声音大小,音量计的颜色还是参考上一个创意任务一里面的。顺便加了一个滑动平均滤波器,让音量的识别平滑一点。
效果如下:
代码:
#include <Adafruit_CircuitPlayground.h>
// 定义渐变颜色
uint32_t gradientColors[10] = {
CircuitPlayground.colorWheel(90),
CircuitPlayground.colorWheel(80),
CircuitPlayground.colorWheel(70),
CircuitPlayground.colorWheel(60),
CircuitPlayground.colorWheel(50),
CircuitPlayground.colorWheel(40),
CircuitPlayground.colorWheel(30),
CircuitPlayground.colorWheel(20),
CircuitPlayground.colorWheel(10),
CircuitPlayground.colorWheel(0)
};
// 滤波系数
const float alpha = 0.3;
float filteredSoundLevel = 0;
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
}
void loop() {
float soundLevel = CircuitPlayground.mic.soundPressureLevel(10);
// 移动平均滤波
filteredSoundLevel = alpha * soundLevel + (1 - alpha) * filteredSoundLevel;
Serial.print("Raw Sound Level: ");
Serial.print(soundLevel);
Serial.print(", Filtered Sound Level: ");
Serial.println(filteredSoundLevel);
updateLEDs(filteredSoundLevel);
delay(200); // 更新频率
}
void updateLEDs(float soundLevel) {
// 计算亮灯的数量
int ledCountToLight = map(soundLevel, 55, 70, 0, 10);
for (int i = 0; i < 10; i++) {
if (i < ledCountToLight) {
CircuitPlayground.setPixelColor(i, gradientColors[i]);
} else {
CircuitPlayground.setPixelColor(i, 0);
}
}
}
void turnOffAllLEDs() {
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 0); // 关闭每个 LED
}
}
第三步,加入舵机控制
官方推荐的那个舵机是个连续舵机,没法控制具体的角度,只能让它以某个速度向什么方向转动多少时间来控制,我们使用#include <Servo.h>这个库来实现,用Servo.write来设置转速,当设置为90时舵机停止,具体来说连续旋转舵机在接收到中立脉宽时停止旋转,在接收到不同偏离中立脉宽的信号时分别以不同速度和方向旋转,两个方向离90越远转的越快。这个速度和时间,每个舵机都不太一样需要自己试一试。板子上的按钮也做了设置,可以控制舵机左转右转。
在代码实现中还遇到一个问题,#include <Servo.h>和彩色led的库有冲突,如果同时使用舵机和彩色led灯话,舵机会时不时朝一个方向抽搐,所以就简单粗暴的把亮灯部分灯功能删掉了,但判断逻辑还是沿用上面的代码,9颗灯珠都亮起时,转动舵机180度(触角收缩),等几秒再转回去。
以下是效果,视频中就不放声音了,可以对这板子的mic吹一口气基本声音就够大了:
纸章鱼
#include <Adafruit_CircuitPlayground.h>
#include <Servo.h>
// 定义渐变颜色
uint32_t gradientColors[10] = {
CircuitPlayground.colorWheel(90),
CircuitPlayground.colorWheel(80),
CircuitPlayground.colorWheel(70),
CircuitPlayground.colorWheel(60),
CircuitPlayground.colorWheel(50),
CircuitPlayground.colorWheel(40),
CircuitPlayground.colorWheel(30),
CircuitPlayground.colorWheel(20),
CircuitPlayground.colorWheel(10),
CircuitPlayground.colorWheel(0)
};
// 滤波系数
const float alpha = 0.3;
float filteredSoundLevel = 0;
// 舵机定义
const int servoPin = A3; // 舵机连接的引脚
Servo myServo;
const int neutralSpeed = 90; // 中立位置速度
const int leftSpeed = 84; // 左转速度
const int rightSpeed = 94; // 右转速度
const int turnDuration = 700; // 转动持续时间
const int delayAfterTurn = 5000; // 等待时间
void setup() {
CircuitPlayground.begin();
Serial.begin(115200); // 用于调试
myServo.attach(servoPin); // 附加舵机到指定引脚
myServo.write(neutralSpeed); // 设置舵机初始速度为停止
}
void loop() {
checkButtons();
float soundLevel = CircuitPlayground.mic.soundPressureLevel(10);
// 滤波
filteredSoundLevel = alpha * soundLevel + (1 - alpha) * filteredSoundLevel;
Serial.print("Raw Sound Level: ");
Serial.print(soundLevel);
Serial.print(", Filtered Sound Level: ");
Serial.println(filteredSoundLevel);
updateLEDs(filteredSoundLevel);
myServo.write(neutralSpeed);
delay(200);
}
void updateLEDs(float soundLevel) {
// 计算亮灯的数量
int ledCountToLight = map(soundLevel, 55, 70, 0, 10);
Serial.print("LED Count: ");
Serial.println(ledCountToLight);
if (ledCountToLight >= 9) {
turnLeft180();
delay(delayAfterTurn);
turnRight180();
}
}
void turnLeft180() {
myServo.write(leftSpeed); // 设置舵机左转速度
delay(1900); // 转动持续时间
myServo.write(neutralSpeed); // 停止舵机
}
void turnRight180() {
myServo.write(rightSpeed); // 设置舵机右转速度
delay(turnDuration); // 转动持续时间
myServo.write(neutralSpeed); // 停止舵机
}
void checkButtons() {//按钮控制
if (CircuitPlayground.leftButton()) {
turnLeft180();
delay(500);
}
if (CircuitPlayground.rightButton()) {
turnRight180();
delay(500);
}
}
|