基于Arduino的社交尴尬沉默检测与互动增强系统SASSIE项目全解析
2026/6/4 13:20:59 网站建设 项目流程

1. 项目概述与核心思路

社交场合中突如其来的沉默,确实会让人手心冒汗、大脑空白。作为一个长期鼓捣嵌入式系统的开发者,我一直在思考,能否用技术手段给这种微妙的社交时刻一点“润滑”?这就是SASSIE项目的起点——一个基于Arduino的社交尴尬沉默检测与互动增强系统。它的核心任务很简单:当对话陷入令人不适的长时间沉默时,系统能自动感知,并通过一种有趣、非侵入性的方式(比如播放一段音乐、转动一个指示牌)来打破僵局,随机“指定”下一个发言者,从而缓解参与者的压力。

这个项目的价值远不止于一个有趣的玩具。它本质上是一个完整的嵌入式系统集成案例,涵盖了传感器数据采集(双麦克风)、实时信号处理(Arduino逻辑判断)、无线通信(XBee模块)以及多执行器协同控制(步进电机、伺服电机、音频模块)。对于想深入学习物联网和智能交互设备开发的朋友来说,SASSIE提供了一个从传感器到执行器、从硬件到软件、从逻辑到机械的绝佳练手项目。它把看似复杂的系统拆解成了可理解、可实现的模块,你不仅能学会让Arduino“听见”沉默,还能让它“动手”去打破沉默。

2. 系统架构与核心模块解析

SASSIE系统在物理上由两个主要单元构成:一个主控与交互单元,以及一个独立的麦克风阵列单元。两者通过无线模块通信,这样的分离式设计让麦克风可以灵活布置在对话区域,而主控单元和它的机械装置可以放在桌子中央作为视觉焦点。

2.1 硬件系统组成与选型考量

整个系统的硬件清单看起来不少,但每一件都有其明确的任务。选型时,我主要基于易用性、可靠性和成本考虑。

1. 核心控制器:Arduino Uno R3为什么用两块Uno?这是本项目的一个关键设计点。一块Uno(主控板)要同时处理双路麦克风信号、控制伺服电机、播放SD卡音频,并通过串口与无线模块通信,其引脚和算力已经接近饱和。如果再让它直接驱动耗电较大且需要精确脉冲控制的步进电机,很可能导致程序运行不稳定或电源波动。因此,将步进电机的驱动任务剥离到第二块Uno(电机驱动板)上,是典型的功能解耦设计。这样做的好处是逻辑清晰,每块板子各司其职,也方便单独调试。Uno的丰富生态和稳定性,使其成为此类原型项目的首选。

2. 感知核心:模拟驻极体麦克风模块这里用了两个麦克风模块,并非为了立体声,而是为了提高检测的可靠性。单个麦克风可能因为方向性、瞬时干扰(如咳嗽、碰杯)而产生误判。使用两个麦克风,并在程序逻辑中设置为“只有当两个麦克风同时检测到无声时,才累计沉默时间”,可以极大降低误触发概率。我们选用的是输出模拟信号的麦克风模块,它输出的是实时变化的电压值,通过Arduino的模拟引脚读取后,可以设定一个阈值来判断“有声”或“无声”。这个阈值需要在实际环境中调试确定。

3. 执行机构:步进电机与伺服电机

  • 28BYJ-48步进电机与ULN2003驱动板:这是最常用的5V减速步进电机套件。选择它的原因是扭矩足够带动一个轻质的指示牌旋转,且价格低廉。ULN2003驱动板简化了连接,只需4个数字引脚即可控制。它的作用是带动主装置旋转,随机指向一位参与者,增加互动的趣味性和随机性。
  • SG90微型伺服电机:伺服电机可以精确控制角度。这里用它来升降一个写着“该你说了!”之类的小旗子或标语牌。当沉默被检测到时,伺服电机转动90度将牌子立起,形成一个明确的视觉提示。SG90重量轻、功耗低,非常适合这种小负载场景。

4. 交互与通信模块

  • XBee Wireless SD Shield:这是一个集成了SD卡槽和XBee无线模块插槽的扩展板。SD卡用于存储WAV格式的提示音或背景音乐(通过TMRpcm库播放),而XBee模块则负责主控板与电机驱动板之间的无线通信。选择XBee是因为其配置相对简单,通信稳定,适合点对点的可靠数据传输。当然,你也可以用更便宜的nRF24L01+模块替代,但软件配置会稍复杂。
  • 0.5W 8欧姆小喇叭:用于播放提示音。直接连接到Arduino的引脚(通过一个三极管简单放大驱动会更安全,但TMRpcm库支持直接驱动小功率喇叭)。

