在这里插入图片描述
基于Arduino与BLDC(无刷直流电机)构建的“环境感知主动情绪响应机器人”,是一套将多模态环境感知、轻量化AI推理与BLDC拟人化执行深度融合的嵌入式情感计算架构。该方案突破了传统Arduino仅能执行简单逻辑的局限,通过边缘侧情绪识别与BLDC柔顺运动控制,赋予机器人在导览陪伴场景中“察言观色”与“情感表达”的能力。

主要特点

  1. 边缘侧轻量化多模态感知与融合
    区别于依赖云端计算的方案,该系统将情绪识别下沉至Arduino边缘端,实现低延迟与高隐私保护。系统通过搭载微型摄像头(如OV7670或ESP32-CAM)采集图像,运行基于TensorFlow Lite Micro训练的轻量化卷积神经网络(CNN),提取面部表情特征(如嘴角上扬、眉毛下垂);通过麦克风阵列采集语音,结合开源语音识别库进行关键词提取和语调分析(如音调高低、语速快慢),判断情绪状态(兴奋、悲伤);通过压力传感器或触摸传感器感知用户的互动力度(如轻拍、紧抱),作为情绪识别的辅助输入。在ESP32等具备DSP指令集的芯片上,采用决策级或特征级融合策略,实时判断用户的情绪状态。
  2. 基于FOC的BLDC拟人化情绪表达
    在此系统中,BLDC电机不再是简单的动力源,而是情绪表达的“执行器官”。通过FOC(磁场定向控制)精确控制转矩电流(Iq),实现力矩柔顺控制。例如,在识别到用户悲伤时,机器人头部的转向不再是刚性的匀速运动,而是带有“迟疑”和“轻柔”的变加速运动,模拟人类的关切姿态;识别到兴奋时,则可通过差速扭矩实现小幅度的原地“雀跃”。此外,BLDC电机的PWM频率可与语音合成器联动,在机器人发声时让头部电机产生微幅振动,模拟人类发声时的生理特征。
  3. 基于状态机的交互叙事引擎
    系统在Arduino上构建有限状态机(FSM),管理复杂的交互逻辑。定义“中性”、“快乐”、“悲伤”、“警惕”等状态,例如连续检测到用户笑声且语调高昂,则从“中性”迁移至“快乐”状态,触发预设的“陪伴玩耍”行为序列。同时,利用EEPROM或外部Flash存储短期交互历史(如“上次用户抚摸头部后情绪好转”),实现基于上下文的个性化响应,而非简单的刺激-反应。
  4. 分层式硬实时情感闭环
    系统构建“感知-决策-执行-反馈”的硬实时闭环:高频层(中断)运行BLDC FOC电流环(10-20kHz),确保拟人化动作的平滑性;中频层(任务)进行传感器数据采集与轻量化情绪推理(100-500Hz);低频层(主循环)处理状态机更新与叙事逻辑(50-100Hz)。这种分层架构确保了在复杂交互中,底层运动控制不被上层逻辑阻塞。

应用场景

  1. 博物馆与科技展馆智能导览
    在博物馆或科技馆中,机器人通过人脸识别判断参观者的兴趣程度与情绪状态。当检测到参观者驻足凝视某展品且表情专注时,机器人主动靠近并启动讲解;当检测到参观者面露困惑时,机器人可放慢语速、配合头部微倾等肢体语言进行补充说明,提升导览体验的互动性与亲和力。
  2. 儿童孤独症干预与陪伴
    作为辅助治疗工具,为孤独症谱系障碍(ASD)儿童提供可预测、非压迫性的社交互动。机器人通过表情识别判断儿童是否焦虑,通过缓慢、重复的BLDC驱动摆动动作(如左右摇晃)提供安抚;通过识别儿童的笑脸,触发奖励性的灯光和音乐,强化正向社交行为。
  3. 老年人情感陪护与认知训练
    在养老机构或居家场景中,机器人通过语音交互识别老人的孤独感,主动靠近(BLDC驱动底盘平滑移动)并播放老人喜爱的音乐;通过表情识别监测老人是否长时间情绪低落,自动通知监护人。同时,机器人可作为认知训练辅助工具,通过互动游戏帮助老人维持认知能力。
  4. 商业综合体迎宾与导购
    在商场、酒店大堂等场所,机器人通过视觉与听觉感知主动迎接顾客,根据顾客的面部表情与语音语调判断其需求紧迫性。对于表情轻松的顾客,机器人以活泼的姿态引导;对于表情焦急的顾客,机器人则快速、直接地提供路径指引,提升服务效率与顾客满意度。

注意事项

  1. 硬件选型与算力瓶颈(首要挑战)
    AVR架构(Arduino Uno)完全不可行。情绪识别需要运行轻量级神经网络(如MobileNetV1 8-bit量化版),强烈推荐ESP32-S3(双核,支持向量指令)或STM32H7系列(带Cache和AI加速器)。若需运行更复杂的模型,可采用“上位机(树莓派/Jetson)推理+Arduino底层BLDC控制”的分布式架构。
  2. 数据隐私与合规性
    由于系统涉及持续的面部图像采集与语音监听,在面向儿童或老人的应用中,必须严格遵守数据隐私法规。建议采用纯边缘端推理,不将原始音视频数据上传云端,并在硬件或软件层面提供明确的隐私指示灯与物理断电开关。
  3. BLDC驱动的EMC与信号隔离
    BLDC电机的大电流PWM驱动信号极易干扰摄像头、麦克风等敏感传感器的信号。在硬件设计上,必须将电机动力线与传感器信号线严格分开布线,并做好屏蔽处理;建议采用隔离电源(如DC-DC模块)为控制电路供电,防止电机启停时的电压波动导致主控板复位或传感器数据失真。
  4. 情绪识别的鲁棒性与误判处理
    在实际环境中,光线变化、背景噪声、多人同时说话等因素都会影响情绪识别的准确率。必须在软件中加入多帧投票机制或置信度阈值过滤,避免因单次误判触发不恰当的情绪反馈。同时,应设计“降级策略”——当识别置信度低于阈值时,机器人回归中性状态,避免做出令人困惑的行为。
  5. 拟人化动作的自然度调优
    BLDC的FOC控制虽然能实现柔顺运动,但“情绪映射”的参数整定需要大量实验。例如,“悲伤”状态下的头部下垂速度、“快乐”状态下的身体晃动幅度,都需要通过真人动作捕捉数据或心理学研究进行标定,避免动作过于夸张或过于僵硬,破坏陪伴的真实感。

