1、项目概述
本项目基于行空板 M10 扩展板,开发了一款支持多城市的智能语音生活信息查询助手,旨在通过自然语音交互,为用户提供便捷的跨城市生活信息查询服务。
解决用户在出行、日常规划中快速获取多城市天气、热映电影等信息的需求,通过语音交互降低操作门槛,适配各年龄段用户(尤其适合不熟悉触屏操作的群体)。
2、功能演示

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

(2)行空板M10扩展板组合
行空板M10扩展板组合由行空板M10电机IO扩展板与行空板M10电池扩展板两块板子组成。二者采用乐高兼容结构及即插即用设计。
行空板M10电机IO扩展板集成双路直流电机驱动、红外收发、RGB,还通过引出金手指扩展出行空板 M10 的剩余 I/O 接口。
行空板M10电池扩展板配备 800mAh 电池。
(3)蓝牙音箱

4、注册天气API
本项目采用免费实况+24小时天气接口,5分钟更新一次,包含基本天气信息、24小时逐小时天气、实时气象预警列表、湿度、能见度、气压、日出日落、空气质量、pm2.5、pm10、o3、no2、so2、是否需要带口罩、外出适宜、开窗适宜、是否需要打开净化器等,可按地名、城市编号、IP查询、经纬度查询。
(1)注册天气API,确保账号状态:已激活,没有过期。网址http://www.tianqiapi.com/

(2)记录鉴权信息:APPID、APISecret,后面程序中需要使用。
(3)请求方式:GET
(3)请求示例:http://gfeljm.tianqiapi.com/free/v2030?appid=your_appid&appsecret=your_appsecret
(4)请求参数说明:

(5)响应JSON

5、注册讯飞开放平台
试用语音识别,确保账号状态:token有余量,且在有效期内。网址https://console.xfyun.cn/app/myapp

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

7、开发准备
(1)下载安装Mind+ V1.8.1 RC3,选择Python模式。
https://download3.dfrobot.com.cn/MindPlus_Win_V1.8.1_RC3.0.exe

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

(3)连接行空板M10
使用数据线连接电脑,连接远程终端——10.1.2.3,如下图所示。

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

(4)行空板M10启用蓝牙功能
在终端窗口依次执行以下命令,如下图所示:
bluetoothctl
default-agent
power on

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

(6)配对连接蓝牙设备
在终端依次输入以下命令,进行配对。
trust 7D:9F:9A:FE:28:D9
pair 7D:9F:9A:FE:28:D9
connect 7D:9F:9A:FE:28:D9

出现:[CHG] Device 7D:9F:9A:FE:28:D9 Connected: yes
Connection successful
表示连接成功到MAC地址为7D:9F:9A:FE:28:D9的蓝牙设备。
输入exit退出。
8、系统UI及前端设计
1.启动与触发
- 设备通电后自动启动,屏幕显示 “按下 A 键开始”。

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

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

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

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

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

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

第二部海报展示

第三部海报展示

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

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

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

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.拓展方向
功能扩展:增加美食推荐、公交查询、新闻播报等生活服务;
硬件优化:增加离线语音模块,减少对网络的依赖;升级电池容量,延长续航时间
附件
评论