回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

基于行空板 M10 扩展板的便携语音生活信息查询助手 简单

头像 豆爸 2025.06.27 84 0

1、项目概述

本项目基于行空板 M10 扩展板,开发了一款支持多城市的智能语音生活信息查询助手,旨在通过自然语音交互,为用户提供便捷的跨城市生活信息查询服务。

解决用户在出行、日常规划中快速获取多城市天气、热映电影等信息的需求,通过语音交互降低操作门槛,适配各年龄段用户(尤其适合不熟悉触屏操作的群体)。

2、功能演示

output.gif

3、硬件设计

材料清单

  • 行空板 M10 X1 链接
  • 行空板M10扩展板组合 X1 链接
  • 蓝牙音箱 X1

硬件说明

(1)行空板M10

行空板M10是一款专为Python学习和使用设计国产开源硬件,集成LCD彩屏、WiFi蓝牙、多种常用传感器和丰富的拓展接口,自带Linux操作系统、Python环境以及常用的Python库。

(2)行空板M10扩展板组合

行空板M10扩展板组合由行空板M10电机IO扩展板与行空板M10电池扩展板两块板子组成。二者采用乐高兼容结构及即插即用设计。

行空板M10电机IO扩展板功能图.png

行空板M10电机IO扩展板集成双路直流电机驱动、红外收发、RGB,还通过引出金手指扩展出行空板 M10 的剩余 I/O 接口。

行空板M10电池扩展板功能图.png

行空板M10电池扩展板配备 800mAh 电池。

(3)蓝牙音箱

featured.png

4、注册天气API

本项目采用免费实况+24小时天气接口,5分钟更新一次,包含基本天气信息、24小时逐小时天气、实时气象预警列表、湿度、能见度、气压、日出日落、空气质量、pm2.5、pm10、o3、no2、so2、是否需要带口罩、外出适宜、开窗适宜、是否需要打开净化器等,可按地名、城市编号、IP查询、经纬度查询。

(1)注册天气API,确保账号状态:已激活,没有过期。网址http://www.tianqiapi.com/

135715s11qqyx3q2810xbo.png

(2)记录鉴权信息:APPID、APISecret,后面程序中需要使用。

(3)请求方式:GET

(3)请求示例:http://gfeljm.tianqiapi.com/free/v2030?appid=your_appid&appsecret=your_appsecret

(4)请求参数说明:

1751345158115.png

(5)响应JSON

1751345660037.png

5、注册讯飞开放平台

试用语音识别,确保账号状态:token有余量,且在有效期内。网址https://console.xfyun.cn/app/myapp

1751275348020.png

记录鉴权信息:APPID、APISecret、APIKey,后面程序中需要使用。

6、流程图

deepseek_mermaid_20250628_c5b12e.png

7、开发准备

(1)下载安装Mind+ V1.8.1 RC3,选择Python模式。

https://download3.dfrobot.com.cn/MindPlus_Win_V1.8.1_RC3.0.exe

1751095666807.png

(2)添加行空板M10库、讯飞语音库、代码生成器库等。

image.png

(3)连接行空板M10

使用数据线连接电脑,连接远程终端——10.1.2.3,如下图所示。

Snipaste_2025-06-30_17-48-53.png

如下图所示,表示连接成功。

Snipaste_2025-06-30_17-51-07.png

(4)行空板M10启用蓝牙功能

在终端窗口依次执行以下命令,如下图所示:

bluetoothctl

default-agent

power on

Snipaste_2025-06-30_22-49-55.png

(5)扫描蓝牙设备

在“终端”窗口输入:scan on,搜索蓝牙设备,记录设备MAC地址,输入scan off结束扫描。

Snipaste_2025-06-30_22-52-27.png

(6)配对连接蓝牙设备

在终端依次输入以下命令,进行配对。

trust 7D:9F:9A:FE:28:D9

pair 7D:9F:9A:FE:28:D9