在这里插入图片描述
1、触摸意图识别与情绪评分
此案例聚焦于主动交互感知。通过多个触摸传感器检测用户交互的位置和时长,将“轻拍头部”“抚摸手臂”等行为映射为不同的情绪评分,作为后续多模态融合的最高权重输入。

#include <SimpleFOC.h>

// ===== 触摸传感器配置 =====
#define TOUCH_HEAD A0
#define TOUCH_LEFT A1
#define TOUCH_RIGHT A2

// 三路触摸状态
int touchState[3] = {0, 0, 0};
float touchScore = 0.5;  // 默认中性

// 触摸检测
int detectTouch() {
    touchState[0] = analogRead(TOUCH_HEAD) > 500 ? 1 : 0;
    touchState[1] = analogRead(TOUCH_LEFT) > 500 ? 1 : 0;
    touchState[2] = analogRead(TOUCH_RIGHT) > 500 ? 1 : 0;
    
    for (int i = 0; i < 3; i++) {
        if (touchState[i] == 1) return i;
    }
    return -1;  // 无触摸
}

// ===== 触摸意图评分(触摸优先级最高)=====
float evaluateTouchIntent(int touchIndex, int touchDuration) {
    if (touchIndex == 0) {  // 头部触摸
        if (touchDuration > 3000) return 0.8f;  // 长时间→亲近
        else return 0.6f;   // 短时→友好
    } else if (touchIndex == 1) {  // 左侧身体触摸
        return 0.7f;  // 寻求帮助
    } else if (touchIndex == 2) {  // 右侧身体触摸
        return 0.3f;  // 可能的拒绝或低落
    }
    return 0.5f;
}

void setup() {
    Serial.begin(115200);
    Serial.println("触摸意图识别系统就绪");
}

void loop() {
    static unsigned long touchStart = 0;
    int touchIndex = detectTouch();
    int touchDuration = 0;
    
    // 记录触摸时长
    if (touchIndex != -1 && touchStart == 0) {
        touchStart = millis();
    }
    
    if (touchIndex != -1) {
        touchDuration = millis() - touchStart;
        // 持续触摸时更新评分
        touchScore = evaluateTouchIntent(touchIndex, touchDuration);
    } else {
        if (touchStart != 0) {
            // 触摸结束,保持评分一小段时间后衰减
            touchStart = 0;
        }
        // 缓慢衰减回中性
        touchScore = touchScore * 0.95f + 0.5f * 0.05f;
    }
    
    Serial.print("触摸: "); Serial.print(touchIndex);
    Serial.print(" | 时长: "); Serial.print(touchDuration);
    Serial.print(" | 评分: "); Serial.println(touchScore);
    
    delay(50);
}

核心逻辑:触摸传感器检测用户交互意图(轻拍、长时间抚摸等)并赋予最高权重。头部触摸通常代表亲近意图,左侧身体触摸可能表示寻求帮助,右侧身体触摸则可能表示拒绝或低落。触摸时长也被纳入评分——长时间抚摸比短时轻拍传递更强烈的情感意图。

2、多模态情绪融合决策
此案例实现触摸+表情+语音的三模态加权融合。触摸评分作为主动交互信号获得最高权重,表情和语音作为辅助模态。融合后的情绪评分映射为“开心/中性/不开心/愤怒”四种情绪状态,为BLDC动作执行提供决策依据。

// ===== 多模态融合权重 =====
float faceScore = 0.5;   // 表情情绪分(来自视觉模块)
float voiceScore = 0.5;  // 语音情绪分(来自语音模块)
float touchScore = 0.5;  // 触摸情绪分(来自案例一)

String currentEmotion = "neutral";

// ===== 三模态加权融合 =====
String multiModalFusion() {
    // 自适应权重:触摸优先(主动交互权重0.4),其次表情(0.3),最后语音(0.3)
    float fusedScore = (touchScore * 0.4f) + (faceScore * 0.3f) + (voiceScore * 0.3f);
    
    if (fusedScore >= 0.7f) return "happy";
    else if (fusedScore >= 0.5f) return "neutral";
    else if (fusedScore >= 0.3f) return "unhappy";
    else return "angry";
}

// ===== 模拟表情和语音输入(实际项目需接入视觉/语音模块)=====
void updateFaceScore() {
    // 实际项目中:通过摄像头捕捉面部表情,计算情绪分
    // 简化:随机波动模拟
    faceScore = 0.5f + (random(-20, 20) / 100.0f);
    faceScore = constrain(faceScore, 0, 1);
}

void updateVoiceScore() {
    // 实际项目中:通过麦克风提取语速、语调等声学特征
    // 简化:基于音量模拟
    voiceScore = 0.5f + (random(-15, 15) / 100.0f);
    voiceScore = constrain(voiceScore, 0, 1);
}

void setup() {
    Serial.begin(115200);
    Serial.println("多模态情绪融合系统就绪");
}

void loop() {
    // 1. 更新各模态评分
    updateFaceScore();
    updateVoiceScore();
    // touchScore 由案例一提供
    
    // 2. 三模态融合
    currentEmotion = multiModalFusion();
    
    // 3. 输出决策结果
    Serial.print("触摸:"); Serial.print(touchScore);
    Serial.print(" 表情:"); Serial.print(faceScore);
    Serial.print(" 语音:"); Serial.print(voiceScore);
    Serial.print(" → 情绪:"); Serial.println(currentEmotion);
    
    // 4. 根据情绪驱动BLDC动作(案例三实现)
    // executeMultiJointAction(currentEmotion);
    
    delay(100);
}