5. 结构部分:激光切割椴木板所有外壳和结构件均设计为激光切割的椴木板。椴木板易于切割、重量轻、强度足够,且外观有质感。设计文件(通常为DXF或SVG格式)需要考虑到板材厚度(例如3mm)、零件之间的插接结构(如榫卯)、以及为电线预留的走线孔。

2.2 软件逻辑与工作流程

系统的“大脑”是运行在主控Arduino上的程序。其核心逻辑是一个状态机,持续监测环境声音并做出决策。

核心循环流程如下:

  1. 数据采集:在loop()函数中,以约20Hz的频率(delay(50))读取两个麦克风模块的模拟引脚值。
  2. 阈值判断:将读取的模拟值(0-1023)与预设的“静音阈值”比较。代码中简化为了digitalRead,这暗示我们可能使用了数字输出的麦克风模块,或者在实际代码中应使用analogRead()并配合阈值判断,例如if(analogRead(micPin1) < threshold)
  3. 沉默计时:如果两个麦克风都低于阈值(判定为“静音”),则开始累加silenceTime变量。每次循环增加0.05秒(对应50ms的延迟)。这个累加值就是系统感知到的“持续沉默时间”。
  4. 触发判断:将累加的silenceTime与一个预设的“尴尬沉默阈值”(代码中常量AWKS,设为4秒)进行比较。一旦沉默时间超过此阈值,系统判定“尴尬沉默”发生。
  5. 执行干预:调用rescue()函数,执行一系列打破沉默的操作: a.视觉提示1:伺服电机缓慢转动90度,升起提示牌。 b.无线通信:通过Serial.write(1)向连接XBee的串口发送一个触发信号(字节‘1’)。 c.音频提示:播放SD卡中预设的一段音乐或提示音(如“4.wav”)。 d.视觉提示2:远程的电机驱动板收到信号‘1’后,驱动步进电机旋转一定角度(如1000步),带动指针随机转动。
  6. 重置与待机:干预动作结束后,所有执行器复位,silenceTime清零,系统回到持续的监听状态。

电机驱动板的逻辑则简单得多:它持续监听串口(连接XBee)。一旦收到特定的触发字符(如‘1’),就改变步进电机的转动方向(通过motorState变量切换),执行正转或反转,实现随机指向的效果。

注意:原始代码中rescue()函数末尾的loop();和多个delay()的调用在逻辑上是错误且危险的。loop();会导致递归调用,可能最终导致栈溢出。正确的做法是让rescue()函数执行完毕后自然返回主loop(),或者用状态标志来管理。多个delay()也会阻塞程序过长时间。在实际优化时,应使用非阻塞的定时方式(如millis())来管理音频播放和电机动作的时序。

3. 硬件制作与机械组装详解

“工欲善其事,必先利其器”。SASSIE的物理实体是其魅力的重要部分。组装过程就像在搭建一个精密的桌面小装置。

3.1 结构件加工与预处理

所有激光切割件拿到后,第一步是进行精细打磨。激光切割的边缘会有���微的焦痕和毛刺,用细砂纸(建议400目以上)轻轻打磨每个边缘,直到触感光滑。这不仅是为了美观,更是为了防止木刺伤手,并让后续的胶合更紧密。

弧形侧板的弯曲工艺是本项目的一个手工亮点。项目中使用的是“湿弯法”:

  1. 用湿布均匀擦拭椴木条,使其充分湿润(但不要浸泡)。
  2. 小心地将其弯曲成所需的圆形弧度。这个过程要缓慢均匀用力,避免折断。
  3. 用遮蔽胶带将其临时固定在这个弯曲的形状上。
  4. 置于通风处自然晾干至少24小时。木材纤维在干燥后会固定在这个新的弯曲形态上。

实操心得:湿弯的成功率取决于木材的厚度和湿度。3mm的椴木板成功率很高。可以在弯曲的内侧用热风枪辅助加热(保持一定距离,避免烤焦),能加速定型并降低反弹。务必彻底干透后再进行下一步组装,否则后期可能会开裂或变形。

