基于ESP32的创意互动机器人:从功能实现到情感化设计

发布时间:2026/7/28 8:32:29
基于ESP32的创意互动机器人:从功能实现到情感化设计 1. 项目缘起从“有用”到“可爱”的电子叛逆每次逛电子论坛或者开源硬件社区满眼都是“智能家居中枢”、“物联网网关”、“工业数据采集”这些听起来就肩负重任的项目。有用当然好但做久了总觉得少了点电子制作最初的那种纯粹乐趣——那种不为解决某个具体问题仅仅因为“这想法挺酷”或者“做出来一定很好玩”而动手的冲动。ESP32这颗芯片能力太强了强到我们总忍不住用它去做一些“正经事”却忘了它也可以很“不正经”。于是这个“可爱的无用机器人”的想法就诞生了。它的核心设计哲学就一条摒弃一切功利性目的极致追求情绪价值与互动趣味。它不需要帮你开关灯、不需要汇报温湿度、更不需要完成任何有实际产出的任务。它的存在就是为了在你伏案工作疲惫时转过头能看到一个憨态可掬的小东西在桌面上进行一些毫无意义的、甚至有点蠢萌的“行为艺术”然后会心一笑。这听起来很“无用”但对缓解现代人的精神内耗或许比很多“有用”的设备更有效。这个项目非常适合那些已经玩过Arduino基础项目对ESP32的Wi-Fi、蓝牙功能有所了解但想跳出功能实现框架探索嵌入式系统在情感化交互和创意表达上可能性的朋友。它不追求技术的复杂度而是聚焦于如何用简单的电子模块和代码赋予硬件一种独特的“性格”。下面我就把自己从构思到实现这个“无用良品”的全过程包括硬件选型的“小心机”、代码编写的“恶趣味”以及那些让机器人真正“活”起来的细节技巧毫无保留地分享出来。2. 整体设计与核心思路如何定义“无用”与“可爱”做一个“无用”的机器人比做一个“有用”的更难因为“有用”有明确的功能指标如精度、速度、稳定性而“无用”的成功标准完全在于它能否唤起人的情感共鸣。我的设计思路围绕三个核心展开不可预测的行为逻辑、拟人化的物理交互、以及低技术门槛的“萌系”外观。2.1 行为逻辑从确定性到“小心思”传统机器人的行为是确定性的收到指令A必然执行动作B。而“无用机器人”的行为需要引入一定的随机性和状态机让它看起来有自己的“小心思”。我采用了多层状态机叠加伪随机触发的模型。基础状态比如“发呆”、“闲逛”、“好奇”、“受惊”、“疲惫”。状态迁移不是定时切换而是通过传感器输入如检测到有人靠近、听到声音结合内部计时器和随机数来决定。例如即使在“发呆”状态也有小概率突然切换到“闲逛”没有任何外部原因就是“它想动了”。动作执行每个状态对应一组动作库。执行时从动作库中随机选取一个序列并加入微小的随机延时和幅度变化避免动作机械重复。这种设计使得机器人的行为无法被完全预测每次互动都有新鲜感这是“可爱”和“拟人”的关键。2.2 交互设计放弃屏幕拥抱实体为了强化“实体玩具”的质感我刻意避免了使用OLED屏幕显示表情虽然那很酷。屏幕表情太直接、太数字化了。我选择用物理动作、灯光和声音这三种更原始的媒介来传递情绪。动作通过舵机实现头部转动、身体摇摆、手臂如果有抬起等。动作速度刻意调得不那么精准和快速带一点缓动和偶尔的“卡顿”模拟生物的不完美感。灯光采用RGB LED但不用来显示信息而是作为“情绪光”。例如“好奇”时缓慢呼吸变换色彩“受惊”时快速闪烁红色“开心”时温和地渐变。声音用一个简单的无源蜂鸣器播放极简的旋律或音效音调不高音量不大像是机器人的自言自语或哼唱。2.3 外观与结构毛绒与硬核的碰撞外观是“可爱”的第一要素。我选择了毛绒公仔外壳内部硬核电子骨架的方案。去玩具店买一个手掌大小的、造型简单的毛绒玩具比如小熊、小猫、外星人作为机器人的“皮肤”。内部用轻木片或3D打印一个简易骨架用于固定ESP32主板、电池和舵机。舵机的转轴通过巧妙的连杆机构与玩偶的头部或肢体连接。这样从外面看是一个完全人畜无害的毛绒玩具内部却是一套完整的嵌入式系统这种反差本身就充满了趣味。3. 硬件选型与“心机”清单硬件不是为了堆性能而是为了精准服务于“行为”和“外观”。以下是经过多次迭代后的优选清单每一件都有其不可替代的理由。3.1 核心大脑ESP32-DevKitC V4为什么是ESP32除了强大的双核处理能力和丰富的接口本项目最看重的是其超低功耗深度睡眠模式和蓝牙功能。低功耗机器人大部分时间可能在“发呆”深度睡眠只有被传感器唤醒或定时器触发时才活动。ESP32的深度睡眠电流可低至10μA左右极大地延长了电池续航让它能真正作为一个摆件长期待机。蓝牙计划后期增加手机App遥控或互动功能蓝牙比Wi-Fi更省电连接也更快捷。虽然本项目初期未启用但预留了可能性。丰富的GPIO和PWM足以驱动多个舵机和LED。注意市面上ESP32开发板型号繁多务必选择带有使能EN引脚和GPIO0引脚引出的型号这是实现可靠深度睡眠和烧录的基础。ESP32-DevKitC是经过充分验证的可靠选择。3.2 运动执行器SG90微型舵机选择SG90是因为它便宜、普及、够用。它的扭矩1.6kg/cm足以驱动一个小型毛绒玩具的头部转动。我们需要两个一个用于左右摇头Pan一个用于上下点头Tilt构成一个简单的二自由度云台。“不完美”调校不要将舵机角度范围用到极限如0-180度通常设定在30-150度之间避免堵转噪音和电流冲击。在代码中为舵机运动增加非线性缓动函数如ease-in-out让转动看起来更柔和自然而不是生硬的“咔咔”到位。3.3 感知器官传感器选型的减法艺术“无用”不代表不需要感知世界而是感知的目的不是为了逻辑判断而是为了触发有趣的反应。HC-SR04超声波传感器用于检测前方障碍物或有人靠近。这不是为了避障而是为了制造互动。比如当手突然靠近它时它可以做出“受惊后退”或“好奇探身”的动作。MAX9814驻极体麦克风放大模块用于检测声音。不是为了识别语音命令而是检测声音的突然出现拍手、咳嗽或持续存在环境噪音。突然的声响可以触发“受惊”状态持续的环境音可能让它进入“伴随节奏摇摆”的状态。可选MPU6050姿态传感器放在机器人内部用于检测自己被拿起、摇晃或翻转。这可以触发“晕眩”、“开心”被摇晃或“抗议”被倒置等状态极大增强了物理交互感。3.4 情绪表达灯光与声音WS2812B RGB LED只需一颗安装在机器人眼睛或胸口位置。使用Adafruit NeoPixel库可以轻松实现复杂的灯光动画。关键是亮度要调低直接用setBrightness(30)左右让它作为氛围光而不是指示灯。无源蜂鸣器连接一个GPIO引脚即可。通过简单的tone()函数播放频率。秘诀在于使用简短的、不完整的旋律片段或自然音效如一声“嘀嘟”、类似小鸟的鸣叫而不是播放完整的歌曲。3.5 能源与结构电源采用一块3.7V 1000mAh的锂电池配合TP4056充电保护一体模块。这比干电池组更紧凑、可充电。ESP32需要3.3V舵机需要5V因此需要一个低压差稳压模块如AMS1117-5.0将电池电压升压/稳压到5V给舵机供电同时电池电压直接接入ESP32的3.3V输入引脚注意ESP32的Vin引脚通常支持更宽的电压范围但直接接3.7V锂电池经过保护板后约4.2V-3.0V是可行的更稳妥是接3.3V引脚。结构用2-3毫米厚的轻木板激光切割或手工制作一个内部骨架。骨架的核心是一个“脊柱”用于固定ESP32主板和电池。前方延伸出两个舵机安装位。连杆可以用回形针或细铁丝弯制一头连接舵机摆臂另一头小心地缝合或粘贴到毛绒玩具内部的关键点。硬件连接总览表模块连接至ESP32引脚说明SG90舵机1 (Pan)GPIO12PWM信号控制左右转SG90舵机2 (Tilt)GPIO13PWM信号控制上下转HC-SR04 TrigGPIO5输出触发信号HC-SR04 EchoGPIO18输入回波信号MAX9814 OUTGPIO34模拟输入检测声音强度WS2812B LEDGPIO15数据输入无源蜂鸣器GPIO14数字输出PWM发声MPU6050 (可选)SDA: GPIO21, SCL: GPIO22I2C接口TP4056模块 B电池正极TP4056模块 B-电池负极TP4056模块 OUTAMS1117-5.0输入TP4056模块 OUT-公共地(GND)AMS1117-5.0输出舵机VCC、ESP32 Vin(可选)提供5V电源电池正极ESP32 3.3V引脚 (需谨慎)或通过AMS1117-3.3供电重要心得在焊接或连接所有设备前务必先在面包板上搭建测试电路逐一验证每个模块功能正常。特别是舵机空载测试没问题后装上负载连杆和外壳时电流会增大要确保电源能带动两个舵机同时运动而不崩溃。4. 软件架构与核心代码解析软件是机器人的“灵魂”。我们使用Arduino IDE进行开发因为其库生态丰富对创意编程友好。4.1 开发环境搭建与库管理首先确保Arduino IDE已安装ESP32开发板支持。在“首选项”的附加开发板管理器网址中添加https://espressif.github.io/arduino-esp32/package_esp32_index.json。然后在开发板管理器中搜索安装“ESP32 by Espressif Systems”。需要安装的核心库Adafruit NeoPixel用于驱动WS2812B LED。ESP32ServoESP32专用的舵机库比标准Servo库更稳定。Adafruit MPU6050(可选)如果使用MPU6050。4.2 核心状态机实现代码的核心是一个简单的有限状态机FSM。我们定义几个状态枚举和对应的处理函数。// 定义机器人状态 enum RobotState { STATE_SLEEPING, // 深度睡眠 STATE_IDLE, // 发呆 STATE_CURIOUS, // 好奇 STATE_ROAMING, // 闲逛 STATE_STARTLED, // 受惊 STATE_PLAYFUL // 玩耍 }; RobotState currentState STATE_IDLE; unsigned long stateEnterTime 0; // 记录进入当前状态的时间 const unsigned long STATE_TIMEOUT 15000; // 状态超时时间毫秒 // 状态处理函数声明 void enterState(RobotState newState); void updateStateIdle(); void updateStateCurious(); void updateStateRoaming(); void updateStateStartled(); void updateStatePlayful();enterState函数负责状态切换时的初始化工作比如改变LED颜色、播放进入音效、重置计时器等。void enterState(RobotState newState) { currentState newState; stateEnterTime millis(); switch(currentState) { case STATE_IDLE: // 设置LED为缓慢呼吸的暖白色 setLedBreathing(255, 240, 220); // 舵机回到放松的中位 servoPan.write(90); servoTilt.write(80); break; case STATE_CURIOUS: // LED快速闪烁淡蓝色 setLedBlink(150, 200, 255, 200); // 播放一个简短的“嘀”声 tone(BUZZER_PIN, 523, 100); // 频率523Hz (C5)持续100ms break; case STATE_STARTLED: // LED快速闪烁红色 setLedBlink(255, 50, 50, 100); // 播放受惊吓的音效两个快速音 tone(BUZZER_PIN, 392, 70); // G4 delay(80); tone(BUZZER_PIN, 311, 70); // D#4 // 舵机快速后仰模拟吓一跳 servoTilt.write(60); delay(200); break; // ... 其他状态 } }4.3 传感器驱动与数据滤波传感器数据不能直接使用需要滤波以抗干扰并转化为有意义的“事件”。超声波测距HC-SR04容易受到声波反射干扰需要中值滤波和持续检测逻辑。const int TRIG_PIN 5; const int ECHO_PIN 18; const long SOUND_SPEED 34300; // 厘米/秒 const int DETECTION_THRESHOLD 20; // 厘米小于此距离认为有物体靠近 long getFilteredDistance() { long durations[5]; for (int i 0; i 5; i) { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); durations[i] pulseIn(ECHO_PIN, HIGH, 30000); // 超时30ms } // 简单排序取中值 sortArray(durations, 5); long medianDuration durations[2]; long distance (medianDuration * 0.0343) / 2; if (distance 0 || distance 500) distance 500; // 无效值处理 return distance; } void checkProximity() { static unsigned long lastCheck 0; if (millis() - lastCheck 200) { // 每200ms检查一次 lastCheck millis(); long dist getFilteredDistance(); if (dist DETECTION_THRESHOLD) { // 触发“好奇”或“受惊”状态加入随机性 if (random(10) 6) { // 40%概率受惊 enterState(STATE_STARTLED); } else { enterState(STATE_CURIOUS); } } } }声音检测MAX9814输出模拟值我们检测其峰值来判断是否有突发声响。const int MIC_PIN 34; const int SOUND_THRESHOLD 800; // 阈值需根据实际环境调整 int soundLevel 0; void checkSound() { static int maxAmp 0; static unsigned long peakTime 0; int currentAmp analogRead(MIC_PIN); if (currentAmp maxAmp) { maxAmp currentAmp; peakTime millis(); } // 每50ms采样一次并检查过去100ms内的峰值 if (millis() % 50 0) { if (maxAmp SOUND_THRESHOLD (millis() - peakTime) 100) { // 检测到突发声响 triggerSoundEvent(); maxAmp 0; // 重置峰值 } else { // 缓慢衰减峰值防止持续高值 maxAmp maxAmp * 0.95; } } } void triggerSoundEvent() { // 根据当前状态和随机数决定如何反应 if (currentState STATE_IDLE || currentState STATE_SLEEPING) { if (random(10) 5) { enterState(STATE_CURIOUS); } } else if (currentState STATE_ROAMING) { // 可能被声音吸引转向声源方向这里简化处理 servoPan.write(90 random(-30, 30)); } }4.4 “闲逛”行为的实现伪随机路径生成“闲逛”状态是机器人的主要自主行为。我们设计一个简单的算法让机器人头部进行看似随意、实则自然的运动。int targetPan 90; // 左右目标角度 int targetTilt 80; // 上下目标角度 int currentPan 90; int currentTilt 80; unsigned long nextMoveTime 0; void updateStateRoaming() { // 1. 检查是否该决定下一个动作 if (millis() nextMoveTime) { // 随机决定下一个动作类型 int action random(0, 100); if (action 40) { // 40%概率缓慢扫视 targetPan 90 random(-60, 60); targetTilt 80 random(-20, 10); } else if (action 70) { // 30%概率点头或摇头快速小幅度往复 targetPan currentPan random(-20, 20); targetTilt currentTilt random(-10, 10); } else if (action 90) { // 20%概率长时间凝视一个方向 targetPan 90 random(-45, 45); targetTilt 75; // 略微低头显得专注 } else { // 10%概率做一个“伸展”动作抬头后复位 targetTilt 60; delay(1000); targetTilt 80; } // 随机决定动作持续时间 nextMoveTime millis() random(2000, 8000); // 小概率伴随灯光变化或声音 if (random(10) 0) { setLedBlink(random(100,255), random(100,255), random(100,255), 300); } if (random(20) 0) { tone(BUZZER_PIN, 262 random(-50, 50), 150); // 随机音调 } } // 2. 平滑移动到目标位置使用缓动函数 currentPan easeMove(currentPan, targetPan, 2); // 每次移动最大2度 currentTilt easeMove(currentTilt, targetTilt, 2); servoPan.write(currentPan); servoTilt.write(currentTilt); // 3. 检查传感器可能被外界打断 checkProximity(); checkSound(); } // 一个简单的线性缓动函数实际可以用更复杂的easing函数 int easeMove(int current, int target, int step) { if (abs(current - target) step) return target; if (current target) return current step; else return current - step; }4.5 深度睡眠与唤醒为了省电当机器人长时间无人互动时应进入深度睡眠。我们可以用一个定时器或者在“发呆”状态超时后触发。void goToDeepSleep() { // 设置唤醒源可以用外部引脚如触摸引脚或定时器 // 这里使用定时器唤醒睡眠10分钟 const uint64_t sleepTimeMicroseconds 10 * 60 * 1000000ULL; // 10分钟 // 关闭外设以省电 setLedOff(); servoPan.detach(); servoTilt.detach(); Serial.println(进入深度睡眠10分钟后或触摸唤醒引脚后唤醒); delay(100); // 等待串口输出完成 // 配置定时器唤醒 esp_sleep_enable_timer_wakeup(sleepTimeMicroseconds); // 也可以配置触摸唤醒如接一个金属片到GPIO4 // touchAttachInterrupt(T4, wakeupCallback, 40); // 阈值40 esp_deep_sleep_start(); // 代码执行将在此停止直到被唤醒 } // 在主循环中检查状态超时 void loop() { // ... 更新当前状态的行为 // 检查是否在“发呆”状态且超时且无传感器触发 if (currentState STATE_IDLE (millis() - stateEnterTime STATE_TIMEOUT)) { // 小概率进入睡眠大概率只是换个姿势继续发呆 if (random(10) 7) { // 30%概率进入睡眠 goToDeepSleep(); } else { // 换个发呆姿势 targetPan 90 random(-20, 20); targetTilt 80 random(-10, 5); enterState(STATE_IDLE); // 重新进入IDLE状态重置计时器 } } delay(20); // 主循环延迟控制更新频率约50Hz }5. 机械组装与外观融合技巧这是将电路板变成“生命”的关键一步也是最需要耐心和创意的地方。5.1 内部骨架制作材料处理用轻木板切割出大约8cm x 4cm的底板作为“躯干”。在底板前端垂直固定一块高约5cm的立板作为“颈部”。在立板上下位置用热熔胶或螺丝固定两个SG90舵机。确保舵机转轴朝向正确一个控制左右一个控制上下。连杆设计用一根细铁丝或硬质铜线一端固定在舵机摆臂上另一端弯成合适的形状准备连接玩偶头部。关键技巧在铁丝与舵机摆臂连接处套上一小段热缩管或硅胶管既能紧固又能缓冲。连杆的长度需要反复调试确保玩偶头部转动范围自然且不卡顿。主板与电池固定将ESP32开发板和TP4056充电模块用尼龙柱或双面胶固定在底板后方。电池用扎带或魔术贴固定在底板下方降低重心。5.2 玩偶改造与结合选择玩偶最好选择头部较大、颈部明显的毛绒玩偶。避免毛太长太密会影响舵机力量和传感器。开口与固定在玩偶后背或底部用剪刀小心剪开一条约6-8厘米的缝作为“手术入口”。将内部填充棉掏出一些腾出空间放入骨架。连接头部将连杆的末端用针线牢牢缝合在玩偶头部内部的正中央。重要心得缝合点一定要结实并且位于头部重心附近。可以先不缝合手动拿着骨架测试头部运动找到最自然、阻力最小的连接点后再下针。隐藏与美化将骨架塞入玩偶体内调整位置使头部姿态自然。用剩余的填充棉轻轻塞回去覆盖住电子设备但注意不要挤压舵机转轴和连杆。最后用隐藏针法缝合开口。传感器超声波、麦克风可以小心地从玩偶的眼睛、耳朵或嘴巴位置露出或者巧妙隐藏。5.3 最终调试与“性格”微调组装完成后上电测试。此时的重点不再是功能而是调校机器人的“性格”。动作速度在代码中调整easeMove函数的step参数改变动作快慢。动作慢显得慵懒、呆萌动作快显得活泼、神经质。随机性权重调整状态转换和动作选择中的随机数概率。增加“闲逛”中长时间凝视的概率会显得它更“深沉”增加小动作和声音的概率则显得更“活泼好动”。灯光色调调整RGB值。暖色调橙、黄给人温暖、安心的感觉冷色调蓝、紫可能显得更神秘或科技感。呼吸灯的频率也能传达情绪缓慢呼吸像在睡觉快速闪烁像在激动。音效设计用tone()函数尝试不同的频率和时长组合。短促的“嘀”声像疑问滑音频率连续变化可以模拟撒娇或不满。6. 常见问题与排查实录在制作过程中你几乎一定会遇到下面这些问题。这里是我的踩坑记录和解决方案。6.1 电源与舵机问题问题1舵机运动时ESP32不断重启或WS2812B LED闪烁/变色。原因舵机启动瞬间电流很大可达500mA-1A导致电源电压瞬间被拉低造成ESP32和LED供电不足复位。解决方案电源分路确保舵机使用独立的5V电源线从AMS1117-5.0输出直接引出并与ESP32的电源在TP4056输出端并联而不是串联。加大电容在AMS1117-5.0的输入和输出端分别并联一个1000μF和100μF的电解电容用于缓冲瞬间大电流。软件限流在代码中避免两个舵机同时从静止状态启动。给它们错开几十毫秒的启动时间或者让它们缓慢运动到目标位置使用缓动函数本身就有帮助。问题2电池续航远低于预期。原因深度睡眠未成功进入或睡眠后仍有高耗电模块在工作。排查使用万用表串联测量进入深度睡眠后的整机电流。正常应在100μA以下。如果达到mA级别说明有漏电。检查代码确保在goToDeepSleep()函数中正确detach()了舵机舵机驱动芯片仍在耗电并关闭了LED。检查硬件拔掉所有外设舵机、传感器、LED仅留ESP32测睡眠电流。然后逐一接回定位耗电模块。有些传感器模块的使能引脚需要拉低才能彻底断电。6.2 传感器误触发与行为异常问题3机器人经常无缘无故“受惊”或“好奇”。原因超声波或声音传感器受到环境干扰。解决方案超声波滤波如前文代码所示采用中值滤波而非平均值。同时可以设置一个“持续检测”逻辑要求距离小于阈值的状态维持至少3次连续测量约600ms才触发事件避免飞虫或飘过的纸张引起误报。声音阈值动态调整环境噪音会变。可以增加一个简单的自适应阈值算法如每10秒采样100个点取平均值加上一个固定偏移量作为新的阈值。void calibrateSoundThreshold() { long sum 0; for(int i0; i100; i) { sum analogRead(MIC_PIN); delay(10); } SOUND_THRESHOLD (sum / 100) 150; // 平均值150作为新阈值 }状态冷却触发一次事件后设置一个“冷却时间”如5秒在此期间忽略同类传感器事件防止机器人“抽搐”。问题4动作卡顿或不流畅。原因主循环delay()使用不当或舵机控制代码阻塞。解决方案摒弃delay()采用非阻塞的时间检查模式。本文示例代码中已大量使用millis()对比来判断时间间隔这是保证系统响应流畅的关键。确保舵机的平滑移动函数如easeMove每轮循环只移动一小步而不是一次性write()到目标角度。6.3 软件与调试问题问题5程序上传失败提示“Timed out waiting for packet header”或“Failed to connect to ESP32”。原因ESP32进入深度睡眠或某些特殊状态后上传程序需要手动进入下载模式。解决方案在上传前按住开发板上的“BOOT”按钮不放再按一下“EN”按钮复位然后松开“EN”等待1秒后再松开“BOOT”。这时ESP32会进入下载模式串口监视器会显示“Waiting for download”。此时再点击Arduino IDE的上传按钮即可。问题6想给机器人增加新功能如蓝牙遥控但代码越来越乱。建议在项目初期就采用更模块化的设计。将不同功能封装成类Class例如BehaviorManager行为管理、SensorHub传感器集线器、ActuatorController执行器控制。主循环只负责调用这些模块的update()方法。这样后续增加功能只需要新建模块并在主循环中添加一行调用代码结构清晰易于维护。7. 进阶玩法与扩展思路当你完成了基础版可能会觉得不过瘾。这里有几个方向可以让你的“无用机器人”变得更有趣。7.1 融入简单AI让互动更有深度利用ESP32的Wi-Fi功能连接家庭局域网通过HTTP请求调用免费的在线AI API如情感分析、简单对话。思路机器人将麦克风采集到的环境音经过FFT简化或预设的“心情指数”通过Wi-Fi发送到本地服务器或云端API。API返回一个情感关键词如“happy”, “calm”, “surprised”。机器人根据这个关键词调整自己的行为模式和灯光颜色。虽然响应速度慢1-2秒但这种延迟反而制造出一种“它在思考”的错觉增加了神秘感和趣味性。7.2 多机器人社交网络制作两个或更多个“无用机器人”让它们之间通过ESP-NOW一种低功耗的ESP32间直接通信协议进行简单的“交流”。实现每个机器人有唯一ID。当机器人A进入“好奇”或“玩耍”状态时可以通过ESP-NOW广播一条消息。机器人B收到后可能会做出“回应”转向A的方向、闪烁灯光、发出声音甚至改变自己的状态。你可以设计一套极其简单的“协议”比如消息类型1打招呼2表达开心3求助。这样它们就能在桌面上上演一出出无声的小剧场。7.3 物理输出扩展不止于摇头晃脑震动马达在机器人底部加一个小型震动马达手机里那种。当它“受惊”或“生气”时让整个身体微微震动触感反馈非常直接有趣。舵机驱动的手臂增加一对更小的舵机如SG90或更小的9g舵机作为手臂。可以做出挠头、挥手、捂脸等更丰富的动作。注意电源负载可能需要更强的电池或独立的舵机驱动板。小型OLED屏幕最终还是用了如果实在想显示表情可以用一个0.96寸的OLED屏幕但不要显示复杂的像素画。只显示两个简单的“点”作为眼睛让这两个“点”做缓慢的眨眼、变成“”形状表示紧张、变成“··”表示困倦。用极简的图形来传递情绪比复杂的动画更含蓄和有力。这个项目的魅力在于它没有标准答案。你可以根据自己的喜好赋予你的机器人完全不同的“性格”。我的第一个版本是个慵懒的、对什么都爱答不理的小熊第二个版本则是个有点神经质、容易被吓一跳的小外星人。代码中的那些概率数字、动作参数、灯光颜色就是你为它塑造性格的画笔。放下对“有用”的执念享受创造生命哪怕是电子生命的纯粹乐趣这个过程本身就是最大的收获。当你看到自己创造的这个小东西在桌角自顾自地演绎着它的“数字生命”时那种成就感是任何一个功能性的项目都无法给予的。