核心逻辑:融合公式 fusedScore = touchScore × 0.4 + faceScore × 0.3 + voiceScore × 0.3 体现了“主动交互优先”的设计哲学。用户主动触摸机器人时,意图最明确、最强烈,因此权重最高。表情和语音作为被动感知信号,权重较低,但能在用户未触摸时提供情绪背景。

3、情绪驱动的BLDC多关节协同表达
此案例将情绪决策转化为BLDC多关节的协同动作。不同情绪对应不同的姿态序列:开心时点头+前倾+挥手,不开心时低头+微侧+轻拍安抚。BLDC配合FOC算法实现平滑、无顿挫的动作表达。

#include <SimpleFOC.h>

// ===== 多关节BLDC定义 =====
BLDCMotor neckJoint = BLDCMotor(7);    // 颈部
BLDCMotor torsoJoint = BLDCMotor(8);   // 躯干
BLDCMotor armJoint = BLDCMotor(9);     // 手臂

BLDCDriver3PWM neckDriver = BLDCDriver3PWM(3, 4, 5);
BLDCDriver3PWM torsoDriver = BLDCDriver3PWM(6, 7, 8);
BLDCDriver3PWM armDriver = BLDCDriver3PWM(9, 10, 11);

// 语音合成串口(可选)
// SoftwareSerial ttsSerial(12, 13);

// ===== 情绪驱动的多关节动作 =====
void executeMultiJointAction(String emotion) {
    if (emotion == "happy") {
        // 开心:颈部点头15° + 躯干前倾10° + 手臂抬起挥动
        neckJoint.moveTo(15);
        torsoJoint.moveTo(10);
        armJoint.moveTo(150);
        // ttsSerial.println("太棒啦!我们一起做游戏吧~");
        Serial.println("执行动作:开心 - 点头+前倾+挥手");
        
    } else if (emotion == "neutral") {
        // 中性:恢复自然姿态
        neckJoint.moveTo(0);
        torsoJoint.moveTo(0);
        armJoint.moveTo(90);
        Serial.println("执行动作:中性 - 自然姿态");
        
    } else if (emotion == "unhappy") {
        // 不开心:颈部低头10° + 躯干微侧 + 手臂轻拍安抚
        neckJoint.moveTo(-10);
        torsoJoint.moveTo(-5);
        // 手臂轻拍3次
        for (int i = 0; i < 3; i++) {
            armJoint.moveTo(45);
            delay(200);
            armJoint.moveTo(60);
            delay(200);
        }
        Serial.println("执行动作:不开心 - 低头+轻拍安抚");
        
    } else if (emotion == "angry") {
        // 愤怒:颈部摇头 + 躯干后仰 + 手臂放下
        for (int i = 0; i < 4; i++) {
            neckJoint.moveTo(10);
            delay(150);
            neckJoint.moveTo(-10);
            delay(150);
        }
        torsoJoint.moveTo(-15);
        armJoint.moveTo(30);
        Serial.println("执行动作:愤怒 - 摇头+后仰");
    }
}

void setup() {
    Serial.begin(115200);
    
    // 初始化BLDC关节(位置控制模式)
    neckJoint.linkDriver(&neckDriver);
    torsoJoint.linkDriver(&torsoDriver);
    armJoint.linkDriver(&armDriver);
    
    neckJoint.init(); torsoJoint.init(); armJoint.init();
    neckJoint.controller = MotionControlType::angle;
    torsoJoint.controller = MotionControlType::angle;
    armJoint.controller = MotionControlType::angle;
    
    Serial.println("情绪表达系统就绪");
}

void loop() {
    // 模拟从案例二获取的情绪决策
    // 实际项目中:String emotion = multiModalFusion();
    String emotion = "happy";  // 测试用
    
    // 执行情绪动作
    executeMultiJointAction(emotion);
    
    // FOC更新(需在循环中持续调用)
    neckJoint.loopFOC();
    torsoJoint.loopFOC();
    armJoint.loopFOC();
    
    delay(2000);  // 动作间隔
}

核心逻辑:不同情绪对应不同的关节姿态序列。BLDC配合FOC算法使动作极其平滑——开心时的点头是缓慢渐进的,不开心时的轻拍是柔和有节奏的,愤怒时的摇头是快速但仍有控制的。这种动作的“质感”直接决定了情感表达的真实感,而BLDC的低转矩脉动特性是实现这一点的硬件基础。

要点解读

  1. 触摸交互是“主动情绪响应”的核心触发信号

在导览陪伴场景中,用户主动触摸机器人是最强烈、最明确的交互意图。触摸位置和时长携带丰富的情感信息:头部长时间抚摸表示亲近,左侧轻拍表示寻求帮助,右侧触摸则可能表示拒绝。触摸评分应作为多模态融合中的最高权重输入,因为它是用户“主动发起”的,而非机器人被动感知的。

  1. 多模态加权融合的关键是“自适应权重”而非固定权重

触摸、表情、语音三种模态的可靠性不同。触摸在用户主动交互时最可靠,但在无人接触时无信号;表情依赖光照和摄像头质量;语音受环境噪声影响。融合策略应采用“动态权重”——当触摸信号存在时提高其权重,触摸信号消失时回退到表情和语音的融合。

  1. BLDC+FOC是实现“有温度”情绪表达的执行基础

情绪表达的本质是“动作质感”。步进电机的顿挫感会让机器人的“点头”像机械臂抓取,而有刷电机的噪声会让“安抚轻拍”变得刺耳。BLDC配合FOC算法提供的平滑转矩输出和低噪音特性,使机器人的动作具有“生物感”——开心时的点头是柔和的,愤怒时的摇头是有力度但仍有控制的。

  1. 情绪决策与动作执行需要解耦,便于独立调试