3.2 核心主机身组装步骤

组装顺序遵循“由内到外,由下至上”的原则,确保内部线路和部件在封闭前都已就位。

1. 底座部分组装:

  • 底部基板底座弧形外皮对齐,使用木工白乳胶粘合,用夹子固定至干燥。这是整个装置的基石。
  • 将四个底部侧边条支撑板先两两粘合,形成两个L形角件,然后再将它们粘到顶部基板的四个边角下方。这个结构构成了一个“托盘”的边框。
  • 关键一步:将步进电机用泡棉胶或尼龙扎带底座临时固定在顶部基板的中心位置。务必注意:电机的轴必须精确对准整个机器的几何中心,并且电机本体应该固定在“托盘”边框的同一侧(即未来朝向内部的一侧)。固定后,将步进电机垫片套在电机轴上。

2. 主体部分组装:

  • 主体底部板主体弧形外皮粘合。
  • 主体顶部环粘在弧形外皮的顶端,加固结构。
  • 主体侧边条插入顶部侧边条滑轨中,这个应该是可滑动或可调节的设计,用于连接主体和底座。
  • 微型伺服电机安装板垂直(成90度)粘在侧边条的顶部内侧。这里是伺服电机的家。

3. 总装与内部布线:

  • 将面包板、主控Arduino Uno(插着XBee SD Shield)以及电机驱动Arduino Uno,预先在底座“托盘”内布局好。规划好电源线(建议共用一个5V/2A以上的外接电源,通过面包板分压)和信号线的走向。
  • 将组装好的顶部基板组件(带着步进电机)盖到底座托盘上,确保所有传感器(目前主要是麦克风线)和执行器(伺服电机、喇叭)的线缆能通过基板上预留的孔洞穿出。
  • 主体组件通过其侧边条滑轨,安装到底座侧边条的对应滑槽内。此时,步进电机的轴应该穿过主体底部的孔,并与主体内部的某个传动结构连接(可能是直接固定,也可能通过联轴器连接一个指针)。
  • 将伺服电机侧向(即旋转轴水平)粘贴在伺服电机安装板的顶端。将其舵盘上安装好“提示牌”,并将伺服电机的三根线(电源、地、信号)沿着侧边条内部走线,用胶带固定,最终连接回主控板。
  • 最后,将“顶部标识”粘在伺服电机舵盘上,将“侧面标识”粘在主体外皮的醒目位置。

4. 麦克风支架组装:

  • 将四个麦克风底座片粘合成一个稳定的十字形或方形底座。
  • 将处理好的圆木棍(一端切平,一端切30度斜角)的平端粘在底座中心。
  • 麦克风安装板粘在木棍的30度斜面上。这个角度可以让麦克风更好地朝向对话区域。
  • 最后,用泡棉胶或小扎带将麦克风模块固定在该安装板上。

注意事项:在整个粘合过程中,白乳胶用量宜少不宜多,挤出溢出的胶水要及时用湿布擦净。每个粘合步骤后,都需要足够的夹持固定时间(至少1-2小时),确保强度。电路部分,所有杜邦线连接务必插紧,电机等大电流设备最好单独从电源取电,避免通过Arduino板载稳压芯片,以防过热。

4. 核心代码编写与调试实录

硬件是躯体,代码是灵魂。下面我们深入代码细节,并分享调试过程中踩过的坑和填坑方法。

4.1 主控板程序深度剖析与优化

原始代码提供了一个框架,但直接使用可能存在一些问题。我们来编写一个更健壮、功能更清晰的版本。

