【人机交互 6-10】第 6/25 课

🤖 第06课:语音识别与合成

📌 语音交互概述

语音是服务机器人最自然的交互方式。完整链路:唤醒→听→理解→回答→说

🔗 语音交互全链路

用户说话 → [VAD检测] → [唤醒词] → [ASR识别] → [NLU理解]
                                                        ↓
用户听到 ← [TTS合成] ← [对话管理] ← [意图响应] ← [技能调用]

📌 语音识别(ASR)

基于关键词匹配的命令识别——实际系统使用深度学习模型(Whisper等):

import re, json

class SimpleASR:
    def __init__(self):
        self.commands = {
            "navigate": ["去","导航到","带我去","走到","前往"],
            "greet": ["你好","嗨","早上好","下午好","晚上好"],
            "deliver": ["送","配送","递送","拿给","带给"],
            "query": ["查询","问一下","帮我查","告诉我","什么"],
            "stop": ["停","停止","暂停","取消","别动"],
            "follow": ["跟我来","跟着我","跟随","跟上"],
            "help": ["帮助","求助","救命","怎么用"],
        }
        self.locations = ["会议室","前台","电梯","洗手间","茶水间","出口","大厅","办公室","停车场","餐厅"]
        self.items = ["咖啡","水","文件","快递","餐食","药品","钥匙"]

    def recognize(self, text):
        result = {"raw": text, "intent": None, "slots": {}, "confidence": 0.0}
        best_intent, best_score = None, 0
        for intent, kws in self.commands.items():
            for kw in kws:
                if kw in text:
                    score = len(kw)/len(text) + 0.5
                    if score > best_score: best_score = score; best_intent = intent
        if best_intent:
            result["intent"] = best_intent
            result["confidence"] = min(best_score, 0.99)
        for loc in self.locations:
            if loc in text: result["slots"]["location"] = loc; break
        for item in self.items:
            if item in text: result["slots"]["item"] = item; break
        nums = re.findall(r'(\d+)楼', text)
        if nums: result["slots"]["floor"] = int(nums[0])
        nm = re.search(r'(?:给|找|送给)([\u4e00-\u9fa5]{2,3})', text)
        if nm: result["slots"]["person"] = nm.group(1)
        return result

asr = SimpleASR()
tests = [
    "你好,请带我去3楼会议室",
    "帮我送一杯咖啡给张总",
    "我想去洗手间怎么走",
    "停下来,不要动了",
    "查询一下前台在哪里",
    "跟我来,我带你去",
    "请导航到5楼办公室",
]
print("语音命令识别(ASR)模拟")
print("=" * 55)
for t in tests:
    r = asr.recognize(t)
    conf = f"{r['confidence']:.0%}" if r['confidence']>0 else "N/A"
    slots = ", ".join(f"{k}={v}" for k,v in r["slots"].items())
    print(f"\n🎤 \"{t}\"")
    print(f"   意图: {r['intent'] or '❓'} (置信度:{conf})")
    if slots: print(f"   槽位: {slots}")
print("\n✅ ASR验证通过")
✅ 验证通过 语音命令识别(ASR)模拟 ======================================================= 🎤 "你好,请带我去3楼会议室" 意图: navigate (置信度:75%) 槽位: location=会议室, floor=3 🎤 "帮我送一杯咖啡给张总" 意图: deliver (置信度:60%) 槽位: item=咖啡, person=张总 🎤 "我想去洗手间怎么走" 意图: navigate (置信度:61%) 槽位: location=洗手间 🎤 "停下来,不要动了" 意图: stop (置信度:62%) 🎤 "查询一下前台在哪里" 意图: query (置信度:72%) 槽位: location=前台 🎤 "跟我来,我带你去" 意图: follow (置信度:88%) 🎤 "请导航到5楼办公室" 意图: navigate (置信度:83%) 槽位: location=办公室, floor=5 ✅ ASR验证通过

📌 语音合成(TTS)

TTS将文本转化为自然语音,SSML标记控制韵律:

import random

class SimpleTTS:
    def __init__(self):
        self.templates = {
            "greeting": ["您好!我是{name},很高兴为您服务。","欢迎!我是{name},有什么可以帮您?"],
            "navigating": ["好的,正在为您导航到{location}。","收到,我将带您前往{location}。"],
            "delivering": ["{item}已送达,请确认。","您的{item}到了,请查收。"],
            "arrived": ["我们已到达{location}。","{location}到了。"],
            "elevator": ["正在等待电梯,预计{wait_time}秒。","电梯已到达,请小心进出。"],
            "error": ["抱歉,我没有理解,能再说一遍吗?","不好意思,能换一种说法吗?"],
        }

    def synthesize(self, scene, **kwargs):
        templates = self.templates.get(scene, self.templates["error"])
        text = random.choice(templates).format(**kwargs)
        pauses = {"。":500,"!":600,"?":500,",":300}
        ssml = text
        for p, ms in pauses.items():
            ssml = ssml.replace(p, f'{p}<break time="{ms}ms"/>')
        duration = len(text) * 0.12
        return {"text": text, "ssml": ssml, "duration": round(duration,1)}