案例二(情绪融合)和案例三(动作执行)应保持接口清晰:融合模块输出标准化的情绪字符串(“happy”/“neutral”/“unhappy”/“angry”),动作模块根据字符串调用预定义的动作序列。这种解耦使调试时可以独立验证融合逻辑(串口打印情绪)和动作效果(手动传入情绪字符串),降低系统复杂度。

  1. 主动情绪响应需要“防误触”和“情绪惯性”机制

触摸传感器容易因振动或电磁干扰产生误触发。工程上需要设置“持续检测阈值”——连续多个控制周期检测到触摸信号才确认有效。同时,情绪状态应有“惯性”——从开心切换到愤怒不应是瞬时跳变,而应通过中间状态平滑过渡,否则机器人的动作会显得突兀、不自然。

在这里插入图片描述
4、导览路径识别+基础情绪反馈程序(核心:自主导览+基础互动)
该案例聚焦导览场景的核心需求——自主跟踪预订路径并输出对应情绪,是环境感知与情绪响应的基础实现,适用于固定路线的展厅、园区导览场景。

核心逻辑
环境感知:红外循迹模块识别地面黑白路径,超声波检测前方障碍物;
运动控制:基于路径偏移量调整双轮速度,实现稳定循迹;
情绪响应:正常循迹时输出“愉悦”灯光(暖黄色呼吸灯),避障时输出“警示”灯光(红色闪烁),提升导览的亲切感。

// 导览路径识别+基础情绪反馈程序
#include <DallasTemperature.h> // 可选,温度检测扩展用

// ===== 硬件引脚定义 =====
// 循迹模块引脚
#define LEFT_TRACKING_PIN 2  // 左侧红外循迹
#define RIGHT_TRACKING_PIN 3 // 右侧红外循迹

// 电机控制引脚
#define LEFT_MOTOR_PWM 9
#define LEFT_MOTOR_DIR 10
#define RIGHT_MOTOR_PWM 11
#define RIGHT_MOTOR_DIR 12// 超声波引脚
#define SONAR_TRIG 18
#define SONAR_ECHO 19

// 情绪输出引脚
#define EMOTION_LED_PIN 8    // 情绪灯带(WS2812需接数字引脚)
#define BUZZER_PIN 7

// ===== 核心参数 =====
const int TRACKING_ERROR_THRESHOLD = 100; // 路径偏移阈值,用于判断偏离
const int COLLISION_DISTANCE = 20;        // 避障距离阈值(cm)
const int NORMAL_SPEED = 70;              // 正常循迹速度(PWM值0~255)
const int TURN_SPEED = 40;                // 转向速度
// ===== 状态变量 =====
bool trackingState = false;   // 循迹状态:true=正常循迹 false=偏离
bool collision = false;       // 障碍物状态bool emotionState = 0;        // 情绪状态:0=愉悦 1=警示
long lastEmotionUpdate = 0;   // 情绪更新计时
const long EMOTION_INTERVAL = 500; // 情绪灯更新间隔(ms)

// ===== 函数声明 =====
void readTracking();
bool checkCollision();
void controlMotor();
void updateEmotion();
void setEmotionLed(uint8_t r, uint8_t g, uint8_t b);