// SASSIE_MainController.ino #include <Servo.h> #include <SD.h> #include <TMRpcm.h> #include <SoftwareSerial.h> // 用于软串口与XBee通信,释放硬串口用于调试 // 引脚定义 #define SERVO_PIN 3 #define MIC_PIN_1 A0 // 使用模拟引脚 #define MIC_PIN_2 A1 #define SPEAKER_PIN 9 #define SD_CS_PIN 10 #define XBEE_RX 6 // 自定义软串口引脚 #define XBEE_TX 5 // 参数定义 #define SILENCE_THRESHOLD 50 // 模拟值阈值,需根据实际环境校准 #define AWKWARD_SILENCE_SECONDS 4.0 // 判定为尴尬沉默的时长(秒) #define LOOP_DELAY_MS 50 // 主循环延迟,对应20Hz采样率 // 全局变量 Servo myServo; TMRpcm audioPlayer; SoftwareSerial xbeeSerial(XBEE_RX, XBEE_TX); // RX, TX float accumulatedSilenceTime = 0; bool isInRescueMode = false; unsigned long lastActionTime = 0; const unsigned long rescueDuration = 15000; // 救援模式总持续时间15秒 void setup() { Serial.begin(115200); // 调试串口 xbeeSerial.begin(9600); // XBee通信串口 pinMode(MIC_PIN_1, INPUT); pinMode(MIC_PIN_2, INPUT); myServo.attach(SERVO_PIN); myServo.write(0); // 初始位置,牌子收起 audioPlayer.speakerPin = SPEAKER_PIN; audioPlayer.setVolume(5); // 音量1-7 if (!SD.begin(SD_CS_PIN)) { Serial.println("SD卡初始化失败!请检查卡和连线。"); while (1); // 卡住,因为音频播放依赖SD卡 } Serial.println("SASSIE 主控制器就绪。"); } void loop() { // 1. 读取麦克风数据 int mic1Value = analogRead(MIC_PIN_1); int mic2Value = analogRead(MIC_PIN_2); // 2. 判断当前是否静音(双麦克风同时低于阈值) bool isSilentNow = (mic1Value < SILENCE_THRESHOLD) && (mic2Value < SILENCE_THRESHOLD); // 3. 如果不是救援模式,则进行沉默检测 if (!isInRescueMode) { if (isSilentNow) { accumulatedSilenceTime += (LOOP_DELAY_MS / 1000.0); // 累加秒数 Serial.print("沉默累积: "); Serial.println(accumulatedSilenceTime); } else { // 有任何声音,重置沉默计时 accumulatedSilenceTime = 0; myServo.write(0); // 确保牌子收起 } // 4. 检查是否达到尴尬沉默阈值 if (accumulatedSilenceTime >= AWKWARD_SILENCE_SECONDS) { Serial.println("检测到尴尬沉默!启动干预程序。"); startRescueProcedure(); } } else { // 5. 处理救援模式下的状态维持与超时退出 if (millis() - lastActionTime > rescueDuration) { endRescueProcedure(); } } delay(LOOP_DELAY_MS); } void startRescueProcedure() { isInRescueMode = true; lastActionTime = millis(); // 动作1:升起伺服电机牌子 for (int angle = 0; angle <= 90; angle += 2) { myServo.write(angle); delay(30); // 缓慢升起,更有仪式感 } // 动作2:发送无线信号给电机板 xbeeSerial.write('A'); // ��送触发字符 Serial.println("已发送启动信号至电机板。"); // 动作3:播放提示音 audioPlayer.play("alert.wav"); // 播放一个短促提示音 delay(1000); // 等待提示音播放完毕(假设alert.wav长度约1秒) audioPlayer.play("background.wav"); // 播放背景音乐 Serial.println("救援程序已启动。"); } void endRescueProcedure() { // 停止播放音乐 audioPlayer.stopPlayback(); // 降下伺服电机牌子 for (int angle = 90; angle >= 0; angle -= 2) { myServo.write(angle); delay(30); } // 发送停止信号(可选,如果需要电机板复位) // xbeeSerial.write('S'); // 重置状态 accumulatedSilenceTime = 0; isInRescueMode = false; Serial.println("救援程序结束,系统复位。"); }

代码关键点解析:

  • 模拟读取与阈值判断:使用analogRead()获取更精细的声音强度数据,通过SILENCE_THRESHOLD灵活调整灵敏度。这个值需要通过串口监视器观察实际环境下的读数来校准。
  • 状态机管理:引入了isInRescueMode标志位,防止在干预过程中重复触发。这是对原代码逻辑的重要修正。
  • 非阻塞定时:使用millis()记录救援模式的开始时间,并在主循环中检查是否超时,从而优雅地退出救援模式,而不是用delay()长时间阻塞整个程序。
  • 软串口通信:使用SoftwareSerial库与XBee通信,将硬件串口Serial留给电脑调试,这样在运行时也能看到打印信息。

4.2 电机驱动板程序解析

电机驱动板的代码相对独立和简单。