tts = SimpleTTS()
scenes = [
    ("greeting", {"name":"小云"}),
    ("navigating", {"location":"3楼会议室A"}),
    ("delivering", {"item":"咖啡"}),
    ("arrived", {"location":"会议室B"}),
    ("elevator", {"wait_time":"30"}),
    ("error", {}),
]
print("语音合成(TTS)模拟")
print("=" * 55)
for scene, kw in scenes:
    r = tts.synthesize(scene, **kw)
    print(f"\n🔊 {scene}: {r['text']} ({r['duration']}s)")
    print(f"   SSML: {r['ssml'][:80]}...")
print("\n✅ TTS验证通过")
✅ 验证通过 语音合成(TTS)模拟 ======================================================= 🔊 greeting: 您好!我是小云,很高兴为您服务。 (1.9s) SSML: 您好!<break time="600ms"/>我是小云,<break time="300ms"/>很高兴为您服务。<break time="500ms"/>... 🔊 navigating: 收到,我将带您前往3楼会议室A。 (1.9s) SSML: 收到,<break time="300ms"/>我将带您前往3楼会议室A。<break time="500ms"/>... 🔊 delivering: 您的咖啡到了,请查收。 (1.3s) SSML: 您的咖啡到了,<break time="300ms"/>请查收。<break time="500ms"/>... 🔊 arrived: 我们已到达会议室B。 (1.2s) SSML: 我们已到达会议室B。<break time="500ms"/>... 🔊 elevator: 正在等待电梯,预计30秒。 (1.6s) SSML: 正在等待电梯,<break time="300ms"/>预计30秒。<break time="500ms"/>... 🔊 error: 抱歉,我没有理解,能再说一遍吗? (1.9s) SSML: 抱歉,<break time="300ms"/>我没有理解,<break time="300ms"/>能再说一遍吗?<break time="500ms"/>... ✅ TTS验证通过

📌 VAD与唤醒词

VAD判断用户是否在说话,唤醒词区分是否在跟机器人说话:

import math, random

class VoiceActivityDetector:
    def __init__(self, threshold=0.3, frame_size=0.02):
        self.threshold = threshold; self.frame_size = frame_size
        self.min_speech = int(0.3/frame_size); self.max_silence = int(0.5/frame_size)

    def detect(self, energies):
        segments = []; in_speech = False; start = 0; silence_count = 0; speech_count = 0
        for i, e in enumerate(energies):
            if e > self.threshold:
                speech_count += 1; silence_count = 0
                if not in_speech and speech_count >= self.min_speech:
                    in_speech = True; start = (i - self.min_speech) * self.frame_size
            else:
                if in_speech:
                    silence_count += 1
                    if silence_count >= self.max_silence:
                        end = (i - self.max_silence) * self.frame_size
                        segments.append({"start":round(start,2),"end":round(end,2),"duration":round(end-start,2)})
                        in_speech = False; speech_count = 0
                else: speech_count = 0
        if in_speech:
            end = len(energies) * self.frame_size
            segments.append({"start":round(start,2),"end":round(end,2),"duration":round(end-start,2)})
        return segments

random.seed(42)
vad = VoiceActivityDetector()
pattern = [0.05]*30 + [0.6]*50 + [0.8]*30 + [0.05]*40 + [0.7]*60 + [0.05]*30
segments = vad.detect(pattern)

print("VAD语音活动检测模拟")
print(f"总时长: {len(pattern)*0.02:.1f}秒")
print(f"检测到语音段: {len(segments)}")
for i, s in enumerate(segments):
    print(f"  段{i+1}: {s['start']}s-{s['end']}s (时长{s['duration']}s)")
print("\n✅ VAD验证通过")
✅ 验证通过 VAD语音活动检测模拟 总时长: 4.8秒 检测到语音段: 2 段1: 0.58s-2.18s (时长1.6s) 段2: 2.98s-4.18s (时长1.2s) ✅ VAD验证通过

📌 工程选型

🛠️ 选型参考

组件开源云服务建议
ASRWhisper、Vosk阿里/腾讯ASR离线Whisper,在线云
TTSVITS、Piper阿里/讯飞TTS情感表达用VITS
VADWebRTC VAD、Silero-Silero精度高
唤醒词Porcupine、Sherpa-Porcupine误唤醒低

📌 练习

📝 练习 1

实现中英文混合ASR:支持'帮我go to会议室'等混合语言识别。

📝 练习 2

为TTS添加情感韵律控制:根据上下文自动选择情绪。

📝 练习 3

模拟远场语音识别:加入噪声模拟,实现降噪后识别对比。

📌 成就

🏆 本课成就

◀ 上一课 📚 目录 下一课 ▶