void setup() {
  Serial.begin(115200);
  
  // 循迹引脚初始化
  pinMode(LEFT_TRACKING_PIN, INPUT);
  pinMode(RIGHT_TRACKING_PIN, INPUT);
  
  // 电机引脚初始化
  pinMode(LEFT_MOTOR_PWM, OUTPUT);
  pinMode(LEFT_MOTOR_DIR, OUTPUT);
  pinMode(RIGHT_MOTOR_PWM, OUTPUT);
  pinMode(RIGHT_MOTOR_DIR, OUTPUT);
  
  // 超声波引脚初始化
  pinMode(SONAR_TRIG, OUTPUT);
  pinMode(SONAR_ECHO, INPUT);
  
  // 情绪外设初始化
  pinMode(EMOTION_LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  
  // 初始化情绪灯
  setEmotionLed(255, 170, 0); // 初始暖黄色
  Serial.println("导览机器人初始化完成");
}

void loop() {
  unsigned long currentTime = millis();
  
  // 1. 环境感知:读取循迹与障碍物状态
  readTracking();bool collision = checkCollision();
  
  // 2. 运动控制:跟随路径+避障
  controlMotor();
  
  // 3. 情绪响应:定时更新情绪表达
  if (currentTime - lastEmotionUpdate > EMOTION_INTERVAL) {
    updateEmotion(trackingState, collision);
    lastEmotionUpdate = currentTime;
  }
  
  // 4. 调试输出
  Serial.print("循迹状态:"); Serial.print(trackingState?"正常":"偏离");
  Serial.print(" 避障状态:"); Serial.print(collision?"有障碍":"正常");
  Serial.print(" 情绪:"); Serial.println(emotionState?"警示":"愉悦");
  
  delay(50);
}

// 读取循迹状态并判断是否偏离
void readTracking() {
  bool leftTrack = digitalRead(LEFT_TRACKING_PIN) == LOW; // 红外检测到黑线返回LOW
  bool rightTrack = digitalRead(RIGHT_TRACKING_PIN) == LOW;
  
  // 双路均检测到黑线:沿路径直行
  if (leftTrack && rightTrack) {
    trackingState = true;
  } 
  // 仅单路检测到黑线:偏离路径,需调整
  else if (leftTrack && !rightTrack) {
    trackingState = false;
  } else if (!leftTrack && rightTrack) {
    trackingState = false;
  } 
  // 均未检测到:路径丢失,停车预警
  else {
    trackingState = false;
  }
}

// 检测前方障碍物
bool checkCollision() {
  digitalWrite(SONAR_TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(SONAR_TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(SONAR_TRIG, LOW);
  
  long duration = pulseIn(SONAR_ECHO, HIGH);
  int distance = (duration * 0.0343) / 2; // 距离cm
  
  return distance < COLLISION_DISTANCE;
}

// 电机控制:根据循迹与避障状态调整
void controlMotor() {
  if (collision) {
    // 有障碍:停车
    analogWrite(LEFT_MOTOR_PWM, 0);
    analogWrite(RIGHT_MOTOR_PWM, 0);
    return;
  }
  
  bool leftTrack = digitalRead(LEFT_TRACKING_PIN) == LOW;
  bool rightTrack = digitalRead(RIGHT_TRACKING_PIN) == LOW;
  
  if (leftTrack && rightTrack) {
    // 沿路径直行:双轮同速
    analogWrite(LEFT_MOTOR_PWM, NORMAL_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, NORMAL_SPEED);
  } else if (leftTrack && !rightTrack) {
    // 偏右:左转修正
    analogWrite(LEFT_MOTOR_PWM, TURN_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, NORMAL_SPEED);
  } else if (!leftTrack && rightTrack) {
    // 偏左:右转修正
    analogWrite(LEFT_MOTOR_PWM, NORMAL_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, TURN_SPEED);
  } else {
    // 路径丢失:缓慢前进寻找路径
    analogWrite(LEFT_MOTOR_PWM, TURN_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, TURN_SPEED);
  }
}

// 情绪响应:根据状态切换情情绪表达
void updateEmotion(bool tracking, bool coll) {
  if (coll) {
    emotionState = 1;
    // 警示:红色闪烁+蜂鸣器短响
    setEmotionLed(255, 0, 0);
    digitalWrite(BUZZER_PIN, HIGH);
    delay(50);
    digitalWrite(BUZZER_PIN, LOW);
  } else if (!tracking) {
    emotionState = 1;
    // 路径丢失:橙色呼吸(轻微警示)
    setEmotionLed(255, 130, 0);
    digitalWrite(BUZZER_PIN, LOW);
  } else {
    emotionState = 0;
    // 正常循迹:暖黄色呼吸(愉悦)
    setEmotionLed(255, 170, 0);
    digitalWrite(BUZZER_PIN, LOW);
  }
}

// WS2812灯颜色设置(需兼容Adafruit_NeoPixel库,此处为简化写法)
// 实际应用可引入Adafruit_NeoPixel库实现精准色彩控制
void setEmotionLed(uint8_t r, uint8_t g, uint8_t b) {
  // 简化:通过模拟PWM输出基础颜色,完整效果需搭配灯带驱动
  analogWrite(EMOTION_LED_PIN, r); 
}

程序优化方向
引入Adafruit_NeoPixel库,实现WS2812灯带的丰富灯效(如呼吸、渐变、闪烁),让情绪表达更生动;
加入编码器测速,实现更精准的循迹速度控制,避免因速度波动导致的情绪状态异常;
支持多路径切换,根据不同导览路线切换对应的情绪主题色,适配多场景导览。

5、人员互动感知+主动情绪响应程序(核心:人机互动+情绪识别)
该案例聚焦导览中的陪伴属性,通过感知人员动作与互动请求,主动输出对应的情绪反馈,适用于展会、病房、养老等需要人机互动的导览陪伴场景。

核心逻辑
环境感知:超声波检测人员距离,红外手势模块识别“跟随/停止”请求;
互动响应:根据人员的互动指令调整机器人动作(跟随/停留/靠近);
情绪响应:针对“跟随”输出绿色在指示灯(热情),“停止”输出蓝色指示灯(耐心),无互动时输出紫色指示灯(待命),若人员靠近则主动靠近并柔声提示。

// 人员互动感知+主动情绪响应程序—陪伴导览场景
#include <Adafruit_NeoPixel.h> // WS2812灯带驱动

// ===== 硬件引脚定义 =====
// 距离与手势感知
#define SONAR_TRIG 18
#define SONAR_ECHO 19
#define GESTURE_PIN 4 // 红外手势传感器(简化:检测挥手/停止动作)

// 电机控制
#define LEFT_MOTOR_PWM 9
#define LEFT_MOTOR_DIR 10
#define RIGHT_MOTOR_PWM 11
#define RIGHT_MOTOR_DIR 12

// 情绪外设
#define EMOTION_LED_PIN 6
#define SERVO_PIN 5     // 舵机(点头互动)
#define BUZZER_PIN 7    #define SPEAKER_PIN A0  // 语音提示输出引脚(可外接功放)

// Adafruit_NeoPixel初始化
#define LED_COUNT 8
Adafruit_NeoPixel strip(LED_COUNT, EMOTION_LED_PIN, NEO_GRB + NEO_KHZ800);

// ===== 核心参数 =====
const int FOLLOW_DISTANCE = 80;    // 跟随距离(cm)
const int NEAR_DISTANCE = 30;     // 靠近提示距离(cm)
const int MOVE_SPEED = 40;        // 跟随移动速度
const int IDLE_SPEED = 0;         // 待机速度

// ===== 状态变量 =====
int personDistance = 0;       // 人员距离bool gestureCommand;          // 手势指令:true=跟随 false=暂停跟随
bool following = false;        // 是否处于跟随状态
int emotionType;              // 情绪类型:0=待命 1=跟随 2=暂停 3=靠近提示
long lastGestureUpdate = 0;   // 手势检测计时
const long GESTURE_INTERVAL = 200;

// ===== 函数声明 =====
int measureDistance();
bool readGesture();
void controlRobot();
void updateEmotion();
void setEmotionEffect(uint32_t color, bool blink);

void setup() {
  Serial.begin(115200);
  
  // 传感器初始化
  pinMode(SONAR_TRIG, OUTPUT);
  pinMode(SONAR_ECHO, INPUT);
  pinMode(GESTURE_PIN, INPUT);
  
  // 电机初始化
  pinMode(LEFT_MOTOR_PWM, OUTPUT);
  pinMode(LEFT_MOTOR_DIR, OUTPUT);
  pinMode(RIGHT_MOTOR_PWM, OUTPUT);
  pinMode(RIGHT_MOTOR_DIR, OUTPUT);
  
  // 情绪外设初始化
  strip.begin();
  strip.show();
  pinMode(SERVO_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(SPEAKER_PIN, OUTPUT);
  
  Serial.println("互动陪伴机器人初始化完成");
}

void loop() {
  unsigned long currentTime = millis();
  
  // 1. 环境感知:获取人员距离与手势指令
  personDistance = measureDistance();
  
  if (currentTime - lastGestureUpdate > GESTURE_INTERVAL) {
    gestureCommand = readGesture();
    following = gestureCommand || (personDistance < FOLLOW_DISTANCE && following);
    lastGestureUpdate = currentTime;
  }
  
  // 2. 动作控制:跟随/停留/靠近
  controlRobot();
  
  // 3. 情绪响应:根据状态输出情绪
  updateEmotion();
  
  // 4. 调试输出
  Serial.print("人员距离:"); Serial.print(personDistance);
  Serial.print("cm 手势:"); Serial.print(gestureCommand? "跟随":"暂停");
  Serial.print(" 情绪:"); Serial.println(emotionType);
  
  delay(50);
}

// 距离测量
int measureDistance() {
  digitalWrite(SONAR_TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(SONAR_TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(SONAR_TRIG, LOW);
  
  long duration = pulseIn(SONAR_ECHO, HIGH);
  return (duration * 0.0343) / 2;
}

// 读取手势指令(简化:检测引脚电平,实际搭配专用手势传感器更精准)
bool readGesture() {
  // 简化逻辑:引脚高电平=挥手跟随,低电平=停止
  return digitalRead(GESTURE_PIN) == HIGH;
}

// 机器人动作控制
void controlRobot() {
  if (!following) {
    // 非跟随状态:停车待命
    analogWrite(LEFT_MOTOR_PWM, IDLE_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, IDLE_SPEED);
    return;
  }
  
  // 距离过近:减速停止,避免碰撞
  if (personDistance < NEAR_DISTANCE) {
    analogWrite(LEFT_MOTOR_PWM, 0);
    analogWrite(RIGHT_MOTOR_PWM, 0);
  } else if (personDistance > FOLLOW_DISTANCE +20) {
    // 距离过远:加速靠近
    analogWrite(LEFT_MOTOR_PWM, MOVE_SPEED);
    analogWrite(RIGHT_MOTOR_PWM, MOVE_SPEED);
  } else {
    // 距离合适:匀速跟随
    analogWrite(LEFT_MOTOR_PWM, MOVE_SPEED - 5);
    analogWrite(RIGHT_MOTOR_PWM, MOVE_SPEED - 5);
  }
}

// 情绪响应更新
void updateEmotion() {
  if (personDistance < NEAR_DISTANCE) {
    emotionType = 3;
    // 靠近提示:绿色呼吸+舵机点头+柔和提示音
    setEmotionEffect(0X00FF00, false);
    analogWrite(SERVO_PIN, 1500); // 舵机点头角度
    playTone(880, 100); // 柔和提示音
  } else if (following) {
    emotionType = 1;
    // 跟随状态:绿色跑马灯(热情)
    setEmotionEffect(0X00FF00, false);
    analogWrite(SERVO_PIN, 1000); // 舵机复位
    digitalWrite(BUZZER_PIN, LOW);
  } else if (gestureCommand == false && following) {
    emotionType = 2;
    // 暂停跟随:蓝色呼吸灯(耐心)
    setEmotionEffect(0X0000FF, false);
    analogWrite(SERVO_PIN, 1500); // 舵机点头回应
    digitalWrite(BUZZER_PIN, LOW);
  } else {
    emotionType = 0;
    // 待命状态:紫色呼吸(安静)
    setEmotionEffect(0X8800FF, false);
    analogWrite(SERVO_PIN, 900); // 舵机复位
    digitalWrite(BUZZER_PIN, LOW);
  }
}

// 情绪灯效设置(基于NeoPixel库)
void setEmotionEffect(uint32_t color, bool blink) {
  if (blink) {
    // 闪烁效果:间隔亮灯
    if (millis() % 200 < 100) {
      for (int i = 0; i < LED_COUNT; i++) {
        strip.setPixelColor(i, color);
      }
      strip.show();
    } else {
      strip.show();
      strip.clear();
      strip.show();
    }
  } else {
    // 呼吸效果:全灯渐变
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, color);
    }
    strip.show();
  }
}

// 简易音调播放(需外接功放模块)
void playTone(int frequency, int duration) {
  tone(BUZZER_PIN, frequency, duration);
}

程序优化方向
引入手势识别传感器(如APDS9960),实现更精准的挥手、指向等互动指令识别;
加入语音识别模块,支持语音指令(如“请跟我来”“停下休息”),强化陪伴属性;
基于人员位置实现自主避让,当人员挡住路径时,机器人主动绕行并输出“抱歉”的情绪表达,提升交互友好度。

6、导览异常应对+情绪安抚程序(核心:异常感知+情绪安抚)
该案例聚焦导览过程中的突发异常场景,当检测到环境危险、设备异常等情况时,主动输出安抚或警示情绪,适用于医院、实验室、危险品存储区等对安全与情绪感知要求高的导览场景。

核心逻辑
异常感知:检测环境异常(温度过高、烟雾浓度超标)、设备异常(电机电流过大、路径长时间丢失);
分级应对:轻微异常时安抚情绪(黄色温和灯),严重异常时警示(红色闪烁+蜂鸣器);
情绪安抚:异常时输出“稳定”的情绪表达(如黄色呼吸灯、舵机缓慢摆动),缓解访客紧张感,同时明确警示风险。

// 导览异常应对+情绪安抚程序—安全导览场景#include <Adafruit_NeoPixel.h>
#include <DallasTemperature.h>

// ===== 硬件引脚定义 =====
// 异常感知
#define SMOKE_PIN A0     // 烟雾传感器(MQ-2,模拟量输出)
#define TEMP_PIN 2       // DS18B20温度传感器
#define MOTOR_CURRENT_PIN A1 // 电机电流采样

// 电机与跟随控制
#define LEFT_MOTOR_PWM 9
#define LEFT_MOTOR_DIR 10
#define RIGHT_MOTOR_PWM 11
#define RIGHT_MOTOR_DIR 12

// 情绪外设
#define EMOTION_LED_PIN 6#define SERVO_PIN 5
#define BUZZER_PIN 7

// 温度传感器初始化
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);#define LED_COUNT 8
Adafruit_NeoPixel strip(LED_COUNT, EMOTION_LED_PIN, NEO_GRB + NEO_KHZ800);

// ===== 异常阈值 =====
const float SMOKE_DANGER_THRESHOLD = 300; // 烟雾浓度阈值(模拟量,需校准)
const float TEMP_DANGER_THRESHOLD = 45.0; // 环境温度阈值(℃)
const int MOTOR_CURRENT_THRESHOLD = 200;  // 电机电流阈值(ADC值)
const int PATH_LOST_TIME = 3000;           // 路径丢失超时阈值(ms)

// ===== 状态变量 =====
float smokeValue = 0;            // 烟雾浓度值
float tempValue = 0;             // 环境温度
int motorCurrent = 0;            // 电机电流
bool dangerState = false;        // 是否为危险状态(严重异常)
bool warningState = false;       // 是否为预警状态(轻微异常)
long pathLostStart = 0;          // 路径丢失计时
int emotionType;                 // 情绪类型:0=正常 1=轻微预警 2=危险警示

// ===== 函数声明 =====
void readEnvironment();
void checkAnomaly();
void emergencyControl();
void updateEmotion();
void setEmotionEffect(uint32_t color, bool blink, bool gradient);

void setup() {
  Serial.begin(115200);
  sensors.begin();
  strip.begin();
  
  // 传感器引脚初始化
  pinMode(SMOKE_PIN, INPUT);
  pinMode(MOTOR_CURRENT_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(SERVO_PIN, OUTPUT);
  
  // 电机初始化
  pinMode(LEFT_MOTOR_PWM, OUTPUT);
  pinMode(LEFT_MOTOR_DIR, OUTPUT);
  pinMode(RIGHT_MOTOR_PWM, OUTPUT);
  pinMode(RIGHT_MOTOR_DIR, OUTPUT);
  
  Serial.println("安全导览机器人初始化完成");
}

void loop() {
  // 1. 环境感知:读取所有异常相关数据
  readEnvironment();
  
  // 2. 异常检测:判断是否存在异常及等级
  checkAnomaly();
  
  // 3. 应急控制:异常时减速/停车
  emergencyControl();
  
  //4. 情绪响应:输出对应安抚/警示情绪
  updateEmotion();
  
  //5. 调试输出
  Serial.print("烟雾:"); Serial.print(smokeValue);
  Serial.print(" 温度:"); Serial.print(tempValue);
  Serial.print("℃ 电流:"); Serial.print(motorCurrent);
  Serial.print(" 状态:"); Serial.print(dangerState ? "危险" : (warningState ? "预警" : "正常"));
  Serial.println();
  
  delay(100);
}

// 读取环境传感器数据
void readEnvironment() {
  sensors.requestTemperatures();
  tempValue = sensors.getTempCByIndex(0);
  smokeValue = analogRead(SMOKE_PIN);
  motorCurrent = analogRead(MOTOR_CURRENT_PIN);
}

// 异常检测与分级
void checkAnomaly() {
  dangerState = false;
  warningState = false;
  
  // 严重异常判定:任意参数超危险阈值
  if (smokeValue > SMOKE_DANGER_THRESHOLD ||
      tempValue > TEMP_DANGER_THRESHOLD ||
      motorCurrent > MOTOR_CURRENT_THRESHOLD) {
    dangerState = true;
    return;
  }
  
  // 轻微异常判定:参数接近阈值
  if (smokeValue > SMOKE_DANGER_THRESHOLD * 0.7 ||
      tempValue > (TEMP_DANGER_THRESHOLD -5.0)) {
    warningState = true;
  }
}

// 应急运动控制
void emergencyControl() {
  if (dangerState) {
    // 严重异常:立即停车
    analogWrite(LEFT_MOTOR_PWM, 0);
    analogWrite(RIGHT_MOTOR_PWM, 0);
  } else if (warningState) {
    // 轻微异常:减速行驶
    analogWrite(LEFT_MOTOR_PWM, 30);
    analogWrite(RIGHT_MOTOR_PWM, 30);
  } else {
    // 正常行驶
    analogWrite(LEFT_MOTOR_PWM, 60);
    analogWrite(RIGHT_MOTOR_PWM, 60);
  }
}

// 情绪响应更新
void updateEmotion() {
  if (dangerState) {
    emotionType = 2;
    // 危险警示:红色闪烁+蜂鸣器长响+舵机剧烈摆动(警示但不刺耳,需调节)
    setEmotionEffect(0XFF0000, true, false);
    digitalWrite(BUZZER_PIN, HIGH);
    // 舵机缓慢摆动,避免过于突兀模拟安抚动作
    for (int angle = 900; angle <= 2100; angle += 100) {
      analogWrite(SERVO_PIN, angle);
      delay(30);
    }
    for (int angle = 2100; angle >= 900; angle -= 100) {
      analogWrite(SERVO_PIN, angle);
      delay(30);
    }
  } else if (warningState) {
    emotionType = 1;
    emotionType = 1;
    // 轻微预警:黄色呼吸灯(温和安抚)+蜂鸣器间歇响
    setEmotionEffect(0XFFAA00, false, false);
    if (millis() % 500 < 250) {
      digitalWrite(BUZZER_PIN, HIGH);
    } else {
      digitalWrite(BUZZER_PIN, LOW);
    }
    analogWrite(SERVO_PIN, 1200); // 舵机保持中间位置,稳定姿态
  } else {
    emotionType = 0;
    // 正常:绿色渐变灯(舒适)
    setEmotionEffect(0X00FF00, false, true);
    digitalWrite(BUZZER_PIN, LOW);
    analogWrite(SERVO_PIN, 1000);
  }
}

// 情绪灯效设置(支持闪烁、呼吸、渐变)
void setEmotionEffect(uint32_t color, bool blink, bool gradient) {
  if (blink) {
    // 闪烁效果
    if (millis() % 150 < 75) {
      for (int i = 0; i < LED_COUNT; i++) {
        strip.setPixelColor(i, color);
      }
    } else {
      strip.clear();
    }
  } else if (gradient) {
    // 呼吸效果:亮度渐变
    uint8_t brightness = (sin(millis() * 0.001) * 127) + 128;
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, color);
    }
    strip.setBrightness(brightness);
  } else {
    // 呼吸效果:恒亮度
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, color);
    }
    strip.setBrightness(200);
  }
  strip.show();
}