connect 7D:9F:9A:FE:28:D9

Snipaste_2025-06-30_23-02-54.png

出现:[CHG] Device 7D:9F:9A:FE:28:D9 Connected: yes
Connection successful

表示连接成功到MAC地址为7D:9F:9A:FE:28:D9的蓝牙设备。


输入exit退出。

8、系统UI及前端设计

1.启动与触发

  • 设备通电后自动启动,屏幕显示 “按下 A 键开始”。

001.png

按下 A 键后,屏幕提示 “聆听中...”,进入 5 秒录音状态。

002.png

2.天气查询场景

  • 语音输入示例:“查询西安天气”;识别反馈:屏幕显示识别文本 “查询西安天气”;

003.png

结果输出:屏幕显示西安天气图标、温度、湿度、PM2.5 等信息(黑色背景 + 白色文字),同时语音播报 “西安当前天气 XX,最高温度 XX 摄氏度...”

004.png

3.电影查询场景

  • 语音输入示例:“查询西安热映电影”;识别反馈:屏幕显示识别文本 “查询西安热映电影”;

007.png

结果输出:


① 先播报 “西安现有 55 部热映电影”,屏幕同步显示该文本;

008.png

依次展示每部电影海报,每部海报停留期间,语音播报 “第 1 部:《XX》,导演 XX,主演 XX,评分 XX 分...”。

第一部海报展示

009.png

第二部海报展示

010.png

第三部海报展示

011.png

9、程序的编写

(1)编写主程序,如下图

001.png

(2)编写天气子程序,如下图

002.png

(3)编写电影子程序,如下图

003.png

10、主要代码片段及说明

1. 初始化

此部分代码用于导入相关库,配置讯飞语音的相关参数(APP_ID、API_KEY、API_SECRET),并实例化语音识别(XfIat)、语音合成(XfTts)等对象,为后续的语音交互做好准备。

代码
#导入库
import sys
import time
import cpca
from unihiker import GUI
from player import PLAYER
from unihiker import Audio
from df_xfyun_speech import XfIat
from df_xfyun_speech import XfTts
from pypinyin import pinyin, Style
from pinpong.board import Board,Pin
from webimage import WebImageManager
from webimage import DoubanMovieScraper
from pinpong.extension.unihiker import *
from tianqiapi_hourlyweather import TianqiApiHourlyWeather

# 讯飞语音配置信息
APP_ID = "XXXX"
API_KEY = "XXXX"
API_SECRET = "xxxx"

# 实例化语音识别和合成对象
iat = XfIat(APP_ID, API_KEY, API_SECRET)  # 语音识别
tts = XfTts(APP_ID, API_KEY, API_SECRET, options)  # 语音合成

#实例化其他对象
u_audio = Audio()
player = PLAYER()
img_manager = WebImageManager(None, None)

2. 语音识别功能

首先通过麦克风录制 5 秒语音并保存为音频文件;然后调用讯飞语音识别接口将音频文件转换为文本。

代码
# 录制语音
u_audio.record("record.wav", 5)

# 语音识别(将语音转为文本)
recognition_result = iat.recognition("record.wav")
print(f"识别结果:{recognition_result}")

# 自然语言处理(关键词匹配判断指令)
if "电影" in recognition_result and "西安" in recognition_result:
    DianYing()  # 调用电影查询函数
elif "天气" in recognition_result and "西安" in recognition_result:
    TianQi()  # 调用天气查询函数
else:
    show_message("未识别到有效指令", 100)
    play_speech("未识别到有效指令,请重新尝试")

3. 城市识别与转换

  • 使用cpca库自动解析地址信息,提取用户语音中的城市名称。
  • 通过pypinyin将中文城市名转换为拼音,适配电影 API 的参数要求。

代码
import cpca
from pypinyin import pinyin, Style

def chinese_to_pinyin(text):
    """将中文转换为拼音(无音调)"""
    pinyin_list = pinyin(text, style=Style.NORMAL)
    return ''.join([p[0] for p in pinyin_list])

