基于CircuitPython与CPB开发板的多感官互动装置设计与实现
1. 项目概述:为特殊需求人群打造一个多感官互动站
在物理计算和辅助技术领域,我们常常思考如何用电子和代码去创造一些有温度、能切实帮助到人的东西。这次分享的项目——“水母感官站”,就是这样一个尝试。它源于我们在大学里的一门物理计算与辅助技术课程,最终落地于波士顿学院校园学校,旨在为有特殊需求的学生们提供一个安全、有趣且富有吸引力的多感官互动环境。
这个项目的核心,是借助一块小巧但功能强大的开发板——Adafruit Circuit Playground Bluefruit(后面简称CPB),去协调灯光、声音和触觉反馈,创造出一个按下按钮就会“活”过来的发光水母。它不仅仅是一个手工制品,更是一个融合了微控制器编程、基础电子焊接、简单木工和纺织工艺的综合性辅助技术装置。其价值在于,通过可控的视觉(LED动画)、听觉(环境音效)和触觉(多种纹理的织物)刺激,为使用者提供一个可以自我调节、探索和享受的平静角落,这在教育辅助和感官整合训练中非常有用。
如果你是一位教育工作者、治疗师、创客,或者只是对如何用技术做些有意义的项目感兴趣,这个案例会很有参考价值。它涉及从需求分析、材料选型、编程到动手制作的全流程。我会详细拆解我们是如何一步步把想法变成实物的,其中踩过的坑、总结的经验,都会毫无保留地分享出来。
2. 项目整体设计与核心思路拆解
2.1 需求分析与设计定位
任何辅助技术项目的起点都必须是用户需求,而不是炫酷的技术。在与校园学校的老师和治疗师多次沟通后,我们明确了几个关键点:
- 安全性至上 :装置必须坚固、稳定,无尖锐边角,所有电子部件需完全封闭,避免误触或吞咽风险。电线连接必须可靠,防止被拉扯脱落。
- 多感官整合 :目标用户可能对特定感官输入有不同需求或偏好。因此,装置应同时提供视觉(灯光)、听觉(声音)和触觉(不同材质)的反馈,且强度可控、可预测。
- 明确的因果交互 :交互逻辑必须极其简单、直观。我们选择了超大尺寸的街机按钮,因为它目标明显、按压力度反馈清晰,能明确建立“按压-响应”的因果关系。
- 美观与沉浸感 :装置本身应该是一个吸引人的“景点”,而不仅仅是功能的堆砌。水母的造型柔和、神秘,其发光和飘动的触须能营造一种平静、梦幻的氛围,有助于情绪安抚。
基于这些,我们确定了“立柱式感官站”的形态:一个稳定的木质底座,支撑起一个透明的亚克力管柱,管柱内是电子核心,顶部则是装饰成水母形态的互动头。用户按压底座上的大按钮,触发管柱内的LED灯带产生星光闪烁动画,并随机播放一段水环境声音。
2.2 核心硬件选型与原理
为什么是Adafruit Circuit Playground Bluefruit?这是本项目成功的关键决策之一。对于这类集成度要求高、功能需求明确的创意电子项目,像CPB这样的“一体化开发板”远比从零开始用Arduino Uno连接各种传感器模块要高效可靠。
Circuit Playground Bluefruit的核心优势 :
- 高度集成 :一块板子上集成了10个可编程RGB NeoPixel LED、运动传感器、温度传感器、光线传感器、声音传感器、蜂鸣器,以及多个触摸感应引脚。这几乎囊括了我们所需的所有基础传感器和反馈器,极大简化了电路设计和焊接工作。
- 内置蓝牙 :虽然本项目未使用,但Bluefruit系列内置的蓝牙低能耗(BLE)为未来升级留下了空间,例如可以通过手机应用远程控制或切换模式。
- CircuitPython支持 :这是另一个决定性因素。CircuitPython是MicroPython的一个分支,针对Adafruit的硬件进行了优化。它让编程变得像在U盘里拖放文件一样简单,无需复杂的IDE设置和编译过程,特别适合快速原型开发和教育场景。
- 充足的GPIO与专用音频引脚 :CPB提供了足够的通用输入输出引脚来连接外部按钮和LED灯带,并且有专用的模拟音频输出引脚,可以直接驱动小功率扬声器或连接功放,处理音频播放任务。
其他关键硬件解析 :
- 外部LED灯带 :CPB板载只有10个LED,且位于板子同一平面,光效不够立体。我们额外选用了20颗/米的WS2812B可寻址LED灯带(4英寸间距),将其盘绕在亚克力管内部,从底部向上照明,模拟深海中的光芒。WS2812B只需要一个数据引脚就能控制所有LED的颜色和亮度,与CPB的NeoPixel库完美兼容。
- 大尺寸街机按钮 :选择它不仅是出于美观和易用性,其内部通常采用微动开关,手感清晰,寿命长。在电路上,我们将其配置为“上拉输入”模式。简单来说,按钮一端接地(GND),另一端接CPB的输入引脚。当按钮未按下时,引脚通过内部上拉电阻连接到高电平(3.3V),读取为“True”或“1”;按下时,引脚直接接地,变为低电平(0V),读取为“False”或“0”。这种配置能有效防止引脚悬空产生误触发。
- 音频输出方案选择与教训 :原始项目中我们使用了一个小型放大器模块,但效果不佳。 这里是个重要的经验点 :CPB的模拟音频输出驱动能力有限,直接推动大尺寸扬声器音量会很小。更优的方案是选择一款“功放一体式”的小喇叭,或者一个带内置D类功放的小型扬声器模块。如果对音质有要求,可以使用I2S接口的数字音频模块,但这需要更复杂的编程和接线。对于本项目,一个3W左右的微型功放喇叭足矣。
2.3 软件与逻辑框架
整个装置的“大脑”是运行在CPB上的CircuitPython程序。逻辑流程非常清晰:
- 初始化 :程序启动后,初始化所有硬件——设置按钮引脚为上拉输入模式,初始化外部LED灯带对象,加载Adafruit LED动画库中的“Sparkle”(闪烁)动画,并配置音频输出。
- 主循环 :程序不断检查按钮引脚的状态。
- 触发判断 :一旦检测到按钮被按下(引脚值变为
False),立即进入响应序列。 - 执行反馈 :
- 灯光 :启动Sparkle动画,让20颗LED随机以紫色闪烁,模拟水母发光和海底星光。
- 声音 :从一个预设的音频文件列表(如海浪声、气泡声、溪流声)中随机选择一个,通过音频接口播放。
- 同步与结束 :在音频播放期间,灯光动画持续更新。播放完毕后,系统回到等待按钮触发的状态。
这种“事件驱动”的模型非常高效,CPU只在事件发生时工作,大部分时间处于低功耗的等待状态,有利于电池续航。
3. 核心制作流程与实操详解
3.1 结构件制作:底座、顶盖与亚克力管
材料与工具准备 :
- 木材 :1英寸厚的实木板或高质量胶合板,尺寸至少15x15英寸两块(用于底座和顶盖)。胶合板更不易开裂。
- 亚克力管 :外径4英寸,高度12英寸的透明圆管。透明材质便于观察内部电子结构和灯光效果。
- 主要工具 :台锯(或请木材店代裁)、曲线锯、电钻/台钻、砂光机(或不同目数砂纸)、卷尺、铅笔。
操作步骤与关键细节 :
-
切割与成型 :
- 用台锯将木板裁切成15英寸x15英寸的正方形作为底座。再裁切一块稍大的木板,用曲线锯或圆规锯切出一个直径15英寸的圆形作为顶盖。圆形边缘用砂光机仔细打磨至光滑,消除所有木刺。
- 安全第一 :操作台锯等电动工具时,务必使用推把,佩戴护目镜和防尘口罩。
-
钻孔与开槽 :
- 核心定位 :这是确保立柱垂直稳固的关键。在底座正方形中心和顶盖圆形的中心,用开孔器或台钻钻一个直径比亚克力管外径小约1-2毫米的圆孔。例如,管外径101.6mm(4英寸),可钻100mm的孔。需要耐心测试,目标是让亚克力管能够紧紧“塞入”或“卡入”,依靠摩擦力初步固定,而不是松松垮垮。
- 走线孔 :在顶盖圆盘上,距离中心一定距离(确保不影响结构强度)再钻一个直径约1英寸(25mm)的孔,用于将所有电子线缆从亚克力管内部引到顶盖之上。
- 亚克力管开孔 :在亚克力管靠近底部的位置,同样开一个1英寸的孔。这个孔是 外部按钮和电池盒电源线的入口 。所有电子设备主体(CPB、灯带、喇叭)都密封在管内,只有按钮和电池盒放在管外便于操作和更换。
-
打磨与处理 :
- 所有木制部件的边角都必须用砂纸打磨成圆角,特别是顶盖的边缘和底座的棱边。
- 可以根据喜好给木头上清漆或环保水性漆,增加美观度和耐用性。务必在通风良好处进行,并等待漆面完全干透固化后再进行组装。
注意 :在这一步, 绝对不要 进行任何永久性粘合(如胶水)或焊接。务必保持所有部件可分离,以便后续的电子部分安装和调试。我们当时就曾差点把管子提前粘死,导致后续穿线困难。
3.2 电子系统搭建与编程
电路连接示意图(文字描述) :
- 电源总线 :将3节AAA电池盒的正极(+)输出线连接到CPB的
VOUT引脚(或3.3V引脚,具体看板子标注),负极(-)连接到CPB的GND引脚。这为整个系统供电。 - 外部LED灯带 :将灯带的
VCC(+5V)接至CPB的VOUT(如果灯带是5V的,CPB的VOUT引脚在USB供电时是5V,电池供电时是电池电压,需确认灯带工作电压范围),GND接CPB的GND,DIN(数据输入)接CPB的A1引脚(或其他任何支持PWM/数字输出的引脚)。 - 大按钮 :按钮的一个端子接CPB的
GND,另一个端子接CPB的A4引脚(配置为上拉输入)。 - 扬声器/功放 :如果使用无源扬声器+功放模块,将功放的音频输入接CPB的
A0(模拟音频输出),功放的电源接CPB的VOUT和GND。如果使用一体式有源喇叭,通常直接连接A0和GND即可。
编程实战(CircuitPython) : 首先,你需要将最新的CircuitPython固件刷写到CPB上(从Adafruit官网下载 .uf2 文件,按住板子上的复位键,通过USB连接电脑,出现 CPLAYBTBOOT 磁盘后,将固件文件拖入即可)。
然后,在你的代码.py文件所在目录,需要安装必要的库文件(通过 circup 工具或手动从Adafruit GitHub库下载):
adafruit_led_animation- 对应的
neopixel.mpy
以下是增强和注释后的核心代码:
# jellyfish_sensory_stand.py
import time
import board
import digitalio
import neopixel
import random
from audiocore import WaveFile
from adafruit_led_animation.animation.sparkle import Sparkle
from adafruit_led_animation.color import PURPLE # 使用预定义颜色
# 1. 初始化大按钮 (配置为上拉输入)
big_button = digitalio.DigitalInOut(board.A4) # 使用A4引脚,根据实际接线修改
big_button.direction = digitalio.Direction.INPUT
big_button.pull = digitalio.Pull.UP # 启用内部上拉电阻
# 2. 初始化外部NeoPixel灯带
NUM_PIXELS = 20 # 灯带上LED的数量
PIXEL_PIN = board.A1 # 数据线连接的引脚
pixels = neopixel.NeoPixel(PIXEL_PIN, NUM_PIXELS, brightness=0.5, auto_write=False)
# brightness建议从0.3开始测试,太亮可能刺眼。auto_write=False允许我们批量更新LED状态,效率更高。
# 3. 创建闪烁动画实例
sparkle_animation = Sparkle(pixels, speed=0.05, color=PURPLE, num_sparkles=5)
# speed: 闪烁速度(值越小越快), num_sparkles: 同时闪烁的LED数量。可调整以获得最佳视觉效果。
# 4. 音频输出初始化(兼容不同板卡)
try:
from audioio import AudioOut
except ImportError:
try:
from audiopwmio import PWMAudioOut as AudioOut
except ImportError:
print("此开发板不支持AudioOut")
pass
# 启用板载音频放大器(如果板子有的话)
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.direction = digitalio.Direction.OUTPUT
speaker_enable.value = True
audio = AudioOut(board.SPEAKER) # 使用板载扬声器引脚
SOUNDS_DIR = "/sounds/" # 声音文件存放在CPB存储盘的/sounds/目录下
def play_random_sound():
"""播放随机选择的一个水声文件"""
sound_files = ["waves.wav", "bubbles.wav", "stream.wav"]
chosen_sound = random.choice(sound_files)
file_path = SOUNDS_DIR + chosen_sound
try:
with open(file_path, "rb") as wave_file:
wave = WaveFile(wave_file)
audio.play(wave)
print(f"正在播放: {chosen_sound}")
# 在播放声音的同时,更新灯光动画
while audio.playing:
sparkle_animation.animate() # 更新动画帧
time.sleep(0.01) # 短暂延时,避免循环过紧
except OSError as e:
print(f"无法播放声音文件 {file_path}: {e}")
# 5. 主循环
print("水母感官站已启动,等待按钮按下...")
last_button_state = True # 记录上一次按钮状态,用于消抖
while True:
current_button_state = big_button.value # 读取当前按钮状态
# 消抖处理:检测到从“未按下”(True)到“按下”(False)的下降沿
if last_button_state and not current_button_state:
print("按钮被按下!")
play_random_sound()
last_button_state = current_button_state # 更新状态
time.sleep(0.05) # 主循环延时,降低CPU占用并进一步消抖
音频文件准备要点 :
- 来源 :从
bigsoundbank.com、freesound.org等网站寻找免版税的水声、自然声WAV文件。 - 处理 :使用Audacity(免费开源软件)进行处理:
- 导入文件。
- 裁剪长度 :将声音裁剪到5-10秒,过长的文件会占用大量存储空间并导致播放延迟。
- 转换格式 :导出时选择“WAV (Microsoft) signed 16-bit PCM”格式,这是CircuitPython
audiocore模块广泛支持的格式。 - 降低采样率 :将采样率降至22050 Hz或甚至16000 Hz,可以显著减小文件体积,且对于环境音效质量损失不大。
- 单声道 :确保导出为单声道(Mono),而非立体声。
- 存放 :将处理好的
.wav文件直接拷贝到CPB存储盘上新建的sounds文件夹内。
3.3 焊接、组装与总装
焊接工序的惨痛教训 : 这是整个项目中最容易出错的一环。我们的黄金法则是: 先穿线,后焊接;先测试,后固定 。
- 规划走线 :在焊接任何一根线之前,用记号笔或胶带标出所有线缆从电路板到最终目的地的路径。特别是要穿过亚克力管上那个1英寸孔的电线(按钮线和电池盒线)。
- 预穿线 : 务必 先将这些需要过孔的线缆穿过亚克力管上的孔,并留出足够长的余量,然后再焊接它们到CPB或灯带上。我们当时就是先焊好了按钮,结果发现插头太大穿不过孔,不得不剪断重焊。
- 焊接技巧 :
- 使用焊台,温度设置在350°C左右。
- 焊接前先给焊盘和线头“上锡”。
- 焊接时使用助焊剂,让焊点圆润光亮。
- 为每个焊点套上热缩管,用热风枪或打火机(小心)加热收缩,实现绝缘和保护。最后再用电工胶布整体包裹线束,既美观又安全。
- 系统测试 :将所有部件(电池、CPB、灯带、按钮、喇叭)连接好,但先不要固定在任何结构上。上电,测试按钮功能、灯光效果、声音播放是否全部正常。这是修复问题的最后黄金时间。
总装步骤 :
- 电子部分入管 :将CPB主板、盘绕好的LED灯带(发光面朝向管壁)、小喇叭用尼龙扎带或双面泡沫胶固定在亚克力管内部。确保线缆整齐,避免杂乱。
- 密封管底 :用一块切割好的圆形亚克力板或厚实的不织布,配合硅胶或热熔胶,将亚克力管底部密封,防止灰尘进入。
- 安装顶盖 :将顶盖木圆盘套在亚克力管顶端。此时,从管侧孔引出的按钮线和电池盒线,以及从顶盖中心孔引出的灯带/喇叭线,都在顶盖上方了。
- 连接与固定外部部件 :将大按钮安装在底座木板预先开好的孔位上(孔位需匹配按钮螺母),电池盒也用胶或魔术贴固定在底座侧面。将对应的线缆连接好。
- 最终结构整合 :将亚克力管插入底座中心的孔中,调整垂直度后,可以从底座下方打入少量木工胶或使用法兰盘固定。将顶盖与亚克力管顶端粘合。 再次强调,确保所有电子功能测试无误后再进行永久粘合!
3.4 水母触须与外观制作
这是赋予项目灵魂的艺术环节。
- 制作伞盖 :
- 用黑色不织布或帆布,裁剪出7-8个长条三角形(底边约15cm,高约30-40cm)。
- 用缝纫机将这些三角形的长边缝合在一起,形成一个圆锥体,这就是水母的“伞盖”。
- 在伞盖的内侧或边缘,缝上或粘上各种触觉材料:毛茸茸的绒球缎带、光滑的亮片缎带、沙沙作响的网纱布料。创造丰富的触觉对比。
- 集成灯光 :
- 在顶盖木圆盘上,围绕中心走线孔,将LED灯带(从亚克力管中伸出的部分)用胶水或小钉子固定成一圈,确保LED朝向伞盖内部。
- 将水母伞盖套在顶盖圆盘上,让灯光能从布料中透出。用填充棉适量塞入伞盖顶部,使其看起来饱满。
- 用热熔胶或手工针线将伞盖的底部固定在顶盖圆盘边缘。
- 制作触须 :
- 剪出长度不一的黑色、半透明纱带或丝带。
- 将这些触须粘在伞盖内侧边缘,垂落下来。
- 可以在部分触须上粘上小铃铛或更小的亮片,增加动态感和听觉细节。
- 最终装饰 :用剩余的亮片布、缎带装饰亚克力管柱和底座,遮盖可能不美观的接缝和线缆。
4. 调试、优化与问题排查实录
即使规划得再周密,实际制作中总会遇到问题。以下是我们在项目中和后续反思中总结的“避坑指南”。
4.1 常见问题与解决方案速查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 上电后无任何反应 | 1. 电池没电或装反。 2. 电源线虚焊或断开。 3. CPB未正确启动。 |
1. 用万用表检查电池电压,确保正负极正确。 2. 检查CPB板载红色电源LED是否亮起。不亮则检查电源焊接点。 3. 重新按一下CPB上的复位键。 |
| 按下按钮无反应 | 1. 按钮接线错误(特别是上拉模式)。 2. 代码中引脚定义错误。 3. 按钮内部接触不良。 |
1. 用万用表通断档,测按钮按下时两引脚是否导通。 2. 在代码中添加 print(big_button.value) ,观察串口输出,按下时应从 True 变 False 。 3. 检查代码中 if not big_button.value: 逻辑是否正确。 |
| LED灯带不亮或部分不亮 | 1. 电源功率不足(特别是LED全白时)。 2. 数据线(DIN)接触不良或接反。 3. 灯带中某个LED损坏导致信号中断。 |
1. 尝试降低代码中的 brightness 值(如从0.8降至0.3)。 2. 检查数据线焊接点。用单独程序测试灯带(如让所有灯亮红色)。 3. 如果部分不亮,检查该LED前后焊接点,或绕过该LED焊接数据线。 |
| 有灯光但无声音 | 1. 扬声器或功放未接电或损坏。 2. 音频文件格式不正确。 3. 音量设置过低或硬件静音。 |
1. 检查喇叭/功放与CPB的连线(电源、地、信号)。 2. 确认 .wav 文件是单声道、16位PCM格式,且已放入正确的文件夹。 3. 尝试用一段简单的代码测试蜂鸣器(如果CPB有)看音频系统是否正常。 |
| 声音播放卡顿或灯光动画掉帧 | 1. 音频文件过大或采样率过高。 2. 主循环中有阻塞操作。 3. 电源电压下降。 |
1. 用Audacity重新处理音频文件,缩短时长、降低采样率。 2. 确保在 audio.playing 循环中调用了 sparkle.animate() ,并留有短暂 time.sleep 。 3. 更换新电池,或考虑使用容量更大的锂电池组。 |
| 按钮响应不灵或连发 | 1. 机械抖动。 2. 主循环扫描速度过快。 |
1. 在代码中实现“消抖”,如检测下降沿并延时。上文代码已包含此逻辑。 2. 在主循环中增加 time.sleep(0.05) 。 |
4.2 项目优化与扩展思路
完成基础版本后,可以考虑以下升级,让装置更具交互性和适应性:
-
多模式切换 :增加一个旋钮开关或第二个按钮,用于切换不同的“场景模式”。例如:
- 模式A :紫色闪烁+水声(基础模式)。
- 模式B :缓慢呼吸的蓝绿色光+轻柔的海浪白噪音。
- 模式C :随着环境声音(如拍手)大小而改变亮度或颜色的互动模式(利用CPB的麦克风)。
- 实现方法:在代码中增加一个
mode变量,通过另一个输入设备改变其值,在主循环中根据mode执行不同的灯光和声音例程。
-
增加传感器互动 :利用CPB板载的丰富传感器。
- 光线传感器 :根据环境光自动调节LED亮度,在暗环境中降低亮度避免刺眼。
- 加速度计 :轻轻拍打或摇晃底座,可以触发不同的灯光效果,提供另一种交互方式。
- 电容触摸 :将一些金属亮片或锡纸连接到CPB的触摸引脚上,当用户触摸这些“触须”时,可以触发特定的反馈。
-
提升音频体验 :
- 使用更高质量的I2S音频DAC模块和扬声器。
- 实现音频的淡入淡出效果,避免声音突然开始和结束。
- 播放更长的、循环的环境音轨,而按钮按下时触发一个短促的“点缀音效”。
-
结构与美学优化 :
- 使用激光切割亚克力板来制作更精致、有图案的底座和顶盖。
- 在亚克力管内部加入一些漫射材料(如磨砂贴膜或乳白色亚克力管),让LED光线更柔和均匀。
- 为整个装置设计一个可拆卸的、易于清洗的外罩。
这个“水母感官站”项目从构思到落地的全过程,深刻地体现了辅助技术项目的核心——技术是手段,关怀与适用才是目的。它不需要多么高深的技术,但需要周全的考虑、耐心的调试和用心的制作。最大的收获不是做出了一个会发光发声的玩具,而是在测试时看到一位学生被绚烂的光影和舒缓的水声吸引,专注地探索不同纹理的触须,那一刻,所有焊接时烫到的手指、调试代码时的抓狂都值了。如果你也想尝试,不妨就从一块Circuit Playground开始,从一个明确的小需求出发,最重要的是,保持动手和分享的热情。
更多推荐

所有评论(0)