程序优化方向
加入多类异常检测(如湿度、光照),适配更多环境场景;
实现异常分级的情绪缓冲,当异常从预警转为危险时,情绪表达逐步过渡(如黄色→红色),避免情绪切换过于突兀,增强安抚感;
异常时自动上报异常信息至后台管理系统,实现远程监控,同时机器人的情绪表达与后台指令联动,适配远程导览管理需求。

要点解读
要点1:环境感知的多维度融合——情绪响应的前提
环境感知是情绪响应的触发源,需实现多维度数据融合,而非依赖单一传感器,需把握三点:
感知覆盖全面:同时覆盖路径、人员、环境、设备四类核心数据,既要识别导览路径,也要感知人员互动、环境异常与设备状态,避免情绪表达脱离场景;
阈值合理校准:所有感知阈值需结合现场环境校准(如烟雾阈值需适配场景的空气质量,距离阈值需匹配机器人移动速度),避免误触发导致情绪表达混乱;
采样同步协调:多传感器采样需保持一定同步性,避免因时间差导致的状态判断误差(如距离测量与手势识别间隔过大,导致跟随逻辑冲突)。

要点2:情绪表达的场景适配性——导览陪伴的核心
情绪表达需贴合导览陪伴的属性,既要考虑情绪的真实性,也要兼顾场景的实用性,关键把握:
情绪状态分级:将情绪按场景需求分为“愉悦/热情/耐心/警示/安抚”等等级,每个等级对应明确的外设输出(灯光颜色、灯效、声音、舵机动作),避免表达模糊;
情绪表达柔和化:导览陪伴场景以服务为核心,警示情绪避免过于刺眼(如红色闪烁频率不宜过高)、声音避免过于刺耳(蜂鸣器频率控制在舒适范围),安抚情绪侧重温和感(如暖色调、柔和声音);
情绪与动作联动:情绪输出需与机器人动作同步(如停车时同步输出警示情绪,跟随时同步输出热情情绪),让访客感知到情绪与行为的一致性。