// SASSIE_MotorDriver.ino #include <Stepper.h> #include <SoftwareSerial.h> #define STEPS_PER_REV 2048 // 28BYJ-48电机减速后的总步数(注意:不同厂家可能有差异) #define MOTOR_SPEED 10 // RPM,转速不宜过快 #define XBEE_RX 2 #define XBEE_TX 3 SoftwareSerial xbeeSerial(XBEE_RX, XBEE_TX); // 连接XBee模块 Stepper myStepper(STEPS_PER_REV, 8, 10, 9, 11); // 引脚顺序:IN1, IN3, IN2, IN4 (根据ULN2003板子调整) bool motorDirection = true; // true为正转,false为反转 void setup() { Serial.begin(115200); xbeeSerial.begin(9600); myStepper.setSpeed(MOTOR_SPEED); Serial.println("电机驱动板就绪,等待指令..."); } void loop() { if (xbeeSerial.available() > 0) { char command = xbeeSerial.read(); Serial.print("收到指令: "); Serial.println(command); if (command == 'A') { // 收到主控板的触发指令 Serial.println("执行随机指向..."); // 随机决定转动方向和步数,增加随机性 long stepsToMove = random(500, STEPS_PER_REV / 2); // 随机转动半圈以内的步数 if (motorDirection) { myStepper.step(stepsToMove); } else { myStepper.step(-stepsToMove); } motorDirection = !motorDirection; // 下次反转方向 delay(1000); // 停顿一下,指示效果更明显 } // 可以添加其他命令,如 'S' 用于复位等 } // 短暂延迟,降低CPU占用 delay(10); }

要点说明:

  • 步进电机步数28BYJ-48的步距角是5.625°,经过64倍减速,实际输出轴步距角为5.625/64=0.08789°,因此一转需要360/0.08789≈4096步。但很多驱动板在四拍模式下是2048步/转,务必根据你的电机实测确认。可以用myStepper.step(2048)测试是否刚好转一圈。
  • 引脚顺序Stepper库的引脚顺序需要与ULN2003驱动板上的IN1-IN4对应。如果电机不转或抖动,最常见的原因就是引脚顺序不对,调整一下即可。
  • 随机性:通过random()函数生成随机的转动步数,使得每次指向的位置都不同,真正实现“随机”选择发言者。

5. 系统集成、调试与问题排查

当硬件组装完毕,代码也分别上传后,最激动人心也最挑战耐心的系统集成调试就开始了。

5.1 上电前检查清单

  1. 电源检查:确保所有Arduino、电机、喇叭的供电电压正确(均为5V)。建议使用一个5V/3A以上的独立电源适配器,通过面包板为整个系统供电,避免USB供电功率不足。
  2. 连线复查:对照电路图,逐一检查每根杜邦线的连接,特别是电机、伺服机、麦克风的电源正负极是否接反。步进电机的四根线序是否与代码定义一致。
  3. 机械检查:手动转动步进电机轴和伺服电机舵盘,确保没有任何机械干涉或卡死。确保所有结构件粘合牢固。
  4. SD卡:确认格式化为FAT16或FAT32,音频文件为wav格式,采样率不超过16kHz,单声道,文件名与代码中调用的一致(如alert.wav)。

5.2 分模块调试流程

不要急于全部连接,分步调试是成功的关键。

第一步:调试主控板基础功能(不接执行器)

  1. 只连接主控Arduino、两个麦克风模块到电脑USB。
  2. 上传主控板代码,打开串口监视器(波特率115200)。
  3. 对着麦克风说话或制造声音,观察串口输出的模拟值变化。确定一个合适的SILENCE_THRESHOLD。安静环境下,模拟值可能很低(如10-30);正常说话时,可能会跳到几百。阈值可以设在这两者之间,比如50。
  4. 保持安静,观察accumulatedSilenceTime是否开始累加,并在达到4秒后打印“检测到尴尬沉默!”。

第二步:调试音频播放

  1. 接上SD卡模块和喇叭。
  2. startRescueProcedure()函数中临时注释掉伺服和无线通信部分,只保留播放音频的代码。
  3. 触发沉默检测(或临时修改代码让其自动触发),听是否能正常播放alert.wavbackground.wav

第三步:调试伺服电机

  1. 接上伺服电机。
  2. 同样,在救援函数中只测试伺服电机转动部分。观察是否能平滑地从0度转到90度,再转回来。