# 从语音识别结果中提取城市名
location = cpca.transform_text_with_addrs(recognition_str, pos_sensitive=True)
city = location.loc[0, '市'][:-1]  # 获取城市名并去除"市"字

4.语音合成功能

该模块实现了语音合成与播放功能,调用讯飞语音合成接口将文本信息转换为语音文件并保存,然后通过扬声器播放该语音文件,播放完成后清理临时文件,保证设备存储空间的合理使用。

代码
def play_speech(text, filename="speech.wav"):
    """语音合成并播放"""
    try:
        tts.synthesis(text, filename)  # 调用讯飞语音合成将文本转为语音并保存
        u_audio.play(filename)  # 播放语音文件
        # 等待语音播放完成
        time.sleep(0.5)
        while u_audio.is_playing():
            time.sleep(0.1)
    except Exception as e:
        print(f"语音播放错误: {e}")
        show_message("语音播放错误,请重试", 100, 16, "#FF0000")
        time.sleep(2)
    finally:
        # 清理临时文件
        if os.path.exists(filename):
            os.remove(filename)

5.天气查询功能

支持传入任意城市名称查询天气。

显示天气图标、温度、湿度、风力等信息。

通过语音合成播报详细天气状况。

代码
def TianQi(city_chinese):
    """查询指定城市的天气信息"""
    u_gui.fill_rect(x=0, y=0, w=240, h=320, color="#000000")
    weather = TianqiApiHourlyWeather("86943926", "Tj6vFcS0", city_chinese)
    
    if weather.fetch_data():
        # 显示天气图片和数据
        u_gui.draw_image(image=weather.get_weather_img("wea_img"), x=0, y=0)
        # 显示温度、湿度等信息...
        
        # 语音播报天气信息
        weather_text = (
            f"{city_chinese}当前天气{weather.get_value('wea')},"
            f"最高温度{weather.get_value('tem1')}摄氏度,"
            f"最低温度{weather.get_value('tem2')}摄氏度,"
            f"{weather.get_value('win')}{weather.get_value('win_speed')},"
            f"PM2.5值为{weather.get_value('air_pm25')},"
            f"紫外线强度{weather.get_value('uvDescription')},"
            f"空气质量{weather.get_value('air_level')}。"
            f"{weather.get_value('air_tips')}"
        )
        tts.synthesis(weather_text, "speech.wav")
        u_audio.play("speech.wav")

6. 电影查询功能

自动逐部播放电影海报,播报电影标题、导演、主演、评分等信息,支持用户连续听取。

代码
def DianYing(city_chinese):
    """查询指定城市的热映电影信息"""
    city_pinyin = chinese_to_pinyin(city_chinese)  # 城市名转拼音
    scraper = DoubanMovieScraper(city_pinyin)
    movie_count = scraper.get_movies_summary("count")
    
    if movie_count > 0:
        # 显示电影数量
        txt2.config(text=f"{city_chinese}现有{movie_count}部热映电影")
        tts.synthesis(f"{city_chinese}现有{movie_count}部热映电影", "speech.wav")
        u_audio.play("speech.wav")
        
        # 循环展示每部电影信息
        for i in range(1, movie_count + 1):
            # 显示电影海报和详情
            poster = img_manager.get_image(scraper.get_movie_attribute(i, "poster_url"))
            u_gui.draw_image(image=poster, x=0, y=0)
            
            # 语音播报电影信息
            movie_info = (
                f"第{i}部:《{scraper.get_movie_attribute(i, 'title')}》,"
                f"导演{scraper.get_movie_attribute(i, 'directors')},"
                f"主演{scraper.get_movie_attribute(i, 'actors')},"
                f"评分为{scraper.get_movie_attribute(i, 'rating')}分。"
            )
            tts.synthesis(movie_info, "speech.wav")
            u_audio.play("speech.wav")
            time.sleep(1)