要点3:情绪响应的实时性与稳定性——交互体验的保障
情绪响应需兼顾实时性与稳定性,避免延迟过高或频繁切换,关键要点:
响应延迟可控:环境感知、运动控制、情绪响应的循环周期需控制在100ms以内,确保异常或互动发生后,情绪能及时输出,不影响体验;
状态去抖处理:对感知数据进行滤波与去抖(如距离测量多次采样取平均、异常状态连续触发才判定),避免因瞬时干扰导致情绪频繁切换;
异常情绪优先:当异常发生时,情绪表达需优先于常规情绪(如警示情绪覆盖愉悦情绪),确保安全相关信息能第一时间传递给访客。

要点4:外设输出的硬件兼容性与效果可控性
情绪表达依赖灯带、舵机、蜂鸣器等外设,需匹配硬件特性并控制输出效果:
外设选型匹配:WS2812灯带需使用标准的数字引脚与驱动库,舵机需选择适合机器人负载的小扭矩舵机,蜂鸣器需搭配功放模块实现清晰的声音输出,避免硬件不兼容导致情绪表达失效;
灯效控制精准:基于NeoPixel等成熟库实现灯效控制,明确区分纯色、闪烁、呼吸、渐变等效果,每种效果对应固定的情绪状态,避免模糊表达;
能耗与续航兼顾:情绪外设的输出强度需适配机器人续航,如待机时采用低亮度的呼吸灯,异常警示时可适当提高强度,平衡情绪效果与续航能力。