第四步:调试无线通信与电机板

  1. 将两个XBee模块分别插入两个扩展板,并确保它们已配对(可通过XCTU软件配置为同一网络ID,互为点对点通信)。
  2. 分别给主控板和电机板上电(不连接电机)。
  3. 主控板触发救援程序时,观察电机板串口是否打印“收到指令: A”。同时,主控板串口应打印“已发送启动信号”。
  4. 最后连接步进电机到驱动板,测试收到指令后电机是否按预期转动。

5.3 常见问题与解决方案实录

以下是我在调试SASSIE过程中遇到的实际问题及解决方法,希望能帮你避开这些坑:

问题1:麦克风一直检测为有声音或一直无声。

  • 排查:打开串口监视器,持续观察analogRead的原始值。用手捂住麦克风或大力拍手,看数值是否有剧烈变化。如果数值始终不变,检查接线(特别是模块的AO引脚是否接对了模拟输入引脚)和模块供电。
  • 解决:调整SILENCE_THRESHOLD。如果环境底噪很大,可能需要适当提高阈值。也可以考虑在代码中加入“动态阈值”或“噪声基线学习”的算法,让系统在上电后前几秒自动学习环境噪声水平。

问题2:步进电机抖动但不转,或转动无力。

  • 排查:这是最典型的问题。首先,确认ULN2003驱动板上的跳线帽是否已接上(如果提供5V供电)。其次,用万用表测量驱动板输出到电机的电压是否在5V左右。
  • 解决
    • 检查引脚顺序Stepper myStepper(STEPS_PER_REV, 8, 10, 9, 11);这行代码中的引脚顺序[8,10,9,11]对应驱动板的[IN1, IN2, IN3, IN4]。如果不对应,电机会抖动。尝试不同的顺序组合。
    • 降低速度:将myStepper.setSpeed()的值调低,比如从15调到10。
    • 检查电源:步进电机启动瞬间电流很大,确保电源能提供至少1A的电流。尝试单独给驱动板外接5V电源。
    • 确认步数:尝试myStepper.step(2048),看是否刚好转一圈。如果不是,调整STEPS_PER_REV宏定义的值。

问题3:SD卡无法初始化,或无法播放音频。

  • 排查:首先确认SD卡格式化为FAT16/32。检查SD模块与Arduino的接线(CS/SCK/MOSI/MISO),尤其是CS引脚(通常为10)是否与代码中SD.begin(10)一致。
  • 解决
    • 尝试使用SD.begin(4)或其他引脚,并修改代码。
    • 确保音频文件是单声道、16kHz或以下采样率、16位PCM编码的WAV文件。可以使用免费软件如Audacity进行转换。
    • TMRpcm库对文件名有要求,尽量使用8.3格式(如sound.wav),避免中文和长文件名。

问题4:无线通信不稳定,电机板收不到信号。

  • 排查:确保两个XBee模块的波特率设置一致(代码中均为9600)。检查软串口引脚定义是否正确,RX接TX,TX接RX。
  • 解决
    • 将两个XBee模块靠近测试,排除距离问题。
    • 在电机板代码中,持续打印xbeeSerial.available()的值,看是否有数据到来。
    • 考虑增加简单的通信协议,比如主控板发送“A\n”,电机板读取直到换行符,提高抗干扰能力。

问题5:系统运行一段时间后Arduino重启或行为异常。

  • 排查:这通常是电源问题代码阻塞导致的。
  • 解决
    • 电源:所有电机(特别是步进电机)务必使用独立的外接电源供电,不要从Arduino的5V引脚取电。Arduino的稳压芯片无法提供持续的大电流。
    • 代码:检查代码中是否使用了过长的delay(),特别是在播放长音频时。优化为基于millis()的非阻塞定时控制,如我提供的优化代码所示。
    • 看门狗:可以考虑启用Arduino的内部看门狗定时器,在程序卡死时自动复位。

完成所有调试后,你就可以欣赏SASSIE的工作了。当对话陷入沉默,那个小装置缓缓立起牌子,播放一段轻松的音乐,并转动指针随机指向一人——那一刻,技术带来的不仅是功能的实现,更是一种有趣的社交实验和体验。这个项目从想法到实现,涉及了电子、编程、机械、通信多个领域,是一次非常综合的锻炼。希望这份详细的拆解,能帮助你复现或创造出属于你自己的智能交互装置。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询