7.完整代码

代码
#  -*- coding: UTF-8 -*-

# MindPlus
# Python
import sys
sys.path.append("/root/mindplus/.lib/thirdExtension/doudad-webimage-thirdex")
sys.path.append("/root/mindplus/.lib/thirdExtension/doudad-player-thirdex")
sys.path.append("/root/mindplus/.lib/thirdExtension/doudad-hourlyweather-thirdex")
import time
import cpca
from unihiker import GUI
from player import PLAYER
from unihiker import Audio
from df_xfyun_speech import XfIat
from df_xfyun_speech import XfTts
from pypinyin import pinyin, Style
from pinpong.board import Board,Pin
from webimage import WebImageManager
from webimage import DoubanMovieScraper
from pinpong.extension.unihiker import *
from tianqiapi_hourlyweather import TianqiApiHourlyWeather

# 自定义函数
def TianQi(city_chinese):
    u_gui.fill_rect(x=0,y=0,w=240,h=320,color="#000000")
    weather = TianqiApiHourlyWeather("86943926", "Tj6vFcS0",city_chinese)
    if weather.fetch_data():
        u_gui.draw_image(image=weather.get_weather_img("wea_img"),x=0,y=0)
        tips=u_gui.draw_text(text=weather.get_value("air_tips"),x=0,y=230,font_size=15, color="#FFFFFF")
        tips.config(w=240)
        u_gui.draw_text(text=weather.get_value("tem"),x=150,y=20,font_size=20, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("humidity"),x=150,y=60,font_size=20, color="#FFFFFF")
        u_gui.draw_text(text="PM2.5",x=5,y=125,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text="空气质量",x=76,y=125,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text="紫外线",x=170,y=125,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("air_pm25"),x=20,y=160,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("air_level"),x=76,y=160,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("uvDescription"),x=185,y=160,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("win"),x=20,y=195,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("win_speed"),x=95,y=195,font_size=15, color="#FFFFFF")
        u_gui.draw_text(text=weather.get_value("win_meter"),x=150,y=195,font_size=15, color="#FFFFFF")
        tts.synthesis((str(city_chinese) + str((str("当前天气") + str((str(weather.get_value("wea")) + str((str(",最高温度") + str((str(weather.get_value("tem1")) + str((str("摄氏度") + str((str(",最低温度") + str((str(weather.get_value("tem2")) + str((str("摄氏度,") + str((str(weather.get_value("win")) + str((str(weather.get_value("win_speed")) + str((str(",PM2.5") + str((str(weather.get_value("air_pm25")) + str((str(",紫外线强度") + str((str(weather.get_value("uvDescription")) + str((str(",空气质量") + str((str(weather.get_value("air_level")) + str(weather.get_value("air_tips"))))))))))))))))))))))))))))))))))), "speech.wav")
        u_audio.play("speech.wav")
def DianYing(city_chinese):
    city_pinyin = chinese_to_pinyin(city_chinese)
    scraper = DoubanMovieScraper("city_pinyin")
    txt.config(text="说话中...")
    txt2.config(text=(str(city_chinese) + str((str("现有") + str((str(scraper.get_movies_summary("count")) + str("部热映电影")))))))
    txt2.config(w=240)
    tts.synthesis((str(city_chinese) + str((str("现有") + str((str(scraper.get_movies_summary("count")) + str("部热映电影")))))), "speech.wav")
    u_audio.play("speech.wav")
    for i in range(1, scraper.get_movies_summary("count"), 1):
        poster = img_manager.get_image(scraper.get_movie_attribute(i, "poster_url"))
        u_gui.draw_image(image=poster,x=0,y=0)
        tts.synthesis((str("第") + str((str(i) + str((str("部:") + str((str(scraper.get_movie_attribute(i, "title")) + str((str(",导演") + str((str(scraper.get_movie_attribute(i, "directors")) + str((str(",主演") + str((str(scraper.get_movie_attribute(i, "actors")) + str((str(",评分") + str(scraper.get_movie_attribute(i, "rating"))))))))))))))))))), "speech.wav")
        u_audio.play("speech.wav")
        time.sleep(1)