要点5:场景扩展的灵活性与可维护性
程序需具备强扩展性,便于适配不同导览陪伴场景,同时降低后续维护成本:
模块化设计:将感知、控制、情绪响应拆分为独立函数,后续新增传感器或情绪类型时,只需修改对应模块,无需重构整体代码(如新增光照传感器只需添加读取函数与异常判定逻辑);
参数可调设计:所有阈值、速度、情绪效果等参数均集中定义,支持串口或参数配置文件动态调整,无需修改代码即可适配不同场景;
调试输出完善:保留串口调试信息,实时输出感知数据、状态与情绪类型,便于后续排查情绪响应异常或感知误差,提升维护效率。

请注意:以上案例仅作为思路拓展的参考示例,不保证完全正确、适配所有场景或可直接编译运行。由于硬件平台、实际使用场景、Arduino 版本的差异,均可能影响代码的适配性与使用方法的选择。在实际编程开发时,请务必根据自身硬件配置、使用场景及具体功能需求进行针对性调整,并通过多次实测验证效果;同时需确保硬件接线正确,充分了解所用传感器、执行器等设备的技术规范与核心特性。对于涉及硬件操作的代码,使用前务必核对引脚定义、电平参数等关键信息的准确性与安全性,避免因参数错误导致硬件损坏或运行异常。

在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