def chinese_to_pinyin(text):
    pinyin_list = pinyin(text, style=Style.NORMAL)
    return ''.join([p[0] for p in pinyin_list])


appId = "XXXX"
apiKey ="XXXX"
apiSecret = "XXXX"
u_gui=GUI()
Board().begin()
u_audio = Audio()
options = {}
iat = XfIat(appId, apiKey, apiSecret)
tts = XfTts(appId, apiKey, apiSecret, options)
player = PLAYER()
img_manager = WebImageManager(None, None)
time.sleep(1)
print("按下A键")
while True:
    u_gui.clear()
    txt=u_gui.draw_text(text="按下A键开始",x=70,y=0,font_size=20, color="#0000FF")
    txt2=u_gui.draw_text(text="",x=0,y=40,font_size=20, color="#0000FF")
    u_gui.wait_a_click()
    if (button_a.is_pressed()==True):
        txt.config(text="聆听中...")
        u_audio.record("record.wav",5)
        txt.config(text="说话中...")
        recognition_result = iat.recognition("record.wav")
        print((str("识别结果:") + str(recognition_result)))
        txt.config(text="")
        txt2.config(text=recognition_result)
        txt2.config(w=240)
        location= cpca.transform_text_with_addrs(recognition_result, pos_sensitive=True)
        city = location.loc[0, '市'][:-1]
        if (recognition_result.find("电影")!=-1):
            DianYing(city)
        if (recognition_result.find("天气")!=-1):
            TianQi(city)
    time.sleep(5)

 

11、核心技术说明

 

1.语音识别技术

 

技术原理:基于科大讯飞的离线 / 在线混合语音识别引擎,通过声学模型和语言模型将语音信号转换为文本,支持中文普通话及多地方言。

 

项目应用:录音完成后,调用iat.recognition("record.wav")接口,将音频文件转为文本(如 “查询北京的天气”)。

 

2.自然语言处理

 

(1)关键词匹配

 

原理:通过字符串查找(find()方法)识别 “天气”“电影” 等关键词,判断用户需求;

应用:若文本含 “天气” 则调用TianQi()函数,含 “电影” 则调用DianYing()函数。

 

(2)地理位置解析

 

原理:基于中文地址分词和规则匹配,从文本中提取省、市、区等行政区划信息;

应用:通过cpca.transform_text_with_addrs(recognition_str)解析出城市名(如从 “查上海的天气” 中提取 “上海”)。

 

(3)拼音转换

 

原理:将中文汉字转为无音调拼音,适配电影 API 的城市参数格式(需拼音作为查询条件);

应用:通过chinese_to_pinyin()函数将 “西安” 转为 “xian”,作为电影查询的地区参数。

 

3.语音合成技术

 

技术原理:将文本转换为自然语音的 TTS(Text-to-Speech)技术,支持调整语速、音色,生成接近真人的语音。

 

项目应用:查询完成后,调用tts.synthesis(文本, "speech.wav")生成语音文件;

通过u_audio.play("speech.wav")播放,实现 “信息→语音” 的反馈(如将天气数据转为 “西安当前气温 25 度...” 的语音)。

 

12、应用场景与拓展方向

 

1.应用场景

 

日常出行:用户出门前查询目的地天气、影院排片;

老年辅助:为不熟悉智能设备的老年人提供语音化生活信息服务;

教学演示:作为 AI 语音交互 + 物联网结合的教学案例,展示自然语言处理的实际应用。

 

2.拓展方向

 

功能扩展:增加美食推荐、公交查询、新闻播报等生活服务;

硬件优化:增加离线语音模块,减少对网络的依赖;升级电池容量,延长续航时间

 

附件

评论

user-avatar