作品设计报告final report

80
基于计算机视觉和语音交互的老人看护陪伴系统 2020 年英特尔杯大学生电子设计竞赛嵌入式系统专题邀请赛 2020 Intel Cup Undergraduate Electronic Design Contest - Embedded System Design Invitational Contest 作品设计报告 Final Report 报告题目:基于计算机视觉和语音交互的 老人看护陪伴系统 学生姓名: 彭振华 尹昊龙 赖启平 指导教师: 张育林 参赛学校: 西安交通大学

Upload: khangminh22

Post on 03-Feb-2023

3 views

Category:

Documents


0 download

TRANSCRIPT

基于计算机视觉和语音交互的老人看护陪伴系统

2020 年英特尔杯大学生电子设计竞赛嵌入式系统专题邀请赛

2020 Intel Cup Undergraduate Electronic Design Contest

- Embedded System Design Invitational Contest

作品设计报告Final Report

报告题目:基于计算机视觉和语音交互的老人看护陪伴系统

学生姓名: 彭振华 尹昊龙 赖启平

指导教师: 张育林

参赛学校: 西安交通大学

基于计算机视觉和语音交互的老人看护陪伴系统

2020年英特尔杯大学生电子设计竞赛嵌入式系统专题邀请赛

参赛作品原创性声明

本人郑重声明:所呈交的参赛作品报告,是本人和队友独立进

行研究工作所取得的成果。除文中已经注明引用的内容外,本论文

不包含任何其他个人或集体已经发表或撰写过的作品成果,不侵犯

任何第三方的知识产权或其他权利。本人完全意识到本声明的法律

结果由本人承担。

参赛队员签名:

日期: 年 月 日

基于计算机视觉和语音交互的老人看护陪伴系统

基于计算机视觉和语音交互的老人看护陪伴系统

摘要

本作品基于 AI-BoxX Gen.1 硬件平台,打造一款兼顾独居老人安全需求和心理需求、操作

便捷、智能化程度高的老人看护陪伴系统。系统基于 OpenPose 人体姿态估计提取关键点,进

行跌倒检测,为老人居家安全保驾护航;通过语音交互技术陪伴老人,提供精神慰藉;利用

外设传感器实现家庭环境监测、健康数据记录和日常服药提醒等功能。本系统集看护和陪伴

于一体,多技术融合、稳定高效、性价比高,具有较高的实用价值和广阔的应用前景。

关键词:计算机视觉,跌倒检测,语音交互,健康监测,娱乐陪伴

基于计算机视觉和语音交互的老人看护陪伴系统

THE ELDERLYCARE AND COMPANION SYSTEM

BASED ON COMPUTER VISION AND VOICE

INTERACTION

ABSTRACT

This entry is based on the AI-BoxX Gen.1 hardware platform to create an elderly care andcompanion system, which takes the safety and psychological needs of the elder people who livesalone into account. This system is easy to operate, and has a high degree of intelligence. It extractskey points based on OpenPose human pose estimation model, and then performs the fall detection toguarantee the safety of the elderly at home. Besides, it applies voice interaction technology toaccompany the elderly for entertainment and provide spiritual comfort. Finally, through peripheralsensors, it can achieve home environment monitoring, health data recording, daily routinemedication reminding and other functions. This system integrates health care and companion of theelderly and achieves multi-technology integration, which is stable and efficient. All in all, this entryhas a high performance-price ratio, high practical value and broad application prospects.

Key words: computer vision, fall detection, voice interaction, health monitoring, companion

基于计算机视觉和语音交互的老人看护陪伴系统

目 录

第一章 绪论----------------------------------------------------------------------------------------------------11.1 项目背景和意义---------------------------------------------------------------------------1

1.1.1 项目背景---------------------------------------------------------------------------11.1.2 项目意义--------------------------------------------------------------------------2

1.2 国内外研究现状------------------------------------------------------------------------21.2.1 国外研究现状--------------------------------------------------------------------------21.2.2 国内研究现状--------------------------------------------------------------------------3

1.3 研究目的及主要内容------------------------------------------------------------------------41.3.1 研究目的--------------------------------------------------------------------------41.3.2 主要内容-------------------------------------------------------------------------4

1.4 研究方法与技术路线------------------------------------------------------------------------5第二章 系统概述-------------------------------------------------------------------------------------6

2.1 需求分析 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 62.2 设计原则------------------------------------------------------------------------------62.3 功能与指标-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 7

2.3.1 跌倒检测--------------------------------------------------------------------------72.3.2 娱乐陪伴-------------------------------------------------------------------------72.3.3 人机交互--------------------------------------------------------------------------72.3.4 家庭环境监测----------------------------------------------------------------------72.3.5 健康数据记录-----------------------------------------------------------------------72.3.6 日常用药提醒--------------------------------------------------------------------------7

2.4 系统组成------------------------------------------------------------------------------72.5 硬件框图------------------------------------------------------------------------------82.6 软件流程-------------------------------------------------------------------------------8

第三章 系统原理及方案--------------------------------------------------------------------103.1 跌倒检测 -- -- - -- - -- - -- - -- -- - -- - -- - -- - -- -- - -- - -- - -- - -- -- - -- - -- - -- - -- - -- -- - -- - -- - -- --10

3.1.1 研究综述--------------------------------------------------------------------------103.1.2 OpenPose 原理--------------------------------------------------------------------113.1.3 老人跌倒事件分析-----------------------------------------------------------------123.1.4 基于人体关键点的跌倒检测算法-----------------------------------------------------13

3.2 语音识别------------------------------------------------------------------------------143.2.1 基于深度神经网络的语音识别------------------------------------------------153.2.2 基于 Kaldi 开源工具箱的语音识别------------------------------------------------------153.2.3 Kaldi 中的语音识别算法与模型---------------------------------------------------163.2.4 THCHS-30 中文数据集训练实例-------------------------------------------------------183.2.5 基于百度语音 API 和图灵机器人 API 的语音识别与交互--------------------------19

基于计算机视觉和语音交互的老人看护陪伴系统

3.3 娱乐陪伴 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 203.3.1 亲友实时通话--------------------------------------------------------------------------203.3.2 音视频播放----------------------------------------------------------------------------203.3.3 怀旧照片翻阅-------------------------------------------------------------------20

3.4 人机交互------------------------------------------------------------------------------203.4.1 语音交互--------------------------------------------------------------------------213.4.2 触摸界面交互----------------------------------------------------------------------------21

3.5 健康数据记录-----------------------------------------------------------------------213.5.1 工作原理---------------------------------------------------------------------------------223.5.2 技术参数--------------------------------------------------------------------------------22

3.6 家庭环境监测-------------------------------------------------------------------------22第四章 系统测试-------------------------------------------------------------------------------------24

4.1 系统测试方案-----------------------------------------------------------------------------244.2 测试设备--------------------------------------------------------------------------------------244.3 跌倒检测测试---------------------------------------------------------------------------244.4 语音交互测试------------------------------------------------------------------------------254.5 外设模块测试-------------------------------------------------------------------------------25

4.5.1 GSM 模块--------------------------------------------------------------------------254.5.2 数字血压采集模块-----------------------------------------------------------------------25

4.6 结果分析-------------------------------------------------------------------------------26第五章 总结与展望---------------------------------------------------------------------------------------27

5.1 系统特色-----------------------------------------------------------------------------275.2 前景展望------------------------------------------------------------------------------27

参考文献---------------------------------------------------------------------------------------28附录(部分程序源代码)-------------------------------------------------------------------------------30

基于计算机视觉和语音交互的老人看护陪伴系统

第 1 页 共 74 页

第一章 绪论

1.1 项目背景和意义

当前我国人口老龄化程度不断加深,助老产品市场需求巨大、发展前景广阔。本作品

的设计针对独居老人群体,切实解决独居老人需求,具有较高的现实意义。

1.1.1 项目背景

在当下的中国社会,经济快速发展,人民生活水平不断提高。与此同时,中国的人口

老龄化程度也在不断加深。在我国实施了三十多年的独生子女政策已正式落幕,它在控制

我国人口过快增长、提高妇女社会地位和人们生活水平上做出了巨大贡献,但同时也是一

把双刃剑,造成了我国老年人口迅速上升,“421”式家庭结构成为主流趋势,带来的结果就

是人口结构的快速老龄化。

图 1-1 “421”式家庭结构模型

根据国际标准,当一个国家 60 岁以上的人口占总人口的比例达到 10%,或者 65 岁以

上的人口数达到总人口的 7%,便可称为“老龄化”社会。国家统计局人口统计数据显示,2010年我国总人口为 13.40 亿,其中 65 岁及以上人口为 1.18 亿,占总人口的 8.87%,而 2019年我国总人口已突破 14 亿,其中 65 岁及以上人口达到了 1.75 亿,占总人口的 12.57%[1]。

从图 1-2 中,可以了解到我国人口老龄化变化情况。

图 1-2 近几年老年人口数据变化

基于计算机视觉和语音交互的老人看护陪伴系统

第 2 页 共 74 页

在人口老龄化趋势下,空巢化现象正越来越明显。根据《第四次中国城乡老年人生活

状况抽样调查结果》显示,空巢老年人占老年人口的比例为 51.3%,老年人精神慰藉服务严

重不足。专家预计,到 2030 年我国空巢老人家庭比例或将达到 90%。然而,空巢老人独居

生活存在着许多风险,常常会发生意外,其中以意外跌倒最为普遍。根据统计数据,大约

超过 30%的 65 岁及以上老人每年会跌倒一次,而且年龄越高,发生跌倒的风险也越大,大

约 50%到 80%的 80 岁以上老人存在跌倒风险[2]。目前,每年至少有 3000 万老人发生 3500万次跌倒,医疗费用的总额超过 50 亿元,社会成本约为 180-200 亿元。意外跌倒成为最昂

贵的救治医疗项目之一,给家庭和政府带来了巨大的经济压力。如何预防摔倒和及时救助,

让独自生活的老人避免不必要的危险已经成为每个家庭都关心的问题[3]。

1.1.2 项目意义

根据马斯洛需求层次理论,人的需求分为五个不同的层次,按照由低到高、由物质到

精神概括为生理需求、安全需求、社交需求、尊重需求和自我实现需求。针对老人的生理

和心理特点,根据马斯洛需求层次理论,郑巧英等人提出了“老有所养、老有所医、老有所

为、老有所学、老有所乐”的目标[4]。

图 1-3 马斯洛需求层次理论

考虑到独居老人身体机能下降的特点,如何确保老人居家安全成了普遍关注的问题。

融合先进的边缘计算和人工智能技术开发一款智能看护产品,有助于对独居老人居家健康

安全提供有力保障,有助于当老人发生意外时提供紧急求救服务,在人口老龄化的时代背

景下具有重要的现实意义。另外,大部分人普遍关注老年人的身体健康状况,却忽视了老

人精神层面的需求。当前的空巢化趋势给老人心理健康方面产生一定的冲击,老人独居而

得不到社交和文化生活的满足,缺少精神慰藉。对此,胡爱敏提出养老服务的着力点在于

满足老年人的多元化需求,既要在物质养老上解除老人的后顾之忧,也要实现精神养老,

使老年人感受关爱、受到尊重和认可,更要注重文化养老,丰富老年人晚年生活[5]。

因此,通过先进技术的优势,项目针对独居老人而设计,切实解决老人健康安全需求

和心理需求,让科技真正造福独居老人,具有较高的现实意义。

1.2 国内外研究现状

1.2.1 国外研究现状

国外对于养老服务机器人已有一定研究。澳大利亚Hobbit[6]项目结合机器人学、老年学

基于计算机视觉和语音交互的老人看护陪伴系统

第 3 页 共 74 页

和人机交互方面的研究,开发了一种能够预防和检测老年人摔倒并处理紧急情况的护理机器

人的原型,此外还测试评估了用户对机器人的可用性等以验证研究结果,引导未来设计的改

进方向。Balaguer等人[7]开发的MART机器人不但能完成日常对老人的照料,如吃饭、剃须

等,还能够在房间里自由移动与进行动作。Schroeter等人[8]在进行机器人设计时以“友好

(CompanionAble)”为导向,将老龄服务机器人与智能家庭相结合,为轻度认知功能障碍的老龄

用户提供服务,主要功能包括日程管理与建议、物品储存、问候与智能管理等,如图1-4所示。但是许多产品以功能为主,对老人并不友好,无法与老人进行互动,也无法满足老年人

的心理需求。

图 1-4 CompanionAble服务机器人

随着计算机视觉领域的迅速发展,基于视频和异常行为识别算法来发现老人摔倒及突发

疾病等现象的技术受到越来越多人的关注。英国爱丁堡大学专门设定Behave视觉项目小组,

研究视频中运动目标的异常以及危险事情的发生。同时英国的雷丁大学也专门设立了用来研

究车辆和行人运动的高级项目研究组。葡萄牙、匈牙利以及法国等国家共同设立了CAVIAR项目,主要对视频中运动目标的行为进行实时监控。

此外还有一些智能产品和技术帮助解决空巢老人的养老问题。英国赫特福德郡大学研发

了一种叫“交互屋”的设施,它不仅可监测居住者的健康,还能够“学习”其生活规律。日本科

学家研制的“baby Lloyd”机器人不仅可治疗空巢老人的孤独症、抑郁症,同时还可提供看护

服务[9]。

1.2.2 国内研究现状

相较于国外养老服务机器人的发展,目前国内这方面的研究尚处在初步发展阶段。哈尔

滨工业大学刘美堂等[10]设计的家用保安机器人能实时监控家庭环境情况,并及时报告险情,

具有低成本、高可靠性、高智能化程度等特点。中科院深圳先进院[11]研制的家庭监控机器

人能够自由移动,并检测家中是否漏水、着火、煤气泄露等,一旦发现异常或有陌生人闯入,

它会主动向主人的手机发送报警短信。这款机器人同时也能为老人提供一定的便利,包括识

别目标、提醒老人吃药等。

国内在视频监控识别异常行为领域贡献最大的是中科院自动化所,其在交通控制和红外

识别方面均有成就,更是建立了CASIA数据集。除此之外许多研究人员也着力于行为识别算

法的研究。吴恩华等人[12]提出了一种人体识别算法,利用人体的侧影和相应的概率所建立

的模型实现对人体的检测。王亚飞[2]提出了一种改进的基于人体形状分析的跌倒检测算法,

通过选取全局运动方向特征、投影直方图和头部运动特征等8个特征来标识跌倒。上海交通

大学田广等人[13]通过对人体的腿、躯干、头和手等建模,然后在特征集上训练身体每个部

基于计算机视觉和语音交互的老人看护陪伴系统

第 4 页 共 74 页

位以及整个身体的检测器,最后根据贝叶斯决策算法确定候选区域中的运动人体。

另外,还有一些按键式、穿戴式摔倒报警装置,智能电视交互系统[9]等,均对老年人居

家养老服务产品的设计提供了一定参考。

图 1-5 智能电视交互系统效果展示[9]

综上所述,国内外对于居家养老服务产品开展了一定研究,随着人工智能、深度学习、

物联网等技术的融入,未来产品将更趋向智能化、便捷化。但目前产品对不同家居环境和用

户个性化需求针对性不高,应用场景定义仍需明确,与人们的期望存在一定差距,并且对于

老人的精神需求关注度不够,智能家居助老产品缺乏人机交互方式等的研究,无法为老人提

供必要的精神慰藉。

1.3 研究目的及主要内容

1.3.1 研究目的

基于以上研究背景,本文拟基于计算机视觉和语音交互技术,研发一种新型智能化居家

助老服务产品,用于对独居老人进行看护和陪伴,通过对系统的人性化创新设计,提高独居

老人生活的安全保障和使用体验。具体而言,本项目作品将围绕以下目标进行设计:

(1)了解独居老人各方面的特征,挖掘其居家养老核心需求;

(2)构建基于计算机视觉的看护系统,通过基于家庭安防摄像头的视频输入,在边缘

侧对老人跌倒行为进行识别和报警;

(3)研究交互设计的方法理论与设计理念,提出针对空巢老人的交互设计方法和原则,

并设计针对独居老人的智能交互方案;(4)通过外设传感器检测并记录老人健康数据和家庭环境数据。

1.3.2 主要内容

本文主要研究一种基于计算机视觉和语音交互的老人看护陪伴系统,对独居老人进行

安全监护和娱乐陪伴。本文所做的主要工作如下:

(1)对目前国内外相关居家助老产品进行分析,提炼独居老人需求和现实痛点,定义

系统功能;

(2)对人体姿态及其动作进行了研究,分析当前国内外跌倒检测方法研究进展,结合

边缘计算发展趋势和 AI-BoxX Gen.1 硬件平台强大的视觉处理能力,采用基于 OpenPose 提

取人体关键点的跌倒检测算法,实现家庭环境中对老人跌倒行为的检测和报警;

(3)运用 Kaldi 开源语音工具箱进行深度学习神经网络的边缘计算与搭建;

(4)分析交互技术相关理论和设计要点,开发对老人友好的交互方式和交互界面;

(5)采用数字血压检测模块和 PMS5003T 环境传感器采集老人健康数据、进行家庭环

境监测;

(6)对独居老人看护陪伴系统进行设计与实现,打造一款具有跌倒检测、娱乐陪伴等

功能的智能化系统。

基于计算机视觉和语音交互的老人看护陪伴系统

第 5 页 共 74 页

1.4 研究方法与技术路线

本文的研究方法主要有:

(1)文献分析法;检索国内外关于居家养老、智慧养老、基于计算机视觉进行跌倒检

测的文献,整理分析有关研究成果,梳理跌倒检测的体系结构与研究框架,发现研究难点与

不足;

(2)实物设计与对比分析:寻找合适的模型,选择合适的指标评价模型性能,基于模

型进行改进和优化,实现家庭环境下老年人的跌倒检测。基于 AI-BoxX Gen.1 硬件平台进行

嵌入式系统设计与开发,并与相关研究成果进行比较,分析优劣,不断加以改进。

本文研究的技术路线如图 1-4 所示。

图 1-4 技术路线

基于计算机视觉和语音交互的老人看护陪伴系统

第 6 页 共 74 页

第二章 系统概述

2.1 需求分析

通过项目前期调研和背景分析,针对 65 岁及以上独居老人,本文总结他们的居家养老

产品需求如下:

(1)危险预警:老人独自在家风险较大,打造一款智能化居家养老产品,最迫切的功

能需求便是当老人发生危险时能够及时识别并远程提醒其家人或社区服务人员,为老人提

供及时的救助。尤其是室内意外跌倒,根据 N.Noury.等人研究,及时检测人体倒地并呼救

可将老人死亡率降低 80%[14];

(2)日常照料:针对老人身体机能下降,需要产品能够提供基本的照料服务,为老人

提供定时的备忘提醒、环境监测、健康数据监督等服务;

(3)精神文化生活服务:独居老人易产生孤独感,需要满足老人的精神文化生活需求,

当前技术发展背景下,通过人机交互等技术为老人提供精神慰藉。

图 2-1 居家养老产品需求分析图

2.2 设计原则

老年人作为特殊群体,在行动能力和感觉能力等方面都有很大程度的降低,针对老年

人群体设计的系统,应当体现对老人的独特关怀和尊重。经过分析,团队确定以下三个设

计原则,如图 2-2 所示。

图 2-2 系统设计原则

基于计算机视觉和语音交互的老人看护陪伴系统

第 7 页 共 74 页

(1)易用性原则[15]:所谓易用性,是指系统同时具备在易见、易学、易用三个方面的

特殊有用性。当产品符合用户的心理认知并满足其内心需求时,用户对产品产生愉悦的情

绪,相应地产品满意度也会随之升高。因此,系统的设计应以老年人生理心理尺度为基准,

依据老年人的辨识和认知能力描述产品功能、操作等的界面来体现设计的人文关怀,不断

提升系统操作的便捷性;

(2)可靠性原则:在功能完整性前提下,助老服务产品应确保使用安全可靠,首先在

结构方面要求稳定牢固,杜绝隐患。对于老人独居时发生的紧急情况应能及时准确地提供

报警服务。产品人机工程学也应被纳入特殊考虑因素中,加强对老年人生理变化尺度的分

析,设计出足以与老年人生理条件匹配,且与之良好互动的优质产品;

(3)人性化原则[16]:老年人能够快速接纳的产品一定包含在他们易于理解的范围内。

人们对于事物的认识是循序渐进的,当一个人对于使用一些物品有了特定的观念的时候,

这种先入为主的观念往往较难改变。而随着年龄的增长,人们接受新鲜事物的能力也有所

衰退,所以以老年人为中心的产品在一定程度上要考虑他们的心理需求。系统设计要注重

老年人精神文化生活需求,通过交互式设计让老人能够及时与不在身边的子女联系、参与

社会活动、或者进行一些娱乐项目等,这对老年人来说是重要的精神慰藉。

2.3 功能与指标

2.3.1 跌倒检测

通过摄像头的视频输入,实时地在视角范围内寻找目标人体,能够较为精准地提取并标

记人体关键点,采用基于人体关键点的跌倒检测算法对老人进行跌倒检测。在设备正常无损

的情况下,跌倒检测准确率达到 90%以上,帧率为 20fps,每帧图片处理时间不超过 20ms。2.3.2 娱乐陪伴

通过接收老人的语音命令,采用语音识别技术,提供拨打家人电话、播放音乐、播放视

频、翻阅照片等娱乐功能。语音命令的识别率达 90%以上。

2.3.3 人机交互

通过语音控制或触摸屏控制进行人机交互。针对老人特点,设计人性化图形界面,提供

友好的交互方式,便于老人操作。语音交互的无接触式设计降低了老年人的学习成本,避免

老人对新技术产生畏难情绪。

2.3.4 家庭环境监测

系统采用 PMS5003T 集成传感器实时监测室内环境温度、湿度、PM2.5 和 PM10 等环境

数据,利用激光散射原理实现精准测量,最小分辨粒径达到 0.3µm,实时响应并连续采集数

据传入处理器,确保室内环境安全。

2.3.5 健康数据记录

系统外接数字血压采集模块,提醒老人定期自测健康数据,通过采集并分析血压和心率

值检测老人身体健康状况。

2.3.6 日常用药提醒

老人可独自设定服药计划,到达服药时间后系统将通过语音提醒老人服用特定种类和数

量的药品,确保老人及时用药和安全。

2.4 系统组成

系统以 AI-BoxX Gen.1 硬件平台为核心,主要包括 USB 监控摄像头、麦克风、扬声器、

触摸显示屏、GSM 模块、数字血压采集模块和 PMS5003T 环境传感器等外设组成,系统实

物如图 2-3 所示。

基于计算机视觉和语音交互的老人看护陪伴系统

第 8 页 共 74 页

图 2-3 系统实物图

2.5 硬件框图

系统基于AI-BoxX Gen.1硬件平台,采用直流12V电压进行供电,通过LAN1口连接WiFi提供网络支持。摄像头与支架相连,模拟家庭安防监控设备,采集视频数据并通过USB口接

入硬件平台进行处理。系统通过音频接口连接麦克风和扬声器用于语音交互,通过HDMI接口连接触摸显示屏展示人机交互界面。数字血压采集模块和PMS5003T环境传感器采用

UART通信将数据传入硬件平台,另外,GSM模块通过RS232接口与硬件平台相连。系统硬

件框图如图2-4所示。

图 2-4 系统硬件框图

2.6 软件流程

系统开始运行时,语音识别,图形界面,跌倒检测三部分同步进行,跌倒检测实时跟

踪并监测老人行动,确认老人跌倒就将自动执行紧急呼救的功能,拨打预设的紧急联系人

电话。系统的控制以语音识别为主,图形界面为辅,致力于让老人轻松便捷地使用系统功

能。系统功能分为多媒体、通信、健康与安全三大模块。多媒体提供了一些最基本的娱乐

功能以实现老有所乐,通信功能让老人可随时和亲人联系,或是危急情况下主动或自动地

紧急呼救,以实现老有所伴,健康与安全模块监测环境质量和健康数据,以实现老有所安。

基于计算机视觉和语音交互的老人看护陪伴系统

第 9 页 共 74 页

图 2-5 系统软件功能框图

基于计算机视觉和语音交互的老人看护陪伴系统

第 10 页 共 74 页

第三章 系统原理及方案

3.1 跌倒检测

随着计算机视觉技术的发展,人体姿态估计受到许多研究人员的关注,近年来取得了一

定的发展。事实上,日常行为识别、异常行为检测技术在智能家居、智慧安防等领域具有广

阔的发展空间,对特殊人群能够起到有效的监督作用。系统基于OpenPose库,在独居老人

家庭环境中进行跌倒检测,发生危险后进行远程报警提醒监护人,及时挽救老人生命,为独

居老人居家安全保驾护航。

3.1.1 研究综述

经过查阅、分析、归纳国内外研究人员在老年人动作识别上的研究可以发现,在跌倒检

测技术方面,主要可分为基于可穿戴式传感技术、基于环境感知技术和基于视频(计算机视

觉)检测技术。

图 3-1 跌倒检测方法

(1)基于可穿戴式传感技术

随着智能化技术的发展,多样化的传感器被广泛植入到穿戴式设备中,直接或间接对人

体的肢体运动或相关生理参数进行测量从而判断跌倒事件的发生。R.Bajcsy 等人[17]研发的一

种佩戴在手腕上的跌倒检测产品,当设备检测到用户向下的加速度和速度达到一定阈值时,

若三面内检测到一个冲击则判定为跌倒,若用户在接下来 60 秒内非活动状态持续 40 秒,则

激活报警,但该产品在向后跌倒的测试的中并不理想。Doukas 等人[18]开发了一个可穿戴设

备来检测病人的跌倒,通过使用加速器来收集数据,并使用 SVM(support vector machine)来判定跌倒。Wibisono W 等人[19]利用手机加速度和陀螺仪来检测人体跌落过程中的加速度

和角速度的最大变化,如果二者达到一定阈值,再判断该时间段内重力的变化,如果重力的

变化也达到一定阈值,则判断发生跌落。薛源[20]研究了一种实时动态检测老人跌倒的穿戴

式监测系统,利用加速度传感器采集人体三轴加速度信息,磁传感器获取人体躯干的倾角信息

以及压力传感器采集两脚的压力数据,提出了基于多传感器信息融合的方法来监测人体跌

倒,其准确率因跌倒姿势不同而不同,大致在 95%左右。

(2)基于环境感知技术

基于环境感知技术的跌倒检测系统主要是通过在活动区域安置各种非视频的无线传感

器来检测跌倒,检测过程中主要捕获人体的运动以及姿态等信息。Alwan 等人[21]在地板安装

了震动传感器来判定跌倒,多个传感器同时工作就可以判定人所处的位置,处理芯片会通过

这些位置来确定跌倒的发生。Mihai1 Popescu 等人[22]制作了一个可以采集声源离地面的高度

基于计算机视觉和语音交互的老人看护陪伴系统

第 11 页 共 74 页

(Sound Source Height Information, SSHI)的声音传感器来采集声频信号,经过滤波降噪,

然后计算声频信号的能量,若 SSHI 小于 2 英尺,则提取信号中的梅尔频率倒谱系数为特征,

以 KNN 算法判定人体是否跌倒。王立敏[23]提出了一种模块化的可拼接的跌倒检测地毯,通

过在地毯中嵌入压力传感器的方式来检测跌倒行为,该方法由于以地毯作为载体,因而在很

大程度上限制了人体活动范围,并且嵌入压力传感器具有一定的施工难度且费用较高。

(3)基于视频检测技术

由于价格不断下降且具有传感器所没有的优势,视频监控摄像头被广泛地应用于家居环

境中。基于视频检测技术的跌倒检测系统通常通过在人体活动区域固定安装视频监控装置,

捕捉人体活动图像,并且通过分析所采集的图像来判断是否有跌倒情况的发生。Rougier 等[24]

采用图像处理的方法,在摄像头捕获的人体图像中确定头部位置,通过算法计算确定头部运

动速度,当头部的水平、垂直速度同时出现峰值,且水平速度大于 2m/s,垂直速度小于

-1.5m/s,则判定出现跌倒情况,准确率为 78.9%。Hoai 等人[25]提取出行为视频的时空兴趣

点特征,采用多层表示 SVM 进行模型训练,最后采用动态规划算法来识别不同类型的行为。

Chua 等[26]采用将分析人体形状以及头部检测相融合的方法来检测跌倒行为,该方法核心是

通过将人体拟合为一个椭圆,通过分析椭圆形状来判断人体姿态,并辅以头部形状分析来进

一步排除类跌倒姿态,最终达到了准确率 95%的跌倒检测效果。沈秉乾[27]基于图像处理技

术,从人体各种姿势的图像信息中提取信号特征,对不同姿势进行 SVM 分类训练,当 SVM训练器判定人体跌倒时,报警变量值加 1,反之减 1 或者置为灵,当报警变量超过设定值时,

则判定为跌倒,准确率达 90%以上。

综合比较上述跌倒检测方法,穿戴式设备对于很多老人来讲,存在一定的使用障碍和抗

拒心理,记忆力不好的老人很容易遗落或者忘记穿戴设备,并且所采集的肢体信息较为单一,

最为常见的就是采集肢体运动过程中的加速度信号,数据较为有限;基于环境感知技术的跌

倒检测系统采用的各种传感器对场景布置条件和成本要求较高,容易因用户的无意踢、踩而

损坏,并且存在很多外界干扰因素;视频式跌倒检测系统适合在室内使用,并且能在很大程

度上判断出跌倒姿态,准确率较高,虽然人体图像的捕获受到活动区域以及光线明暗的限制,

并且光线太暗也会影响图像效果,但随着安防摄像头产品的不断升级换代,系统采集到的视

频信息质量将逐步提升,并且边缘计算深入发展,边缘侧设备的算力将有质的飞跃。考虑到

AI-BoxX Gen.1 硬件平台上的 AI 卡 HDDL-R8 具有强大的视觉加速能力,本文选择基于视频

检测技术进行跌倒检测,通过提取人体关键点设计跌倒检测算法,力求在家庭环境中实现较

高的准确率。

3.1.2 OpenPose 原理

人体姿态估计(Human Pose Estimation),是通过将图片中已检测到的人体关键点正确

的联系起来,从而估计人体姿态。人体关键点通常对应人体上有一定自由度的关节,比如颈、

肩、肘、腕、腰、膝、踝等。

OpenPose人体姿态识别项目是美国卡耐基梅隆大学(CMU)基于卷积神经网络和监督

学习并以Caffe为框架开发的开源库,可以实现人体动作、面部表情、手指运动等姿态估计,

适用于单人和多人,具有极好的鲁棒性,是世界上首个基于深度学习的实时多人二维姿态估

计应用,基于它的实例如雨后春笋般涌现。

基于计算机视觉和语音交互的老人看护陪伴系统

第 12 页 共 74 页

图 3-2 人体关键点

OpenPose的实现原理包括以下三方面:

①输入一幅图像,经过卷积网络提取特征,得到一组特征图,然后分成两个岔路,分别

使用CNN网络提取关节点置信图(PCM)和关节亲和场(PAF);②得到这两个信息后,使用图论中的偶匹配求出关节联系,将同一个人的关节点连接起

来,由于PAF自身的矢量性,使得生成的偶匹配很正确,最终合并为一个人的整体骨架;

③最后基于PAF求多人关节点解析,把多人关节点解析问题转换成图问题,利用匈牙利

算法求解。

图 3-3 OpenPose实现原理

实现的神经网络包括以下三个阶段:

阶段一:VGGNet的前10层用于为输入图像创建特征映射。

阶段二:使用2分支多阶段CNN,其中第一分支预测身体部位位置(例如肘部,膝部等)

的一组2D置信度图(S)。第二分支预测一组部分亲和度的2D矢量场(L),其编码部分之间

的关联度。

阶段三:通过贪心推理解析置信度和亲和力图,对图像中的所有人生成2D关键点。

图 3-4 OpenPose网络结构

3.1.3 老人跌倒事件分析

跌倒是老年人日常生活中经常发生的意外,也严重影响着老年人的身心健康、生活质量,

基于计算机视觉和语音交互的老人看护陪伴系统

第 13 页 共 74 页

生活中大量老人因跌倒造成严重伤害,甚至死亡。国际老人跌倒预防工作组对跌倒有如下定

义:跌倒是指在意外情况下,身体任何部位因失去平衡而摔倒在地面或其他一些更低的平面

上。当老人由于某种原因突然失去平衡,即将发生跌倒的时候,首先会整体向某个方向大幅

度倾斜,这是人体经过一连串自然反应的结果。老人跌倒一般有图 3-5 所示的几种姿势[28]。

图 3-5 老人跌倒姿势分析

根据翟远[29]的相关研究分析,各种跌倒姿态中,躯干起主导作用,脊椎骨的支撑作用

对于躯干部分的活动必不可少,正常情况下,一般人的脊椎骨具有一定的弯曲能力,能够小

范围的屈伸,即通过柔韧性来体现,但是对于老年人群体而言,年纪的增加导致身体柔性减

弱,显得更为僵硬,因而脊椎骨的屈伸角度相比一般人要小很多,因此在分析老年人的躯干

运动时可将其等效为一块固体。跌倒行为是在短时间内完成的动作,身体躯干在某一瞬间出

现快速且大幅度的弯曲,因而相对于有意识的日常动作而言跌倒时身体会产生较大的速度。

基于 OpenPose,本文通过提取视频监测中老人的人体关键点,以跌倒时产生较大速度值这

一特征作为重点依据,同时辅以身体倾角的检测,综合分析后判断跌倒事件的发生。

3.1.4 基于人体关键点的跌倒检测算法

系统以两个检测特征判断人体跌倒事件的发生,利用 OpenPose 逐帧处理来自摄像头的

视频流,提取出人体的鼻、耳、眼、颈、肩、肘、腕、腰、膝、踝关键点,通过设计的算

法分析关键点生成姿态特征,若满足两个跌倒检测特征之一,则判断老人可能跌倒。

(1)特征一:人体关键骨架向量与地面夹角

系统通过提取与人体跌倒相关的头部 1C 、腰部 0C 、踝部 2C 三个关键的特征点,利用

这些点在摄像头前景平面上投影点的相对位置变化来检测跌倒事件的发生[30]。

定义两个向量 101 CCD , 022 CCD 分别描述人体上下两部分的姿态。利用 1D , 2D计算出人体上下两部分的比例 21 /DDp 及 1D , 2D 与水平地面的夹角 1 , 2 ,根据人体

生理及运动特性,用比例 p判断人体处于何种活动(站立、坐下、下蹲),得出在该活动下

的 1 与 2 合理值的范围,若 1 和 2 从合理值急剧减小到 15°以下,判断为可能跌倒。

基于计算机视觉和语音交互的老人看护陪伴系统

第 14 页 共 74 页

表 3-1 正常活动状态下人体骨架向量与地面夹角合理范围

活动类型 p 值范围 1 合理范围 2 合理范围

站立活动 0.9 ~ 1.2 1 > 60° 90°左右

坐下活动 1.5 ~ 2.2 1 > 60° 40°左右

下蹲活动 2.5 ~ 3.5 70°< 1 <90° 90°左右

(2)特征二:重心下降速度

根据文献[31]中的结果,重心下降速度在 1.21m/s-2.05m/s 时人体可能跌倒,结合实验测

试结果,系统选取 1.37m/s 做为人体中心点下降速度的阈值。一旦超过该阈值,则判断为可

能跌倒。

系统每获取一帧图像就利用 OpenPose 提取出的人体关键点计算出人体重心坐标 xO ,

并记录此图像的拍摄时间 xT ,分别与其前第 10 帧的图像对应的重心坐标 10xO ,拍摄时间

10xT 和相减,得到重心垂直方向变化距离 yO ,时间间隔 T ,计算出重心下降速度 TV ,

若 TV >1.37m/s,则判断老人可能跌倒。

跌倒检测的总体流程如图 3-6 所示。

图 3-6 跌倒检测流程图

当姿态特征满足跌倒检测特征时,判断老人可能跌倒。此时通过语音询问老人是否摔

倒,如果接收到“是”的回应,或一分钟内老人无应答,自动调用求救系统向紧急联系人

求助。

3.2 语音识别

系统基于人工智能相关理论,运用 Kaldi 开源语音工具箱进行深度学习神经网络的边缘

计算与搭建,通过综合语音识别、合成、控制与交互以及网络、多媒体等技术,实现了与

老人的语音交互,方便老人调用行为监测、相册翻阅、音频播放、电话通信等功能。此外,

基于计算机视觉和语音交互的老人看护陪伴系统

第 15 页 共 74 页

系统中基于百度语音 API 和图灵机器人 API 的知心小雨机器人可以与老人进行日常聊天对

话,实现了交互陪伴的功能,给老人带来精神慰藉。

3.2.1 基于深度神经网络的语音识别

人工智能的基础是深度学习,而深度学习又是机器学习的一个分支,是一种使用多重线

性变换形成多个神经网络处理层复杂结构,进而对数据进行处理与高层抽象的算法[32]。基

于深度神经网络的语音识别系统能够有效地利用持续增长的数据量,提升识别性能,激发更

多的产业应用,打通语音数据的获取渠道,从而进一步优化模型。深度神经网络内部的神经

网络可以分为三类:输入层、隐藏层和输出层,如下图 3-7 所示。

图 3-7 语言识别深度神经网络内部结构图

在深度神经网络(DNN)中层与层是全连接的,其局部模型是一个线性关系:

)( bxWfy

(1)

其中 y代表输出向量, x代表输入向量,W 代表权重矩阵, f 代表激活函数,b代表

偏移向量。

3.2.2 基于 Kaldi 开源工具箱的语音识别

语音系统链路复杂,涉及技术模块多样,所需的领域知识点繁多,对工程优化的要求

高。随着语音算法的逐代升级,语音技术链路的相关研发工具也逐步成型完善,其中的代

表是 HTK 和 Sphinx 工具集,这两个工具集都能够完成从模型训练到产品原型搭建等一系列

工作,而最近几年来,新一代开源项目 Kaldi 逐步取代了 HTK 和 Sphinx,成为流行的开源

语音工具箱,对语音技术的普及和研发起到了举足轻重的作用。

(1)Kaldi 工具箱的系统架构

Kaldi 是一款基于 C++的开源语音识别系统,运行稳定、可嵌入本地平台,支持子空间

高斯混合模型、网络自适应技术和循环神经网络等高阶模型,还集成了 MFCC、LDA 等特

征提取手段,既可实现 CPU 多线程加速的深度神经网络 SGD 训练,也可运用 GPU、计算

棒等加速计算[33],其系统框架如图 3-8 所示。

基于计算机视觉和语音交互的老人看护陪伴系统

第 16 页 共 74 页

图 3-8 Kaldi 系统框架图

此外,Kaldi 以语音识别为核心进行全局设计:包含自成一派的文件 I/O 及存储、数据

处理流水线、模型训练流水线,整合了线性代数拓展与有限状态机(WFST)作为语音识别

解码的统一框架,并提供了离线/在线识别原型等,它的代码设计易于扩展、开放开源协议,

是一个完整的语言识别系统搭建工具。

(2)Kaldi 语音识别模型的训练流程

基于Kaldi工具箱的语音训练与识别系统主要包括四个部分:语音信号处理和特征提取、

声学模型(AM)、语言模型(LM)和解码搜索,如图 3-9 所示。Kaldi 的语音识别分为训练

和部署两个部分,训练部分主要基于依赖于语料库的 AM 模型和依赖于文本库的 LM 模型,

当两个模型的参数在训练过程中逐渐收敛,达到一定的模型参数便可用于模型部署,即语言

的解码和搜索。

图 3-9 Kaldi语音识别模型训练流程

此外,Intel 的 OpenVINOTM工具包中的模型优化器可以根据受过训练的网络拓扑,将

Kaldi 模型转化为优化中间表示(IR 模型),通过推理引擎示例应用程序在目标环境中测试

IR 模型,最后将推理引擎集成到本地嵌入式平台,实现模型的优化部署。

3.2.3 Kaldi 中的语音识别算法与模型

(1)子空间高斯混合模型(SGMM)

高斯混合模型(GMM)是单一高斯概率密度函数的延伸,能够平滑地近似任意形状的

密度分布,可以运用在说话人识别和语音识别等方面[34]。

基于计算机视觉和语音交互的老人看护陪伴系统

第 17 页 共 74 页

一个连续随机变量 X的混合高斯分布的概率密度函数为:

M

mmmmmm

M

m

x

m

m cxxNcecXf m

m

1

2

1

)(21

0;0; ),,;(2

)(

(2)

上式即为具有多个峰值分布的混合高斯分布,其混合权重的累加和等于 1,即 11

M

mmc 。

多元混合高斯分布的联合概率密度函数为:

M

mmmmm

M

m

xx

mD

m cxNcecxf mm

Tm

11

)()(21

0 ),,;(2

)(

1

(3)

由于高斯混合模型中参数非常多,因此提出了子空间高斯混合模型(SGMM),“子空

间”的意思是 GMM 的参数是整个 I 维混合模型的子空间。SGMM 的拓扑结构与 GMM 的

拓扑结构大致相同,仅在输出概率的参数上有所差异,SGMM 的输出概率公式如下:

),;()|(1

iji

I

iji xNjxp

(4)

jiji vM (5)

I

ij

Ti

jTi

ji

v

v

1exp

exp

(6)

这里 x是特征向量,j是状态的索引,所有的状态均有 I个高斯,每个高斯的均值、权

重通过与每个状态相关的矢量 jv 映射得到。

(2)隐马尔科夫模型(HMM)

隐马尔科夫算法(Hidden Markov Models)统一了语音识别中声学层和语音学层的算法结

构,以概率的形式将声学层中得到的信息和语音学层中已有的信息完美地结合在一起,极大

地增强了语音识别的效果。

HMM 模型分为两个过程:一个是具有有限状态的马尔科夫链来模拟语音信号统计特性

变化的隐含的随机过程,另一个是与马尔科夫链的每一个状态相关联的观测序列的随机过

程。

设马尔科夫链的状态空间是)( j

t sq ,一个马尔科夫链可被转移概率完全表示,其定义

如式(7)所示。

Njitpisqsqp ijtj

t ,,2,1, )),()(|( 1)( (7)

其观察概率 )|( )(it soP ,其中 i与观察向量 to 都是离散的,每个状态的概率分布对应于

观察对象 kv 的概率,如式(8)所示。

NisqvoPkb itkti ,,2,1 ),|()( (8)

在语音识别中,使用 HMM 的概率密度函数来描述观察向量的概率分布,运用 GMM 对

其建模可得:

基于计算机视觉和语音交互的老人看护陪伴系统

第 18 页 共 74 页

M

m

oo

iD

iti

mimit

Tmit

emcob1

)()(21 1

,,,

2)(

(9)

隐马尔科夫模型可以使用动态规划递归求解,进行模型参数的训练以最大化训练数据集

的概率,主要运用前后向算法和维特比算法。

(3)加权有限状态转换机(WFST)Kaldi 的 WFST 主要有声学模型 WFST、发音字典 WFST、上下文 WFST 和语音模型

WFST,它们存在于 HCLG.fst 文件中。

WFST 主要用于 ASR 解码,有限状态转换机定义为一个五元组:

),,,,( 0 FqEQA (10)

其中,Q是状态集合,是输入符号集合,E为转移集,其接收一个状态和输入符号,

输出一个目的状态或者空。 Qq 0 是初始状态, QF 是最终状态集或者接受状态集。

3.2.4 THCHS-30 中文数据集训练实例

清华大学 30 小时的数据集(THCHS-30)是清华大学语音与语言实验中心(CSLT)公

布的开源数据集,该数据集包含了语音数据及抄本、标准零分贝噪声测试数据以及训练好的

语言模型和相应词典等三个文件,又可分为训练集、开发集和测试集三个部分,可以实现中

文普通话的识别并转换为文字输出[35]。

(1)数据准备与预处理

数据准备脚本 local/thchs-30_data_prep.sh 主要工作是从三个下载的数据集文件

($thchs/data_thchs30)分别生成词序列(word.txt)、音素序列(phone.txt)、语音(wav.scp)、句子与说话人的映射(utt2spk)和说话人与句子的映射(spk2utt)。

(2)特征提取

提取 MFCC 特征,共分为两步,先通过 steps/make_mfcc.sh 提取 MFCC 特征,再通过

steps/compute_cmvn_stats.sh 计算倒谱均值和方差归一化。

使用标准的 13 维 MFCC 加上一阶和二阶导数训练单音素 GMM 系统,采用倒谱均值归

一化(CMN)来降低通道效应。然后基于具有由 LDA 和 MLLT 变换的特征的单音系统构造

三音 GMM 系统,最后的 GMM 系统用于为随后的 DNN 训练生成状态对齐。

基于 GMM 系统提供的对齐来训练 DNN 系统,特征是 40 维 FBank,并且相邻的帧由

11 帧窗口(每侧 5 个窗口)连接。连接的特征被 LDA 转换,其中维度降低到 200。然后应

用全局均值和方差归一化以获得 DNN 输入。DNN 架构由 4 个隐藏层组成,每个层由 1200个单元组成,输出层由 3386 个单元组成。基线 DNN 模型用交叉熵的标准训练。使用随机

梯度下降(SGD)算法来执行优化。将迷你批量大小设定为 256,初始学习率设定为 0.008(3)声学模型训练

提取特征之后,就要根据大量的特征样本获取发音的声学模型,THCHS-30 的脚本中有

单音素、三音素、DNN 等声学模型的训练,其训练脚本和功能分别为:

① train_mono.sh 用来训练单音子隐马尔科夫模型,一共进行 40 次迭代,每两次迭代进

行一次对齐操作。

② train_deltas.sh 用来训练与上下文相关的三音子模型。

③ train_lda_mllt.sh 用来进行线性判别分析和最大似然线性转换。

④ train_sat.sh 用来训练发音人自适应,基于特征空间最大似然线性回归。

⑤ run_dnn.sh 用来训练 DNN,包括 xent 和 MPE。

基于计算机视觉和语音交互的老人看护陪伴系统

第 19 页 共 74 页

⑥ train_dae.sh 用来实验基于 dae 的去噪效果。

(4)解码与在线识别

解码用于根据状态截取概率最大的音素或音素组合,数学上使用加权有限状态机

(WFST)解码,命令为:

local/thchs-30_decode.sh --mono true --nj $n "steps/decode.sh" exp/mono data/mfcc &(5)中文在线识别应用创建与运行

① 创建相关文件——从 voxforge 把 online_demo 拷贝到 thchs30 下,和 s5 同级,在

online_demo 目录下建 online-data 和 work 两个文件夹。online-data 下建 audio 和 models 目录,

audio 放要识别的 wav,在 models 目录下建 tri1 目录,将 s5 下/exp/下的 tri1 下的 final.mdl和 35.mdl 拷贝过去,把 s5/exp/tri1/graph_word 中的 words.txt 和 HCLG.fst 也复制到该目录。

② 修改脚本——修改 online_demo 下的 run.sh,使用自己训练的模型和连续语言识别;

修改模型的类型,即将 ac_model_type=tri2b_mmi 改成 ac_model_type=tri1,以便使用单因素

训练模型;更改命令调用格式,即指定 final.mdl 文件。

③ 运行 run.sh--test-mode live,实现在线语言识别。利用麦克风采集测试语言实时识别

结果,即将语音输入转换为文字输出显示。

最终获得的中文在线识别应用目录结构如图 3-10 所示。

图 3-10 中文在线识别应用目录结构图

3.2.5 基于百度语音 API 和图灵机器人 API 的语音识别与交互

(1)百度语音 API百度 AI 云平台适用于语音识别、合成、控制和对话等场景,不同场景有相同的 API Key

和 Secret Key,只要以 HTTPS 的方式向平台请求服务,就可以得到 AI 分析结果。百度 AI平台调用开放 API 需要使用 OAuth2.0 授权,需要在 URL 里包含 AccesssToken 参数,此参

数可以向授权服务地址发送请求来获取[36]。

为实现本地嵌入式平台与百度云端通信,创建应用并将对应的 API Key 和 Secret Key填写到 Python 代码中,通过编写程序,调用百度 REST API 给予的标准 HTTP 端口,按照

JSON 数据格式,将采集的语音数据(pcm、wav、amr、m4a 等格式,采样率 16000、8000,16bit 位深,单声道)上传百度 AI 云平台(语音识别请求地址:http://vop.baidu.com/server_api)进行识别,接收文字识别结果,并通过百度 AI 语音合成接口获得一段 MP3 进行语音播放,

基于计算机视觉和语音交互的老人看护陪伴系统

第 20 页 共 74 页

即实现了语音的识别与合成,调用流程如下图 3-11 所示。

图 3-11 百度 API 调用流程图

(2)图灵机器人 API图灵机器人是一个人工智能机器人开放平台,通过调用其 API 可以快速为本地系统接

入一款具备个性化身份属性特征、满足不同场景多轮对话及上下文对话的人工智能机器人,

从而实现与老人的对话式交互。图灵机器人 API 的调用方式与百度 API 类似,也可通过

Python 实现,不再赘述。

3.3 娱乐陪伴

独居老人日常生活中长时间独自在家中起居,无法与亲友进行直接联系,但是人带有

社会属性,生活中需要陪伴和关怀。系统考虑到独居老人情感陪护需求,通过各式多媒体

模块,为老人提供娱乐陪伴功能。

3.3.1 亲友实时通话

系统外接 GSM 模块,通过 RS232 接口与 AI-BoxX Gen.1 硬件平台相连。老人可选择系

统的亲友实时通话功能,系统将通过 GSM 模块自动连入移动通信网络,向老人亲友拨打电

话。GSM 模块如图 3-12 所示。该模块采用工业标准接口,工作频率为 850/900/1800/1900Mhz,内嵌 TCP/IP 协议,可以低功耗实现语音通话。

图 3-12 GSM模块实物图

3.3.2 音视频播放

系统带有音乐和视频播放功能,能够根据老人喜好,为老人播放感兴趣的音乐和视频,

放松身心。

3.3.3 怀旧照片翻阅

当老人阅尽千帆,容易怀念自己以前的事迹,心理学家把这种现象称为“回归心理”。

为了满足老人的怀旧情绪、通过对过去的积极感知来增强自我的连续性、并且统一过去和

现在的自我从而更好地适应生活,系统设置怀旧照片翻阅功能,独居老人可通过语音控制

翻阅怀旧照片,满足老人的“回归心理”。

3.4 人机交互

交互设计的核心是用户,出发点是用户需求,目的是创造和构建用户与产品或服务之

间有意义的关系,其本质是研究“人”与“机器”进行信息交流时的各种行为特征、认知

以及正确理解、正确使用和便于使用的问题[37]。考虑到系统与用户的交互需要是简单的、

反馈及时的、以及它是在家居生活场景下为老人提供服务的,因此本系统采用语音交互和

基于计算机视觉和语音交互的老人看护陪伴系统

第 21 页 共 74 页

触摸界面交互相结合的方式进行人机交互过程。交互界面如图 3-13 所示。

图 3-13 用户交互界面

3.4.1 语音交互

语音交互是最符合人类直觉的交互方式之一,它由人与人之间自然对话的交流方式衍生

而来,是负担最小和学习成本最低的一种交互方式[15]。本系统通过调用百度语音 API 使交

互方式更加自然简单化,降低老人使用难度,提高产品易用性。

3.4.2 触摸界面交互

触摸屏界面交互是用户通过触摸控制的方式与屏幕进行互动,是随着互联网+时代的来

临得以广泛使用的交互方式。当前的界面交互方式主要以多点触控技术为基础,用户通过触

控发出指令后,以界面呈现的视觉信息为主,声音、触感等为辅,给予用户相应的反馈,是

目前用户与智能设备进行交互的主流交互方式[15]。系统除语音交互外同时采用触摸界面交

互,旨在方便老人快速开启特点功能,提高效率。

3.5 健康数据记录

系统通过外设数字血压检测模块采集老人血压和心率等健康数据并形成测量日志记录。

该模块基于示波法降压测量原理开发而成的多参数输出模块,通过串口输出测量到的血压和

心率值,采用 UART 与 AI-BoxX Gen.1 硬件平台通信。

图 3-14 数字血压检测模块实物图

数字血压检测模块如图所示,主要由血压模块、气泵、电磁阀、放气阀、气路管件和袖

带等组成。气泵和电磁阀的额定电压为 DC6V,串口配置如表 3-2 所示。

基于计算机视觉和语音交互的老人看护陪伴系统

第 22 页 共 74 页

表 3-2 串口配置

波特率 数据位 停止位 校验位

9600 8 1 None

3.5.1 工作原理

示波法降压测量法原理如下:血压计使用气泵对袖带进行充气加压,利用充气袖带压迫

动脉血管,使动脉血管处于完全闭阻状态。随后开启放气阀,使袖带内压力缓慢下降。随着

袖带内压力的下降,动脉血管呈完全阻闭—渐开—全开的变化过程。降压过程中,动脉内压

力振幅大小变化趋势如图 3-15 所示。

图 3-15 动脉内压力振幅大小变化趋势

压力传感器采集大小变化的袖带内压力,将其转化为数字信号送入 MCU,通过软件辨

别动脉血流受阻过程中相应压力点,根据经验累积的软件算法得出人体的舒张压、收缩压和

平均压。

3.5.2 技术参数

(1)工作条件

·温度:5℃~40℃·相对湿度:18%~80%·大气压力:80kPa~105kPa·电源电压:DC 6V;3.7V 锂电池(充满电在 3.7V~4.2V 区间使用)

(2)最大功耗:不大于 4.5VA(3)量程/采集范围:0~39.9kPa(299mmHg)

3.6 家庭环境监测

系统通过 PMS5003T 环境传感器实现家庭环境温湿度和空气质量等数据的监测,助力

智慧家居。PMS5003T 环境传感器如图所示,其可以同时监测空气中颗粒物浓度和温湿度,

其中颗粒物浓度的监测基于激光散射原理,可连续采集并计算单位体积内空气中不同粒径的

悬浮颗粒物个数,即颗粒物浓度分布,进而换算成为质量浓度。传感器同时内嵌温湿度一体

检测芯片。颗粒物浓度数值和温度、湿度数值合并以通用数字接口形式输出。

基于计算机视觉和语音交互的老人看护陪伴系统

第 23 页 共 74 页

图 3-16 PMS5003T环境传感器

PMS5003T 环境传感器采用激光散射原理。即令激光照射在空气中的悬浮颗粒物上产生

散射,同时在某一特定角度收集散射光,得到散射光强度随时间变化的曲线。进而微处理器

基于米氏(MIE)理论的算法,得出颗粒物的等效粒径及单位体积内不同粒径的颗粒物数量。

传感器的各功能部分框图如图 3-17 所示。

图 3-17 PMS5003T环境传感器功能框图

PMS5003T 环境传感器采用 UART 与 AI-BoxX Gen.1 硬件平台通信,各接口定义如表所

示。

表 3-2 各接口定义

引脚 定义 功能描述

PIN1 VCC 电源正(+5V)

PIN2 GND 接地

PIN3 SET设置管脚/TTL 电平@3.3V,高电平或悬空为

正常工作状态,低电平为休眠状态

PIN4 RXD 串口接收管脚/TTL [email protected]

PIN5 TXD 串口发送管脚/TTL [email protected]

PIN6 RESET 模块复位信号/TTL 电平@3.3V,低复位

PIN7 NC —

PIN8 NC —

基于计算机视觉和语音交互的老人看护陪伴系统

第 24 页 共 74 页

第四章 系统测试

4.1 系统测试方案

由于条件有限,本系统测试均在实验室环境中完成。测试过程可分为跌倒检测测试、

语音交互测试和外设模块测试。首先将摄像头接入系统平台,进行跌倒检测的测试,分析

跌倒检测准确率。接着对于语音交互功能进行测试,分析语音识别精度。最后对系统其它

功能模块进行测试,分析各模块可行性,测试系统整体性能。

4.2 测试设备

·基于计算机视觉和语音交互的老人看护陪伴系统(本作品),如图 4-1 所示。

·摄像头三脚架

·瑜伽垫

图 4-1 测试设备实物图

4.3 跌倒检测测试

在光线较好的实验室中,采用三脚架将摄像头支起一定高度,用于模拟家庭安防监控

摄像头,将摄像头与系统通过 USB 接口相连。在实验室中铺好瑜伽垫用于安全防护,三位

测试者首先在无遮挡条件下依次在距离摄像头约 5m、10m、15m 处以不同姿势进行 20 次跌

倒演示,随后在部分遮挡条件下重复一次演示,采用摄像头进行视频拍摄记录作为跌倒检

测测试数据集。

系统在 Ubuntu18.04 环境下利用硬件平台 CPU 单独测试跌倒检测功能程序,得到视频

处理平均约为每秒 16fps,CPU 占有率平均约为 38%。测试统计数据如表 4-1 所示。

表 4-1 跌倒检测测试成功率数据统计表(CPU)

测试对象

准确率

无遮挡 部分遮挡

距离 5m 距离 10m 距离 15m 距离 5m 距离 10m 距离 15m甲 90% 90% 75% 80% 85% 85%乙 85% 90% 85% 85% 85% 75%丙 90% 95% 85% 85% 90% 75%

基于计算机视觉和语音交互的老人看护陪伴系统

第 25 页 共 74 页

随后利用硬件平台 AI 卡(HDDL-R8)测试跌倒检测功能程序,得到视频帧率平均约为

每秒 15fps,CPU 占有率平均约为 18%。测试统计数据如表 4-2 所示。

表 4-2 跌倒检测测试成功率数据统计表(HDDL)

测试对象

准确率

无遮挡 部分遮挡

距离 5m 距离 10m 距离 15m 距离 5m 距离 10m 距离 15m甲 90% 90% 80% 80% 90% 80%乙 90% 100% 85% 85% 85% 85%丙 95% 95% 85% 85% 85% 80%

4.4 语音交互测试

考虑到独居老人在家中时有较大噪音情况出现的几率较小,因此系统主要在较安静条

件下进行语音交互测试。项目邀请了三位测试者依次测试 20 次,测试数据如表 4-3 所示。

表 4-3 语音交互测试数据统计表

测试对象准确率

语音唤醒 语音命令交互 紧急呼救

甲 95% 90% 95%乙 100% 90% 100%丙 100% 85% 100%

4.5 外设模块测试

由于系统外接了数个模块电路,需要对这些外设模块逐一进行测试。

4.5.1 GSM 模块

连接好各信号线,GSM 模块上电开始运行。在 GSM 模块上插上一张 SIM 卡,手机号

为 150****6168,然后在 Ubuntu18.04 系统中通过串口发送 AT 指令,测试 GSM 模块通话功

能,分别向 10 个测试者手机拨打电话,测试数据如表 4-4 所示。

表 4-4 GSM 模块通话功能测试数据统计表

电话号码 测试结果

187****8682 拨号成功

130****9392 拨号成功

187****2711 拨号成功

186****1508 拨号成功

131****3383 拨号成功

136****1508 拨号成功

131****1618 拨号成功

187****2586 拨号成功

188****8396 拨号成功

158****8041 拨号成功

4.5.2 数字血压采集模块

连接好各信号线,数字血压采集模块上电开始运行。对三位测试者分别进行 3 次健康

数据采集,测试数据如表 4-5 所示。

基于计算机视觉和语音交互的老人看护陪伴系统

第 26 页 共 74 页

表 4-5 数字血压采集模块测试数据统计表

测试

对象

第一次测试 第二次测试 第三次测试

高压 低压 心率 高压 低压 心率 高压 低压 心率

甲 115 82 69 122 84 74 116 78 70乙 112 76 73 117 80 69 122 79 72丙 125 84 78 121 83 74 126 87 82

4.6 结果分析

测试结果表明,系统各模块运行效果良好。对于跌倒检测算法,在硬件平台 CPU 下准

确率约为 87%,经 AI 视觉加速卡加速后,准确率约为 90%,且加速前后 CPU 占有率从 38%下降到 18%,展示了硬件平台 AI 卡强大的视觉处理能力。语音交互部分采用百度语音 API,识别精度能够达 90%以上。

基于计算机视觉和语音交互的老人看护陪伴系统

第 27 页 共 74 页

第五章 总结与展望

5.1 系统特色

(1)多技术融合,稳定高效

系统融入人体姿态估计、语音识别、多媒体、通信等技术,基于人体关键点设计了跌倒

检测算法,鲁棒性较高,稳定性较好。

(2)体现对独居老人群体的关注

当前针对独居老人群体设计的产品较少,而在当前社会背景下,独居老人居家助老产品

需求较大,本系统针对独居老人而设计,降低了老人的学习成本,具有高度的人性化。

(3)硬件简单,成本低廉

系统采用的多为当前较成熟的外设模块,摄像头要求并不是十分严格,配合边缘计算算

法,具有较高的性价比。

(4)充分挖掘系统资源提高系统性能

在系统实现时,深度挖掘 AI-BoxX Gen.1 硬件平台的资源,充分利用 AI 卡强大的视觉

处理加速功能,对程序进行优化,有效降低 CPU 负载,提高系统性能。

(5)交叉应用潜力大

本系统主要基于家庭安防监控摄像头进行设计,旨在智慧养老、智慧家居领域提供见解,

有潜力与其他家居产品进行融合,打造全新家居生态,也有潜力在智慧安防等领域发挥重要

作用。

5.2 前景展望

(1)在技术发展趋势上,系统综合运用计算机视觉和语音交互技术,深度融入当前边

缘计算发展浪潮。而计算机视觉和语音交互领域方兴未艾,是最受创投圈欢迎的人工智能

技术,也是各大企业积极布局以及投资者极为看好的方向。随着人工智能和物联网技术的

发展,海量数据需要快速有效的提取和分析,这大大加强了对于边缘计算的需求,未来 AI技术、边缘计算和物联网将更加密切进行融合发展,而且随着芯片技术的发展,先进硬件

将为人工智能模型的速度提供更高的支持来进行复杂的计算。因此,系统计算性能仍将有

较大的提升潜能,有机会为独居老人提供更加安全可靠的服务。

(2)在政策支持上,在当前社会背景下,养老助老产业具有广阔的市场需求和良好的

发展前景,智慧健康养老行业的国家政策频发,为养老产品市场发展提供了有力的催化剂。

而项目融入人工智能+边缘计算,在人工智能领域,我国已系统布局,整体部署我国的人工

智能发展规划,抢抓人工智能发展的战略机遇,为行业发展带来及时雨,有力推动人工智

能产业进步。由此可见,国家政策上为产业发展提供了相应的支持,项目设计内容符合社

会发展需要,符合国家战略支持,具有丰富的时代背景和深刻的现实意义,发展前景广阔。

基于计算机视觉和语音交互的老人看护陪伴系统

第 28 页 共 74 页

参考文献

[1] 国家统计局. http://data.stats.gov.cn/easyquery.htm?cn=C01[2] 王亚飞. 视频监控系统中的跌倒异常行为检测技术研究[D].南京邮电大学,2014.[3] 郭曌睿. 室内监控系统下老人异常行为识别算法研究[D].西安电子科技大学,2019.[4] 郑巧英,张小霞,陈雪莲.基于马斯洛需求层次原理的养老服务标准体系研究[J].标准科

学,2020(03):77-81.[5] 胡爱敏.高速老龄化背景下我国养老服务的着力点——以马斯洛需求层次理论为观照[J].

中共福建省委党校学报,2012(12):92-97.[6] Fischinger D, Einramhof P, Papoutsakis K, et al. Hobbit, a care robot supporting independent

living at home: First prototype and lessons learned[J]. Robotics and Autonomous Systems,2016, 75(PA): 60-78.

[7] Balaguer C, Gimenez A. Jardon A.et al. Live experimentation of the service robot applicationsfor elderly people care in home environments [C]. In 2005 IEEE/RSJ InternationalConference on Intelligent Robots and Systems,2005:2345-2350.

[8] 张皓. 老龄化服务机器人设计研究[D].北京邮电大学,2017.[9] 雷尚仲. 面向空巢老人居家养老的智能电视系统交互设计研究[D].华南理工大学,2017.[10] 刘美堂. 家用保安机器人避障传感单元研制及路径规划研究[D].哈尔滨工业大学,2007.[11] 中科院研制监控机器人协助家庭安防建设[J].机械,2014,41(01):35.[12] 孙庆杰,吴恩华.基于矩形拟合的人体检测[J].软件学报,2003(08):1388-1393.[13] 田广,戚飞虎,朱文佳,毛欣,陈磐君.单目移动拍摄下基于人体部位的行人检测[J].系统仿

真学报,2006(10):2906-2910.[14] 赵宗玉.人工智能技术现状剖析[J].中国安防,2020(03):29-33.[15] 吕秋萍. 家用智能助老服务机器人产品概念设计研究[D].浙江工业大学,2019.[16] 许晓云,田静.适用于老年人的无障碍智能家居产品设计研究[J].设计,2013(06):46-47.[17] Bajcsy R, Chen J, Kwong K, et al. Fall detection using wireless sensor networks[C],2005.[18] Doukas C, Maglogiannis I, Tragas P, et al. Patient fall detection using support vector

machines[M]. Artificial Intelligence and Innovations 2007: from Theory to Applications.Springer US, 2007: 147-156.

[19] Wibisono W, Arifin D N, Pratomo B A, et al. Falls Detection and Notification System UsingTri-axial Accelerometer and Gyroscope Sensors of a Smartphone[C]. Conference onTechnologies and Applications of Artificial Intelligence. IEEE Computer Society,2013:382-385.

[20] 薛源. 基于多传感器的老人跌倒检测系统的研究与应用[D].武汉理工大学,2011.[21] Alwan M, Rajendran P J, Kell S, et al. A smart and passive floor-vibration based fall detector

for elderly[C]. Information and Communication Technologies, 2006. ICTTA'06. 2nd. IEEE,2006, 1: 1003-1007.

[22] Cheffena M. Fall Detection using Smartphone Audio Features.[J]. IEEE Journal ofBiomedical & Health Informatics, 2016, 20(4):1073.

[23] 王立敏.模块化的可拼接的跌倒检测地毯:中国,201320021788.5[P].2013-07-31.[24] Rougier, Meunier, St Arnaud. Monocular 3D head tracking to detect falls of elderly people.[J].

基于计算机视觉和语音交互的老人看护陪伴系统

第 29 页 共 74 页

2006, 1(1):6384-6387.[25] Hoai M, Lan Z Z, De la Torre F. Joint segmentation and classification of human actions

invideo[C]. Computer Vision and Pattern Recognition (CVPR), 2011 IEEE Conference onIEEE, 2011: 3265-3272.

[26] Chua J L, Chang Y C, Lim W K. A simple vision-based fall detection technique for indoorvideo surveillance[J]. Signal, Image and Video Processing, 2015, 9(3):623-633.

[27] 沈秉乾. 基于视频分析的室内人体跌倒检测方法的实现[D].华南理工大学,2013.[28] 兰天宇. 室内意外跌落智能报警技术研究[D].天津大学,2018.[29] 翟远. 基于多传感器的老年人日常动作识别与跌倒检测研究[D].武汉纺织大学,2017[30] 刘国帅, 熊平. 基于三个特征点的人体跌倒检测[J]. 科技视界, 2015, 000(021):5-6.[31] Sasidhar S , Panda S K , Xu J . A Wavelet Feature Based Mechanomyography Classification

System for a Wearable Rehabilitation System for the Elderly[C]// International Conference onSmart Homes and Health Telematics. Springer Berlin Heidelberg, 2013.

[32] 杨胜捷, 朱灏耘, 冯天祥, et al. 基于 Kaldi 的语音识别算法[J]. 电脑知识与技术, 2019,015(002):163-166.

[33] 朱春山. 基于 Kaldi 的语音识别的研究[D].[34] 陈果果 ,都家宇 ,那兴宇 ,张俊博等 . Kaldi 语言识别实战 [M]. 北京 :电子工业出版

社,2020:04.[35] 葛世超,吕强,钱思冲,张博伦,张硕等. 实时语言处理实践指南[M]. 北京:电子工业出版

社,2020:05.[36] 牛可. 智能家居语音识别通用语音 AI 云平台的设计与实现[D]. 2019.[37] 唐磊. 交互设计的发展现状与趋势[J]. 长春教育学院学报,2012,28(4).56-57

基于计算机视觉和语音交互的老人看护陪伴系统

第 30 页 共 74 页

附录

1 跌倒检测

1-1.跌倒检测主函数文件

// Copyright (C) 2018-2019 Intel Corporation// SPDX-License-Identifier: Apache-2.0///*** \brief The entry point for the Inference Engine Human Pose Estimation demo application* \file human_pose_estimation_demo/main.cpp* \example human_pose_estimation_demo/main.cpp*/#include<cmath>#include <string>#include <vector>#include <chrono>

#include <inference_engine.hpp>

#include <monitors/presenter.h>#include <samples/ocv_common.hpp>

#include "human_pose_estimation_demo.hpp"#include "human_pose_estimator.hpp"#include "render_human_pose.hpp"

using namespace InferenceEngine;using namespace human_pose_estimation;//全局变量

int timelength = 0;//循环数组长度

bool falldownflag=false;//判断是否摔倒了

double* pointarr = new double[36];double*** timescale = new double**[20];//时空循环指针数组

double* timearr = new double[20];//记录与时空循环指针数组对应的时间节点

double** persons = new double*[5];//最多同时读取三人的信息

const int personnum = 5;//同时读取的人的数量

char globaltemp[10];//用于返回关节编号对应的数据

int arrlength=0;//记录读取的数组数

//flags

基于计算机视觉和语音交互的老人看护陪伴系统

第 31 页 共 74 页

bool speedfallflag[5];//按重心下降速度判断是否摔倒

bool posefallflag[5];//按姿势判断是否摔倒

//函数声明

char* translate(int k);//将关节编号翻译成数据

double distances(double x1,double y1,double x2,double y2);//计算两点间的距离

double getdeg(double x1,double y1,double x2,double y2);//获取与地面之间的夹角

void takemeddetect();//服药检测

void weightmovetest();//重心移动检测

bool getweightpoint(double* temppointarr);//获取重心坐标

double weightpoint[2];//重心坐标

double getscale(double* temppointarr);//获取 scale 值

bool isusable(double* arr);//判断一组数据是否无效

bool isusable(double* arr){for(int k=0;k<36;k++){

if(arr[k]!=-1) return true;}return false;

}//获取 scale 值

double getscale(double* temppointarr){return temppointarr[36];

}//获取与地面之间的夹角

double getdeg(double x1,double y1,double x2,double y2){if((x1-x2)!=0){

return atan((y2-y1)/abs(x1-x2))*180/3.1415;}else{

//直角

return 90;}

}double distances(double x1,double y1,double x2,double y2){

return sqrt(pow(x1-x2,2)+pow(y1-y2,2));}//获取重心坐标

bool getweightpoint(double* temppointarr){if(temppointarr[11*2]>0&&temppointarr[8*2]>0){

weightpoint[0] = (temppointarr[11*2]+temppointarr[8*2])/2;weightpoint[1] = (temppointarr[11*2+1]+temppointarr[8*2+1])/2;

}else if(temppointarr[11*2]>0){weightpoint[0] = temppointarr[11*2];weightpoint[1] = temppointarr[11*2+1];

}else if(temppointarr[8*2]>0){、

基于计算机视觉和语音交互的老人看护陪伴系统

第 32 页 共 74 页

weightpoint[0] = temppointarr[8*2];weightpoint[1] = temppointarr[8*2+1];

}else{return false;

}return true;

}//判断是否满足特征一

void falltest(double* temppointarr,int id){//获取关键点

double top_x;double top_y;double weight_x;double weight_y;double foot_x;double foot_y;bool useknee=false;//头部代表

if(temppointarr[0]>0){top_x = temppointarr[0];top_y = temppointarr[1];

}else if(temppointarr[28]>0&&temppointarr[30]>0){top_x = (temppointarr[28]+temppointarr[30])/2;top_y = (temppointarr[29]+temppointarr[31])/2;

}else if(temppointarr[32]>0&&temppointarr[34]>0){top_x = (temppointarr[32]+temppointarr[34])/2;top_y = (temppointarr[33]+temppointarr[35])/2;

}else if(temppointarr[10]>0&&temppointarr[4]>0){top_x = (temppointarr[10]+temppointarr[4])/2;top_y = (temppointarr[11]+temppointarr[5])/2;

}else{return;

}//腹部代表

if(!getweightpoint(temppointarr)) return;else{

weight_x = weightpoint[0];weight_y = weightpoint[1];

}//小腿代表

if(temppointarr[13*2]>0&&temppointarr[10*2]>0){foot_x = (temppointarr[13*2]+temppointarr[10*2])/2;foot_y = (temppointarr[13*2+1]+temppointarr[10*2]+1)/2;

基于计算机视觉和语音交互的老人看护陪伴系统

第 33 页 共 74 页

}else if(temppointarr[13*2]>0){foot_x = temppointarr[13*2];foot_y = temppointarr[13*2+1];

}else if(temppointarr[10*2]>0){foot_x = temppointarr[10*2];foot_y = temppointarr[10*2+1];

}else if(temppointarr[24]>0&&temppointarr[18]>0){foot_x = (temppointarr[24]+temppointarr[18])/2;foot_y = (temppointarr[25]+temppointarr[19])/2;useknee = true;

}else if(temppointarr[24]>0){foot_x = temppointarr[24];foot_y = temppointarr[25];useknee = true;

}else if(temppointarr[18]>0){foot_x = temppointarr[18];foot_y = temppointarr[19];useknee = true;

}else{return;

}double d1;double d2;if(useknee){

d1 = distances(top_x,top_y,weight_x,weight_y);d2 = 2*distances(weight_x,weight_y,foot_x,foot_y);

}else{d1 = distances(top_x,top_y,weight_x,weight_y);d2 = distances(weight_x,weight_y,foot_x,foot_y);

}double p = d1/d2;double deg1 = getdeg(top_x,top_y,weight_x,weight_y);double deg2 = getdeg(weight_x,weight_y,foot_x,foot_y);if(deg1<25&&deg2<25){

//老人跌倒,调用报警函数

alarm()posefallflag[id] = true;falldownflag = true;

}else{falldownflag = false;posefallflag[id] = false;

}}

基于计算机视觉和语音交互的老人看护陪伴系统

第 34 页 共 74 页

//多人检测

void falltests(){for(int k=0;k<personnum;k++){

falltest(persons[k],k);}

}//重心移动检测

void weightmovetest(){if(arrlength<6) return;double** nowpoint = timescale[(timelength+19)%20];double** agopoint = timescale[(timelength+14)%20];double timetin = timearr[(timelength+14)%20]-timearr[(timelength+19)%20];//每个人单独判断

for(int k=0;k<personnum;k++){getweightpoint(nowpoint[k]);double now_y=weightpoint[1];getweightpoint(agopoint[k]);double ago_y=weightpoint[1];double y_move = ago_y*getscale(agopoint[k])-now_y*getscale(nowpoint[k]);double speed = y_move/100/(timetin/1000);if(speed>1.37){

speedfallflag[k] = true;}else{

speedfallflag[k] = false;}

}}bool ParseAndCheckCommandLine(int argc, char* argv[]) {

// ---------------------------Parsing and validation of input args--------------------------------------

gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);if (FLAGS_h) {

showUsage();showAvailableDevices();return false;

}

std::cout << "Parsing input parameters" << std::endl;

if (FLAGS_i.empty()) {throw std::logic_error("Parameter -i is not set");

}

基于计算机视觉和语音交互的老人看护陪伴系统

第 35 页 共 74 页

if (FLAGS_m.empty()) {throw std::logic_error("Parameter -m is not set");

}

return true;}

int main(int argc, char* argv[]) {//数据初始化

for(int k=0;k<20;k++){double** temppoint = new double*[5];//再给每个人申请空间

for(int j=0;j<personnum;j++){double* temppersonpoint = new double[37];temppoint[j] = temppersonpoint;

}timescale[k] = temppoint;

}for (int k=0;k<personnum;k++){

posefallflag[k] = false;speedfallflag[k] = false;double* temppoint = new double[37];//一个包含 37 个浮点数的数组,记录 18 个关键

点的坐标,以及,画面比例转化比,测量改点的时间结点

persons[k] = temppoint;}try {

std::cout << "InferenceEngine: " << GetInferenceEngineVersion() << std::endl;

// ------------------------------ Parsing and validation of input args---------------------------------

if (!ParseAndCheckCommandLine(argc, argv)) {return EXIT_SUCCESS;

}

HumanPoseEstimator estimator(FLAGS_m, FLAGS_d, FLAGS_pc);cv::VideoCapture cap;if (!(FLAGS_i == "cam" ? cap.open(0) : cap.open(FLAGS_i))) {

throw std::logic_error("Cannot open input file or camera: " + FLAGS_i);}

int delay = 33;

// read input (video) frame

基于计算机视觉和语音交互的老人看护陪伴系统

第 36 页 共 74 页

cv::Mat curr_frame; cap >> curr_frame;cv::Mat next_frame;if (!cap.grab()) {

throw std::logic_error("Failed to get frame from cv::VideoCapture");}

estimator.reshape(curr_frame); // Do not measure network reshape, if it happened

std::cout << "To close the application, press 'CTRL+C' here";if (!FLAGS_no_show) {

std::cout << " or switch to the output window and press ESC key" << std::endl;std::cout << "To pause execution, switch to the output window and press 'p' key"

<< std::endl;}std::cout << std::endl;

cv::Size graphSize{static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH) / 4),60};

Presenter presenter(FLAGS_u,static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT)) - graphSize.height - 10,graphSize);

std::vector<HumanPose> poses;bool isLastFrame = false;bool isAsyncMode = false; // execution is always started in SYNC modebool isModeChanged = false; // set to true when execution mode is changed

(SYNC<->ASYNC)bool blackBackground = FLAGS_black;

typedef std::chrono::duration<double, std::ratio<1, 1000>> ms;auto total_t0 = std::chrono::high_resolution_clock::now();auto wallclock = std::chrono::high_resolution_clock::now();double render_time = 0;

//while 循环中处理视频数据

while (true) {//记录开始工作的时刻点

auto t0 = std::chrono::high_resolution_clock::now();//here is the first asynchronus point://in the async mode(异步模式) we capture frame to populate(填入) the NEXT

infer request//一般都不为异步模式,所以 isAsyncMode==0//in the regular mode(一般模式) we capture frame to the current infer requestif (!cap.read(next_frame)) {

基于计算机视觉和语音交互的老人看护陪伴系统

第 37 页 共 74 页

if (next_frame.empty()) {isLastFrame = true; //end of video file

} else {throw std::logic_error("Failed to get frame from cv::VideoCapture");

}}

//非异步,此段不执行

if (isAsyncMode) {if (isModeChanged) {

estimator.frameToBlobCurr(curr_frame);}if (!isLastFrame) {

estimator.frameToBlobNext(next_frame);}

} else if (!isModeChanged) {estimator.frameToBlobCurr(curr_frame);

}

//记录时间点,推算出解码时间

auto t1 = std::chrono::high_resolution_clock::now();double decode_time = std::chrono::duration_cast<ms>(t1 - t0).count();

//记录解码完成后的时间点

t0 = std::chrono::high_resolution_clock::now();// Main sync point:// in the trully Async mode we start the NEXT infer request, while waiting for the

CURRENT to complete// in the regular mode we start the CURRENT request and immediately wait for it's

completion

//非异步,此段不执行

if (isAsyncMode) {if (isModeChanged) {

estimator.startCurr();}if (!isLastFrame) {

estimator.startNext();}

} else if (!isModeChanged) {estimator.startCurr();

}

基于计算机视觉和语音交互的老人看护陪伴系统

第 38 页 共 74 页

//下面这一大段主要是计算性能,并打印出来

if (estimator.readyCurr()) {t1 = std::chrono::high_resolution_clock::now();ms detection = std::chrono::duration_cast<ms>(t1 - t0);t0 = std::chrono::high_resolution_clock::now();ms wall = std::chrono::duration_cast<ms>(t0 - wallclock);wallclock = t0;

t0 = std::chrono::high_resolution_clock::now();

//显示性能面板,执行以下打印代码

if (!FLAGS_no_show) {if (blackBackground) {

curr_frame = cv::Mat::zeros(curr_frame.size(), curr_frame.type());}std::ostringstream out;//模型推断所花时间

out << "OpenCV cap/render time: " << std::fixed << std::setprecision(2)<< (decode_time + render_time) << " ms";

cv::putText(curr_frame, out.str(), cv::Point2f(0, 25),cv::FONT_HERSHEY_TRIPLEX, 0.6, cv::Scalar(0, 255,

0));out.str("");out << "Wallclock time " << (isAsyncMode ? "(TRUE ASYNC):

" : "(SYNC, press Tab): ");out << std::fixed << std::setprecision(2) << wall.count()

<< " ms (" << 1000.f / wall.count() << " fps)";cv::putText(curr_frame, out.str(), cv::Point2f(0, 50),

cv::FONT_HERSHEY_TRIPLEX, 0.6, cv::Scalar(0, 0,255));

if (!isAsyncMode) { // In the true async mode, there is no way tomeasure detection time directly

out.str("");//模型检测所花时间

out << "Detection time : " << std::fixed << std::setprecision(2) <<detection.count()

<< " ms ("<< 1000.f / detection.count() << " fps)";cv::putText(curr_frame, out.str(), cv::Point2f(0, 75),

cv::FONT_HERSHEY_TRIPLEX, 0.6,cv::Scalar(255, 0, 0));

}

基于计算机视觉和语音交互的老人看护陪伴系统

第 39 页 共 74 页

}//此处调用骨架推断模型

poses = estimator.postprocessCurr();

//FLAGS_r==false,此段代码不执行

if (FLAGS_r) {if (!poses.empty()) {

std::time_t result = std::time(nullptr);char timeString[sizeof("2020-01-01 00:00:00: ")];std::strftime(timeString, sizeof(timeString),

"%Y-%m-%d %H:%M:%S: ", std::localtime(&result));std::cout << timeString;

}

for (HumanPose const& pose : poses) {std::stringstream rawPose;rawPose << std::fixed << std::setprecision(0);for (auto const& keypoint : pose.keypoints) {

rawPose << keypoint.x << "," << keypoint.y << " ";}rawPose << pose.score;std::cout << rawPose.str() << std::endl;

}}

//此处根据模型推断结果绘制图像,并在视频上显示

if (!FLAGS_no_show) {presenter.drawGraphs(curr_frame);auto nowtime = std::chrono::high_resolution_clock::now();ms curtime = std::chrono::duration_cast<ms>(nowtime - total_t0);//此处记录关键点

double** personpoint = timescale[timelength];timearr[timelength] = curtime.count();//最后记录时间结点

timelength++;timelength = timelength%20;arrlength++;

//std::cout << "Detection time : " << std::fixed << std::setprecision(2)<< .count()<<std::endl;

renderHumanPose(poses, curr_frame,curtime.count(),persons);int usablenum=0;//可用数据数量

for(int k=0;k<personnum;k++){double* temppointarr = persons[k];

基于计算机视觉和语音交互的老人看护陪伴系统

第 40 页 共 74 页

if(!isusable(temppointarr)) break;usablenum++;

}//将 persons 里的数据传进 timescale 的一个时刻里

for(int k=0;k<usablenum;k++){double* temppointarr = personpoint[k];double* tempperson = persons[k];for(int j=0;j<37;j++){

temppointarr[j] = tempperson[j];}

}

//判断是否跌倒

for(int k=0;k<usablenum;k++){if(posefallflag[k]){

//老人满足特征一跌倒调用报警函数

alarm();}if(speedfallflag[k]){

//老人满足特征二跌倒调用报警函数

alarm();}

}cv::imshow("Human Pose Estimation on " + FLAGS_d, curr_frame);t1 = std::chrono::high_resolution_clock::now();render_time = std::chrono::duration_cast<ms>(t1 - t0).count();

}falltests();//检测特征一

weightmovetest();//检测特征二

}

if (isLastFrame) {break;

}

if (isModeChanged) {isModeChanged = false;

}

// Final point:// in the truly Async mode we swap the NEXT and CURRENT requests for the next

iterationcurr_frame = next_frame;

基于计算机视觉和语音交互的老人看护陪伴系统

第 41 页 共 74 页

next_frame = cv::Mat();if (isAsyncMode) {

estimator.swapRequest();}

//键盘控制函数

const int key = cv::waitKey(delay) & 255;if (key == 'p') {

delay = (delay == 0) ? 33 : 0;} else if (27 == key) { // Esc

break;} else if (9 == key) { // Tab

isAsyncMode ^= true;isModeChanged = true;

} else if (32 == key) { // SpaceblackBackground ^= true;

}presenter.handleKey(key);

}

//计算并打印总视频推断时间(摄像头不执行)

auto total_t1 = std::chrono::high_resolution_clock::now();ms total = std::chrono::duration_cast<ms>(total_t1 - total_t0);std::cout << "Total Inference time: " << total.count() << std::endl;std::cout << presenter.reportMeans() << '\n';

}//错误处理

catch (const std::exception& error) {std::cerr << "[ ERROR ] " << error.what() << std::endl;return EXIT_FAILURE;

}catch (...) {

std::cerr << "[ ERROR ] Unknown/internal exception happened." << std::endl;return EXIT_FAILURE;

}

std::cout << "Execution successful" << std::endl;return EXIT_SUCCESS;

}1-2 人体关键点推测与记录文件

// Copyright (C) 2018-2019 Intel Corporation// SPDX-License-Identifier: Apache-2.0//

基于计算机视觉和语音交互的老人看护陪伴系统

第 42 页 共 74 页

#include<cmath>#include<string>#include <utility>#include <vector>

#include <opencv2/imgproc/imgproc.hpp>#include <fstream>#include <iostream>#include "human_pose_estimator.hpp"#include "render_human_pose.hpp"//全局变量

double temppointarr[18][2];double curpointarr[18][2];//函数声明

double distance(double x1,double y1,double x2,double y2);//计算两点间的距离

void zerotemparr();//将临时数组重置

void zerocurarr();//将当前数组重置

double calheight();//计算人的大致高度

bool exist(double k);//判断某个关键点是否存在

//将数组重置

//计算两点间的距离

double distance(double x1,double y1,double x2,double y2){return sqrt(pow(x1-x2,2)+pow(y1-y2,2));

}void zerotemparr(){

for(int k =0;k<18;k++){temppointarr[k][0]=-1.0;temppointarr[k][1]=-1.0;

}}//将当前数组重置

void zerocurarr(){for(int k =0;k<18;k++){

curpointarr[k][0]=-1.0;curpointarr[k][1]=-1.0;

}}//计算人的大致高度

double calheight(){//如果脖子和臀部都检测到,直接计算

if(exist(temppointarr[1][1])&&exist(temppointarr[11][1])){return

distance(temppointarr[1][0],temppointarr[1][1],temppointarr[11][0],temppointarr[11][1])*3;

基于计算机视觉和语音交互的老人看护陪伴系统

第 43 页 共 74 页

}else if(exist(temppointarr[1][1])&&exist(temppointarr[8][1])){return

distance(temppointarr[1][0],temppointarr[1][1],temppointarr[8][0],temppointarr[8][1])*3;}//如果脖子和膝盖都检测到

if(exist(temppointarr[1][1])&&exist(temppointarr[12][1])){return

distance(temppointarr[1][0],temppointarr[1][1],temppointarr[12][0],temppointarr[12][1])*2;}else if(exist(temppointarr[1][1])&&exist(temppointarr[9][1])){

returndistance(temppointarr[1][0],temppointarr[1][1],temppointarr[9][0],temppointarr[9][1])*2;

}return 0.0;

}//判断某个点是否存在

bool exist(double k){return k!=-1.0;

}namespace human_pose_estimation {void renderHumanPose(const std::vector<HumanPose>& poses, cv::Mat& image,doublecurtime,double** persons) {

CV_Assert(image.type() == CV_8UC3);//std::cout <<"当前时间是"<< curtime/1000<<"s"<<std::endl;static const cv::Scalar colors[HumanPoseEstimator::keypointsNumber] = {

cv::Scalar(255, 0, 0), cv::Scalar(255, 85, 0), cv::Scalar(255, 170, 0),cv::Scalar(255, 255, 0), cv::Scalar(170, 255, 0), cv::Scalar(85, 255, 0),cv::Scalar(0, 255, 0), cv::Scalar(0, 255, 85), cv::Scalar(0, 255, 170),cv::Scalar(0, 255, 255), cv::Scalar(0, 170, 255), cv::Scalar(0, 85, 255),cv::Scalar(0, 0, 255), cv::Scalar(85, 0, 255), cv::Scalar(170, 0, 255),cv::Scalar(255, 0, 255), cv::Scalar(255, 0, 170), cv::Scalar(255, 0, 85)

};static const std::pair<int, int> limbKeypointsIds[] = {

{1, 2}, {1, 5}, {2, 3},{3, 4}, {5, 6}, {6, 7},`{1, 8}, {8, 9}, {9, 10},{1, 11}, {11, 12}, {12, 13},{1, 0}, {0, 14}, {14, 16},{0, 15}, {15, 17}

};

const int stickWidth = 4;const cv::Point2f absentKeypoint(-1.0f, -1.0f);zerotemparr();

基于计算机视觉和语音交互的老人看护陪伴系统

第 44 页 共 74 页

double maxheight=0;//当前检测到的最大身高,如果不超出太多的话则会记入

int personid=0;//当前读取数据的人的 iddouble aveheight = 165.0;//老人预估身高

double scale = 1.0;//老人身高与画面的比例

for (const auto& pose : poses) {CV_Assert(pose.keypoints.size() == HumanPoseEstimator::keypointsNumber);for (size_t keypointIdx = 0; keypointIdx < pose.keypoints.size(); keypointIdx++) {

if (pose.keypoints[keypointIdx] != absentKeypoint) {cv::circle(image, pose.keypoints[keypointIdx], 4, colors[keypointIdx], -1);//将当前读取的点的数据放入临时数组

temppointarr[keypointIdx][0] = pose.keypoints[keypointIdx].x;temppointarr[keypointIdx][1] = pose.keypoints[keypointIdx].y;// pointarr[keypointIdx*2] = pose.keypoints[keypointIdx].x;// pointarr[keypointIdx*2+1] = pose.keypoints[keypointIdx].y;

}}//现在只看一个人的状态,需要过滤掉其他人的干扰

//估算人的大致身高

double tempheight = calheight();if(maxheight!=0.0){

//因为人在移动后身高会随着离摄像头的距离改变而改变

if(tempheight>0.9*maxheight&&tempheight<1.1*maxheight){maxheight = tempheight;scale = aveheight/maxheight;

}}else{

maxheight = tempheight;scale = aveheight/maxheight;if(personid<5){

double* temppointarrs = persons[personid];for(int k=0;k<18;k++){

temppointarrs[k*2] = temppointarr[k][0];temppointarrs[k*2+1] = temppointarr[k][1];

}temppointarrs[36] = scale;

}}personid++;

}//std::cout<<"估计身高为:"<<maxheight<<std::endl;cv::Mat pane = image.clone();for (const auto& pose : poses) {

for (const auto& limbKeypointsId : limbKeypointsIds) {

基于计算机视觉和语音交互的老人看护陪伴系统

第 45 页 共 74 页

std::pair<cv::Point2f, cv::Point2f>limbKeypoints(pose.keypoints[limbKeypointsId.first],

pose.keypoints[limbKeypointsId.second]);if (limbKeypoints.first == absentKeypoint

|| limbKeypoints.second == absentKeypoint) {continue;

}

float meanX = (limbKeypoints.first.x + limbKeypoints.second.x) / 2;float meanY = (limbKeypoints.first.y + limbKeypoints.second.y) / 2;cv::Point difference = limbKeypoints.first - limbKeypoints.second;double length = std::sqrt(difference.x * difference.x + difference.y * difference.y);int angle = static_cast<int>(std::atan2(difference.y, difference.x) * 180 / CV_PI);std::vector<cv::Point> polygon;cv::ellipse2Poly(cv::Point2d(meanX, meanY), cv::Size2d(length / 2, stickWidth),

angle, 0, 360, 1, polygon);cv::fillConvexPoly(pane, polygon, colors[limbKeypointsId.second]);

}}cv::addWeighted(image, 0.4, pane, 0.6, 0, image);

}} // namespace human_pose_estimation

2.图形界面,语音识别

2-1 总控制程序,实现语音和图形界面控制,并在开始运行时同步启动 c++编写的基于摄像

头的跌倒检测程序

import osimport pygame as pyimport numpy as npimport timeimport _threadimport serialimport arrayimport cv2import multiprocessingimport mathimport sounddevice as sdfrom mttkinter import mtTkinter as tkfrom PIL import Image, ImageTkfrom aip import AipSpeechfrom videopage import videopage #视频播放界面

from photopage import photopage #相册界面

from musicpage import musicpage #音乐界面

基于计算机视觉和语音交互的老人看护陪伴系统

第 46 页 共 74 页

from callpage import callpage #拨打电话界面

from heartpage import heartpage #心率测量

from clockpage import clockpage #定时服药

from envpage import envpage #环境检测界面

#屏幕宽高

winwidth = 0winheight = 0#百度语音 api 账号密码

app_id = "22734117"api_key = "dGN7IjSC9ZbeaEb9wpumWF8I"secret_key = "7wDCfXsZ8blwwa2peLOwVOHIoZuOyHUm"client = AipSpeech(app_id, api_key, secret_key)#全局变量

length = 2duration = 50 # seconds

#桌面

class basedesk():def __init__(self, master):

self.root = masterself.root.config()self.root.title('Base page')self.root.geometry(str(winwidth) + 'x' + str(winheight))initface(self.root)littleface(self.root)

#初始界面

class initface():def __init__(self, master):

self.master = master#基准界面 initfaceself.initface = tk.Canvas(self.master,

bg="white",width=winwidth,height=winheight,highlightthickness=0)

self.initface.pack()#常量定义

self.closebtnwidth = 70 #关闭按钮的大小

self.Bigbtnwidth = winwidth / 3 #中央大按钮的大小

self.btnwidth = winheight / 5 #普通按钮的大小

self.padding1 = 40 #普通按钮之间的间隔

基于计算机视觉和语音交互的老人看护陪伴系统

第 47 页 共 74 页

self.midlength = winwidth * 9 / 24self.sidelength = winwidth * 15 / 48self.tomid = 50self.Amovex = winwidth / 3 - self.padding1 - self.btnwidth - self.tomidself.Bmovey = winheight / 4self.Cmovex = winwidth * 2 / 3 + self.padding1 + self.tomidself.midmove = 60 #中间层按钮偏移量

self.heartratelist = [80,90,69,110,102,79] #心跳速度列表

self.oxygenlist = [89,90,98,96,87,92] #血氧浓度列表

self.twomove = 100self.child = [] #页面栈

#偏移量

self.move = [[

[self.Amovex-self.twomove/2, self.Bmovey - self.btnwidth / 2 - self.padding1/ 2],

[self.Amovex - self.midmove-self.twomove,self.Bmovey + self.btnwidth / 2 + self.padding1 / 2

],[

self.Amovex -self.midmove+self.btnwidth+60-self.twomove,self.Bmovey + self.btnwidth / 2 + self.padding1 / 2

],[

self.Amovex-self.twomove/2,self.Bmovey + self.btnwidth * 3 / 2 + self.padding1 * 3 / 2

]],[

[winwidth / 3, 0],[winwidth / 3, self.Bmovey],[winwidth / 3, self.Bmovey+self.Bigbtnwidth-90],

],[

[self.Cmovex+self.twomove/2, self.Bmovey - self.btnwidth / 2 - self.padding1/ 2],

[self.Cmovex + self.midmove-self.twomove,self.Bmovey + self.btnwidth / 2 + self.padding1 / 2

],[

基于计算机视觉和语音交互的老人看护陪伴系统

第 48 页 共 74 页

self.Cmovex + self.midmove+self.btnwidth-self.twomove+60,self.Bmovey + self.btnwidth / 2 + self.padding1 / 2

],[

self.Cmovex+self.twomove/2,self.Bmovey + self.btnwidth * 3 / 2 + self.padding1 * 3 / 2

]]

]#读取本地 icon 资源

self.titleimage = ImageTk.PhotoImage(Image.open("srcimage/title.jpg").resize(

(int(winwidth / 3), int(winheight / 6))))self.backgroundimage = ImageTk.PhotoImage(

Image.open("srcimage/background.jpg").resize((int(winwidth), int(winheight))))

self.bookimage = ImageTk.PhotoImage(Image.open("srcimage/photos.jpg").resize(

(int(self.btnwidth), int(self.btnwidth))))self.movieimage = ImageTk.PhotoImage(

Image.open("srcimage/video.jpg").resize((int(self.btnwidth), int(self.btnwidth))))

self.musicimage = ImageTk.PhotoImage(Image.open("srcimage/music.jpg").resize(

(int(self.btnwidth), int(self.btnwidth))))self.studyimage = ImageTk.PhotoImage(

Image.open("srcimage/emecall.jpg").resize((int(self.btnwidth), int(self.btnwidth))))

self.gameimage = ImageTk.PhotoImage(Image.open("srcimage/phone.jpg").resize(

(int(self.btnwidth), int(self.btnwidth))))self.hearto2image = ImageTk.PhotoImage(

Image.open("srcimage/hearto2.jpg").resize((int(self.btnwidth), int(self.btnwidth))))

self.clockimage = ImageTk.PhotoImage(Image.open("srcimage/clock.jpg").resize(

(int(self.btnwidth), int(self.btnwidth))))self.environmentimage = ImageTk.PhotoImage(

Image.open("srcimage/environment.jpg").resize((int(self.btnwidth), int(self.btnwidth))))

self.workimage = ImageTk.PhotoImage(Image.open("srcimage/looking.jpg").resize(

(int(self.Bigbtnwidth), int(self.Bigbtnwidth))))

基于计算机视觉和语音交互的老人看护陪伴系统

第 49 页 共 74 页

self.faceback = ImageTk.PhotoImage(Image.open("srcimage/faceback.jpg").resize(

(int(self.Bigbtnwidth), int(self.Bigbtnwidth))))self.working0 = ImageTk.PhotoImage(

Image.open("srcimage/working0.jpg").resize((int(self.Bigbtnwidth), int(self.Bigbtnwidth))))

self.working1 = ImageTk.PhotoImage(Image.open("srcimage/working1.jpg").resize(

(int(self.Bigbtnwidth), int(self.Bigbtnwidth))))self.working2 = ImageTk.PhotoImage(

Image.open("srcimage/working2.jpg").resize((int(self.Bigbtnwidth), int(self.Bigbtnwidth))))

self.working3 = ImageTk.PhotoImage(Image.open("srcimage/working3.jpg").resize(

(int(self.Bigbtnwidth), int(self.Bigbtnwidth))))self.working4 = ImageTk.PhotoImage(

Image.open("srcimage/working4.jpg").resize((int(self.Bigbtnwidth), int(self.Bigbtnwidth))))

self.working5 = ImageTk.PhotoImage(Image.open("srcimage/working5.jpg").resize(

(int(self.Bigbtnwidth), int(self.Bigbtnwidth))))self.closeimage = ImageTk.PhotoImage(

Image.open("srcimage/close.jpg").resize((int(self.closebtnwidth), int(self.closebtnwidth))))

# 右边的三个按钮

self.buttonA_1 = tk.Button(self.initface,image=self.bookimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.gotophoto,bd=0,highlightthickness=0,highlightcolor="white")

self.buttonA_1.place(x=self.move[0][0][0], y=self.move[0][0][1])self.buttonA_2 = tk.Button(self.initface,

image=self.movieimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.gotovideo,bd=0,highlightthickness=0)

self.buttonA_2.place(x=self.move[0][1][0], y=self.move[0][1][1])self.buttonA_3 = tk.Button(self.initface,

基于计算机视觉和语音交互的老人看护陪伴系统

第 50 页 共 74 页

image=self.musicimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.gotoenvpage,bd=0,highlightthickness=0)

self.buttonA_3.place(x=self.move[0][2][0], y=self.move[0][2][1])self.buttonA_4 = tk.Button(self.initface,

image=self.environmentimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.gotomusic,bd=0,highlightthickness=0)

self.buttonA_4.place(x=self.move[0][3][0], y=self.move[0][3][1])# 交大的校徽

self.titleCanvas = tk.Canvas(self.initface,width=self.Bigbtnwidth,height=winheight / 6)

self.titleCanvas.place(x=self.move[1][0][0], y=self.move[1][0][1])self.titleCanvas.create_image(0, 0, anchor='nw', image=self.titleimage)self.titleCanvas.configure(highlightthickness=0)# 中间的按钮

self.buttonB = tk.Button(self.initface,image=self.faceback,height=int(winheight / 2),width=int(winwidth / 3),relief="ridge",bd=0,highlightthickness=0)

self.buttonB.place(x=self.move[1][1][0], y=self.move[1][1][1])#下面的文字

self.wordcanvas =tk.Canvas(self.initface,bg="white",width=winwidth/3,height=200,highlightthickness=0)

self.wordcanvas.place(x=self.move[1][2][0],y=self.move[1][2][1])self.wordcanvas.create_text(winwidth/6,100,text="知心小雨为您服务 ",font=("宋体

",30))# 左边的三个按钮

self.buttonC_1 = tk.Button(self.initface,image=self.studyimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.emecall,

基于计算机视觉和语音交互的老人看护陪伴系统

第 51 页 共 74 页

bd=0,highlightthickness=0)self.buttonC_1.place(x=self.move[2][0][0], y=self.move[2][0][1])self.buttonC_2 = tk.Button(self.initface,

image=self.gameimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",command=self.callfamily,bd=0,highlightthickness=0)

self.buttonC_2.place(x=self.move[2][1][0], y=self.move[2][1][1])self.buttonC_3 = tk.Button(self.initface,

image=self.clockimage,height=self.btnwidth,width=self.btnwidth,relief="ridge",bd=0,highlightthickness=0,command = self.gotoclockpage)

self.buttonC_3.place(x=self.move[2][2][0], y=self.move[2][2][1])self.buttonC_4 = tk.Button(self.initface,

image=self.hearto2image,height=self.btnwidth,width=self.btnwidth,relief="ridge",bd=0,highlightthickness=0,command = self.gotoheartpage)

self.buttonC_4.place(x=self.move[2][3][0], y=self.move[2][3][1])#关闭按钮

self.closebtn = tk.Button(self.initface,image=self.closeimage,width=self.closebtnwidth,height=self.closebtnwidth,bd=0,command=self.destroypage)

self.closebtn.place(x=winwidth - self.closebtnwidth, y=0)self.hearto2page = []#刷新显示图片

_thread.start_new_thread(self.showtitle,("threadname",1))_thread.start_new_thread(self.startSR,("treadname",1))

# 跳转到视频播放界面

def gotovideo(self):self.child.append(videopage(self.master, winheight, winwidth))

# 跳转到相册界面

基于计算机视觉和语音交互的老人看护陪伴系统

第 52 页 共 74 页

def gotophoto(self):self.child.append(photopage(self.master, winheight, winwidth))

# 跳转到音乐播放界面

def gotomusic(self):self.child.append(musicpage(self.master, winheight, winwidth))

# 跳转到血氧测量界面

def gotohearto2(self):self.child.append(hearto2page(self,self.master,winheight,winwidth))

# 跳转到视频播放界面

def gotoheartpage(self):self.child.append(heartpage(self,self.master, winheight, winwidth))

#紧急呼救

def emecall(self, event):#打开串口,波特率 115200,无校验,停止位 1,数据位 8,连接超时 2 秒

ser = serial.Serial("/dev/ttyS0",115200,parity='N',stopbits=1,bytesize=8,timeout=2)

#拨打电话

ser.write('ATD' + 18792858682 + ';\n'.encode())serlen = ser.inWaiting()print(ser.read(serlen))

# 跳转到拨打家庭电话界面

def callfamily(self):callpage(self.master, winheight, winwidth)

#设置定时闹钟

def gotoclockpage(self):clockpage(self.master, winheight, winwidth)

#检测环境参数

def gotoenvpage(self):envpage(self.master, winheight, winwidth)

# 刷新显示图像

def showtitle(self,threadname,x):def video_loop():

try:while True:

self.titleCanvas.create_image(0,0,anchor='nw',image=self.titleimage)

基于计算机视觉和语音交互的老人看护陪伴系统

第 53 页 共 74 页

self.master.update_idletasks() #最重要的更新是靠这两句来实现

self.master.update()except:

pass

video_loop()#self.face1.mainloop()

# 血压测量串口调用

def bloodpressuretest(self,callclass,theadname,name):#打开串口,波特率 9600,无校验,停止位 1,数据位 8,连接超时 2 秒

ser=serial.Serial("/dev/ttyUSB0", 9600, parity='N', stopbits=1, bytesize=8, timeout=5)#开始测量

ser.write('AT+ST:1\r\n'.encode())time.sleep(15)#等待测量完成后读取数据,取最后 U1 开头即可

flag = Falsefor item in ser.readlines():

string = bytes.decode(item)if string[1] == "1":

#此时输出数据有效

flag = Trueresult = self.resolvestr2(string[3:])break

if not flag:result="false"callclass.showbadresult()

else:callclass.showresult(result)

# 关闭当前界面

def destroypage(self):self.master.destroy()

# 将字符串处理成可用数据

def resolvestr(self,_str):array = _str.split(',')arrayr = []for item in array:

try:item = int(item)

except:item = -1

arrayr.append(item)return arrayr

基于计算机视觉和语音交互的老人看护陪伴系统

第 54 页 共 74 页

def resolvestr2(self,_str):array = _str.split(',')arrayr = []for item in array:

try:item = int(item)

except:pass

arrayr.append(item)return arrayr

# 语音识别

def SR(self):os.system("arecord -d %d -r 16000 -c 1 -t wav -f S16_LE record.wav" %

(length, ))with open("record.wav", 'rb') as fp:

res = client.asr(fp.read(), 'wav', 16000, {'dev_pid': 1537,

})res = res['result'][0]print(res)if "照片" in res:

_thread.start_new_thread(self.gotophoto,("threadname",1))return

if "视频" in res:self.gotovideo()return

if "音乐" in res:self.gotomusic()return

if "心率" in res:self.gotoheartpage()return

if "电话" in res:self.callfamily()return

if "退出" in res:self.child[0].back()

# 读取获取的声音大小

def print_sound(self,indata, outdata, frames, time, status):volume_norm = np.linalg.norm(indata)*10if int(volume_norm)>100:

self.SR()def startSR(self,threadname,x):

基于计算机视觉和语音交互的老人看护陪伴系统

第 55 页 共 74 页

with sd.Stream(callback=self.print_sound):sd.sleep(duration * 1000)

#知心小雨表情和语音管理

class littleface():def __init__(self,master):

self.master = masterself.facepage =

tk.Canvas(self.master,bg='white',width=winwidth,height=winheight,highlightthickness=0)self.facepage.place(x=0,y=0)self.littlewidth = 340self.littleheight = self.littlewidth*winheight/winwidth# 读取表情图片包

self.smile0 = ImageTk.PhotoImage(Image.open("srcimage/smile0.jpg").resize(

(int(winwidth), int(winheight))))self.smile1 = ImageTk.PhotoImage(

Image.open("srcimage/smile1.jpg").resize((int(winwidth), int(winheight))))

self.smile2 = ImageTk.PhotoImage(Image.open("srcimage/smile2.jpg").resize(

(int(winwidth), int(winheight))))self.smile3 = ImageTk.PhotoImage(

Image.open("srcimage/smile3.jpg").resize((int(winwidth), int(winheight))))

self.happy = ImageTk.PhotoImage(Image.open("srcimage/happy.jpg").resize(

(int(winwidth), int(winheight))))self.dislike = ImageTk.PhotoImage(

Image.open("srcimage/dislike.jpg").resize((int(winwidth), int(winheight))))

self.lsmile0 = ImageTk.PhotoImage(Image.open("srcimage/smile0.jpg").resize(

(int(self.littlewidth), int(self.littleheight))))self.lsmile1 = ImageTk.PhotoImage(

Image.open("srcimage/smile1.jpg").resize((int(self.littlewidth), int(self.littleheight))))

self.lsmile2 = ImageTk.PhotoImage(Image.open("srcimage/smile2.jpg").resize(

(int(self.littlewidth), int(self.littleheight))))self.lsmile3 = ImageTk.PhotoImage(

Image.open("srcimage/smile3.jpg").resize((int(self.littlewidth), int(self.littleheight))))

基于计算机视觉和语音交互的老人看护陪伴系统

第 56 页 共 74 页

self.lhappy = ImageTk.PhotoImage(Image.open("srcimage/happy.jpg").resize(

(int(self.littlewidth), int(self.littleheight))))self.ldislike = ImageTk.PhotoImage(

Image.open("srcimage/dislike.jpg").resize((int(self.littlewidth), int(self.littleheight))))

# 控制 flagself.smiling = Trueself.small = Falseself.emojiid = 0_thread.start_new_thread(self.playface,("threadname",1))angeemoji)self.changeemoji()

# 持续刷新表情以达到动画效果

def playface(self,treadname,x):while True:

self.smileface()if self.emojiid == 0:

self.happyface()if self.emojiid == 1:

self.dislikeface()# 正常状态下的笑脸

def smileface(self):flag = 0plus = Truewhile self.smiling:

if self.small:if flag ==0:

self.facepage.create_image(0,0,anchor='nw',image=self.lsmile0)if flag ==1:

self.facepage.create_image(0,0,anchor='nw',image=self.lsmile1)if flag ==2:

self.facepage.create_image(0,0,anchor='nw',image=self.lsmile2)if flag ==3:

self.facepage.create_image(0,0,anchor='nw',image=self.lsmile3)else:

if flag ==0:self.facepage.create_image(0,0,anchor='nw',image=self.smile0)

if flag ==1:self.facepage.create_image(0,0,anchor='nw',image=self.smile1)

if flag ==2:self.facepage.create_image(0,0,anchor='nw',image=self.smile2)

if flag ==3:self.facepage.create_image(0,0,anchor='nw',image=self.smile3)

基于计算机视觉和语音交互的老人看护陪伴系统

第 57 页 共 74 页

if plus:flag = flag + 1

else:flag = flag - 1

if flag == 0:plus = True

if flag == 4:plus = False

if plus:time.sleep(0.05)

else:time.sleep(0.1)

if flag == 1 and plus:time.sleep(3)

# 其他表情

def happyface(self):self.facepage.create_image(0,0,anchor='nw',image=self.happy)time.sleep(2)self.smiling = True

def dislikeface(self):self.facepage.create_image(0,0,anchor='nw',image=self.dislike)time.sleep(2)self.smiling = True

# 改变表情尺寸

def changeemoji(self,x=1):self.facepage.config(width = self.littlewidth,height=self.littleheight)self.facepage.place(x=(winwidth-self.littlewidth)/2,y=(winheight-self.littleheight)/2+15)self.facepage.create_image(0,0,anchor='nw',image=self.lsmile0)self.small = True# self.emojiid = x# self.smiling = False

# 调用 sh 文件以启动 c++语言编写的摔倒检测程序

def falldetection(threadname,x):os.system("./hello.sh")

if __name__ == '__main__':# 开启摔倒检测

_thread.start_new_thread(start,("threadname",1))root = tk.Tk()#全屏应用

root.attributes("-fullscreen", True)#获取宽高

winwidth = root.winfo_screenwidth()

基于计算机视觉和语音交互的老人看护陪伴系统

第 58 页 共 74 页

winheight = root.winfo_screenheight()basedesk(root)root.mainloop()

1-2 相册界面

from backbtn import backbtn #返回按钮

from title import titlefrom background import backgroundfrom PIL import Image, ImageTkimport tkinter as tkimport tkinter.colorchooserimport pygame as pyimport timeimport _threadimport cv2import multiprocessingimport glob

class photopage():def __init__(self,master,_winheight,_winwidth):

#获取本地图片文件

self.extensionlist = ['jpg']self.imagelist = []for extension in self.extensionlist:

file_list = glob.glob('photos/*.'+extension) #返回一个列表

for item in file_list:self.imagelist.append(item[7:])

self.winheight = _winheightself.winwidth = _winwidthself.photowidth = self.winwidth/8self.photopadding = self.winwidth/64self.master = masterself.imagereadlist=[]self.topheight = 130 #顶部标题高度

#读取图片

for n in range(0,len(self.imagelist)):self.imagereadlist.append(ImageTk.PhotoImage(self.goodimage(n)))

self.photopage =tk.Canvas(self.master,bg="pink",width=self.winwidth,height=self.winheight)

self.photopage.place(x=0,y=0)self.photopage.configure(highlightthickness=0)

#背景

bg = background(self.photopage,self.winheight,self.winwidth,"call")#返回按钮

基于计算机视觉和语音交互的老人看护陪伴系统

第 59 页 共 74 页

backbtn(self.photopage,self.winheight,self.winwidth)#标题

title(self.photopage,self.winheight,self.winwidth,"看照片")#图片缩略图放置

for n in range(0,len(self.imagelist)):locals()['self.tempbutton'+str(n)] =

tk.Button(self.photopage,image=self.imagereadlist[n],width=self.photowidth,height=self.photowidth,command=self.returnfun(n),bd=0)

locals()['self.tempbutton'+str(n)].place(x=n%7*(self.photowidth+self.photopadding)+self.photopadding,y=int((n+1)/8)*(self.photowidth+self.photopadding)+self.topheight)

bg.showimage()def back(self):

self.photopage.destroy()#self.vbar.destory()

def showbigimage(self,x):bigimage(self.photopage,x,self.winheight,self.winwidth,self.imagelist)

def returnfun(self,x):return lambda:self.showbigimage(x)

def goodimage(self,id):tempimage = Image.open("photos/"+self.imagelist[id])imgwidth = tempimage.size[0]imgheight = tempimage.size[1]imgscale = imgwidth/imgheightif imgscale>1:

#将高度设置到最大

tempimage =tempimage.resize((int(self.photowidth*imgscale),int(self.photowidth)))

else:#将宽度设置到最大

tempimage =tempimage.resize((int(self.photowidth),int(self.photowidth/imgscale)))

return tempimageclass bigimage():

def __init__(self,master,id,_winheight,_winwidth,imagelist):self.imagelist =imagelistself.winwidth = _winwidthself.winheight = _winheightself.curid = idself.master = masterself.bigimage =

tk.Canvas(self.master,bg="pink",width=self.winwidth,height=self.winheight)self.bigimage.configure(highlightthickness=0)

基于计算机视觉和语音交互的老人看护陪伴系统

第 60 页 共 74 页

self.bigimage.place(x=0,y=0)self.paddingl = 200 #水平的间隙

self.paddingv = 60 #竖直方向的间隙

self.canvaswidth = self.winwidth-self.paddingl*2self.canvasheight = self.winheight-self.paddingv*2self.canvasscale = self.canvaswidth/self.canvasheightself.pointbtnwidth = 100 #切换图片按钮的大小

self.pointbtnpadding = 50 #按钮的间距

self.closebtnwidth = 50 #关闭按钮的大小

self.closebtnpadding =30 #关闭按钮的间距

self.rightimg =ImageTk.PhotoImage(Image.open("srcimage/toright.jpg").resize((int(self.pointbtnwidth),int(self.pointbtnwidth))))

self.leftimg =ImageTk.PhotoImage(Image.open("srcimage/toleft.jpg").resize((int(self.pointbtnwidth),int(self.pointbtnwidth))))

self.closeimg =ImageTk.PhotoImage(Image.open("srcimage/close.jpg").resize((int(self.closebtnwidth),int(self.closebtnwidth))))

self.showimg = ImageTk.PhotoImage(self.goodimage(id))self.pos = self.rightpos(id)#显示图片的地方

self.showimagecanvas =tk.Canvas(self.bigimage,bg="pink" ,width=self.canvaswidth,height = self.canvasheight)

self.showimagecanvas.place(x=self.paddingl,y=self.paddingv)self.showimagecanvas.configure(highlightthickness=0)#显示按钮

self.rightbtn =tk.Button(self.bigimage,image=self.rightimg,width=self.pointbtnwidth,height=self.pointbtnwidth,command=self.changeright)

self.leftbtn =tk.Button(self.bigimage,image=self.leftimg,width=self.pointbtnwidth,height=self.pointbtnwidth,command=self.changeleft)

self.leftbtn.place(x=self.pointbtnpadding,y=self.winheight/2-self.pointbtnwidth/2)

self.rightbtn.place(x=self.winwidth-self.pointbtnpadding-self.pointbtnwidth,y=self.winheight/2-self.pointbtnwidth/2)

#关闭按钮

self.closebtn =tk.Button(self.bigimage,image=self.closeimg,width=self.closebtnwidth,height=self.closebtnwidth,command=self.close)

基于计算机视觉和语音交互的老人看护陪伴系统

第 61 页 共 74 页

self.closebtn.place(x=self.winwidth-self.closebtnwidth-self.closebtnpadding,y=self.closebtnpadding)

self.showimage()def changeright(self):

self.curid = (self.curid+1)%len(self.imagelist)self.showimg = ImageTk.PhotoImage(self.goodimage(self.curid))self.pos = self.rightpos(self.curid)

def changeleft(self):self.curid = (self.curid-1)%len(self.imagelist)self.showimg = ImageTk.PhotoImage(self.goodimage(self.curid))self.pos = self.rightpos(self.curid)

def close(self):self.bigimage.destroy()

def showimage(self):def video_loop():

try:while True:

self.showimagecanvas.create_image(self.pos[0],self.pos[1],anchor='nw',image=self.showimg)#canvas4.create_image(0,0,anchor='nw',image=picture1)self.master.update_idletasks() #最重要的更新是靠这两句来实现

self.master.update()except:

self.close()

video_loop()#self.face1.mainloop()self.vc1.release()cv2.destroyAllWindows()

#返回合适比例的图片

def goodimage(self,id):tempimage = Image.open("photos/"+self.imagelist[id])imgwidth = tempimage.size[0]imgheight = tempimage.size[1]imgscale = imgwidth/imgheightif imgscale<self.canvasscale:

#将高度设置到最大

tempimage =tempimage.resize((int(self.canvasheight*imgscale),int(self.canvasheight)))

else:#将宽度设置到最大

基于计算机视觉和语音交互的老人看护陪伴系统

第 62 页 共 74 页

tempimage =tempimage.resize((int(self.canvaswidth),int(self.canvaswidth/imgscale)))

return tempimage#返回图片适合的放置位置

def rightpos(self,id):tempimage = Image.open("photos/"+self.imagelist[id])imgwidth = tempimage.size[0]imgheight = tempimage.size[1]imgscale = imgwidth/imgheightposx = 0posy = 0if imgscale<self.canvasscale:

#将高度设置到最大

tempimage =tempimage.resize((int(self.canvasheight*imgscale),int(self.canvasheight)))

posy = 0posx = (self.canvaswidth-self.canvasheight*imgscale)/2

else:#将宽度设置到最大

posy = (self.canvasheight-self.canvaswidth/imgscale)/2posx = 0tempimage =

tempimage.resize((int(self.canvaswidth),int(self.canvaswidth/imgscale)))return posx,posy

if __name__ == '__main__':root = tk.Tk()root.attributes("-fullscreen",True)winwidth = root.winfo_screenwidth()winheight = root.winfo_screenheight()photopage(root,winheight,winwidth)root.mainloop()

1-3 视频播放界面

from threading import Threadfrom tkinter import *from mttkinter import mtTkinter as tkfrom moviepy.editor import*from PIL import Image, ImageTkfrom backbtn import backbtn #返回按钮

from title import titlefrom background import backgroundimport tkinter.colorchooserimport pygletimport sys

基于计算机视觉和语音交互的老人看护陪伴系统

第 63 页 共 74 页

import pygameimport timeimport _threadimport threadingimport cv2import multiprocessingimport os.pathimport osimport glob

window_width=960window_height=620image_width=320image_height=int(window_height*1)imagepos_x=0imagepos_y=0butpos_x=450butpos_y=450class videopage():

def __init__(self,master,_winheight,_winwidth):self.extensionlist = ['mp4']self.videolist = []for extension in self.extensionlist:

file_list = glob.glob('videos/*.' + extension) #返回一个列表

for item in file_list:self.videolist.append(item[6:])

self.master = masterself.master.config(bg='blue')self.playing = False #当前是否有视频正在播放

# 屏幕宽高

self.winheight = _winheightself.winwidth = _winwidthself.videowidth = self.winwidth/8self.videopadding = self.winwidth/64self.topheight = 130 #顶部标题高度

self.firstimagelist = []for n in range(0,len(self.videolist)):

self.firstimagelist.append(self.getfirstimage(self.videolist[n]))# 获取每个视频的第一张图

# 视频集画框

self.videocanvas =tk.Canvas(self.master,bg="pink",height=self.winheight,width=self.winwidth)

self.videocanvas.place(x=0,y=0)

基于计算机视觉和语音交互的老人看护陪伴系统

第 64 页 共 74 页

#背景

bg = background(self.videocanvas,self.winheight,self.winwidth,"call")#返回按钮

backbtn(self.videocanvas,self.winheight,self.winwidth)#标题

title(self.videocanvas,self.winheight,self.winwidth,"播放视频")# 视频缩略图放置

for n in range(0,len(self.videolist)):locals()['self.tempbutton'+str(n)] =

tk.Button(self.videocanvas,image=self.firstimagelist[n],width=self.videowidth,height=self.videowidth,command=self.returnfun(n),bd=0)

locals()['self.tempbutton'+str(n)].place(x=n%7*(self.videowidth+self.videopadding)+self.videopadding,y=int((n+1)/8)*(self.videowidth+self.videopadding)+self.topheight)

bg.showimage()# 按钮点击响应函数

def playvideo(self,x):self.showpygletvideo(self.videolist[x])#showvideo(self.videopage,x,self.winheight,self.winwidth)

def returnfun(self,x):return lambda:self.playvideo(x)

#图像转换,用于在画布中显示

def tkImage(self,vc):ref,frame = vc.read()cvimage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)pilImage = Image.fromarray(cvimage)pilImage = pilImage.resize((image_width, image_height),Image.ANTIALIAS)tkImage = ImageTk.PhotoImage(image=pilImage)return tkImage

#获取每个视频的第一张图片

def getfirstimage(self,str):vc = cv2.VideoCapture('videos/'+str)ref,frame = vc.read()cvimage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)pilImage = Image.fromarray(cvimage)imgwidth = pilImage.size[0]imgheight = pilImage.size[1]imgscale = imgwidth/imgheightif imgscale > 1:

#将高度设置到最大

pilImage = pilImage.resize((int(self.videowidth*imgscale),int(self.videowidth)))else:

#将宽度设置到最大

基于计算机视觉和语音交互的老人看护陪伴系统

第 65 页 共 74 页

pilImage = pilImage.resize((int(self.videowidth),int(self.videowidth/imgscale)))tkImage = ImageTk.PhotoImage(image=pilImage)return tkImage

#图像的显示与更新

def video(self,):def video_loop():

try:while True:

picture1=self.tkImage(self.vc1)self.canvas1.create_image(0,0,anchor='nw',image=picture1)self.videopage.update_idletasks() #最重要的更新是靠这两句来实现

self.videopage.update()except:

self.back()

video_loop()self.vc1.release()cv2.destroyAllWindows()

def back(self):self.videocanvas.destroy()

def showpygletvideo(self,name):pygame.init()os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0,0)pygame.display.set_mode((640,480),1300,32)clip = VideoFileClip('videos/'+name)self.playing = Trueclip.preview()pygame.quit()

def quitpygame(self):pygame.quit()

1-4 音乐播放界面

from tkinter import *from PIL import Image, ImageTkfrom backbtn import backbtn #返回按钮

from title import titlefrom background import backgroundimport tkinter as tkimport tkinter.colorchooserimport pygame as pyimport timeimport _threadimport cv2import multiprocessing

基于计算机视觉和语音交互的老人看护陪伴系统

第 66 页 共 74 页

import os.pathimport globclass musicpage():

def __init__(self, master, _winheight, _winwidth):

#获取本地音乐文件

self.extensionlist = ['mp3', 'flac']self.musiclist = []for extension in self.extensionlist:

file_list = glob.glob('music/*.' + extension) #返回一个列表

for item in file_list:self.musiclist.append(item[6:])

#尺寸变量

self.winheight = _winheightself.winwidth = _winwidthself.photopadding = self.winwidth / 128self.photoleftpadding = 500self.photowidth = self.winwidth - self.photopadding * 2 - self.photoleftpaddingself.photoheight = 70self.photomovex = (self.winwidth - self.photowidth) / 2self.master = masterself.musicreadlist = []self.topheight = 130 #顶部标题高度

self.backbtnheight = self.topheight / 2self.backbtnwidth = self.backbtnheightself.backbtnpadding = self.topheight / 4 #顶部按钮的间距

self.pausebtnwidth = 40self.pausebtnmovey = (self.photoheight - self.pausebtnwidth) / 2self.pausebtnmovex = self.photowidth - 30 - self.pausebtnwidthself.curid = -1self.pausemusicflag = False #是否有音乐暂停

self.pausemusicid = -1 #暂停的音乐 id#读取图片

self.backimg = ImageTk.PhotoImage(Image.open("srcimage/toleft.jpg").resize(

(int(self.backbtnwidth), int(self.backbtnheight))))self.pauseimg = ImageTk.PhotoImage(

Image.open("srcimage/pause.jpg").resize((int(self.pausebtnwidth), int(self.pausebtnwidth))))

self.playimg = ImageTk.PhotoImage(Image.open("srcimage/play.jpg").resize(

(int(self.pausebtnwidth), int(self.pausebtnwidth))))

基于计算机视觉和语音交互的老人看护陪伴系统

第 67 页 共 74 页

self.musicpage = tk.Canvas(self.master,bg="pink",width=self.winwidth,height=self.winheight)

self.musicpage.place(x=0, y=0)self.musicpage.configure(highlightthickness=0)

#背景

bg = background(self.musicpage,self.winheight,self.winwidth,"call")#返回按钮

backbtn(self.musicpage,self.winheight,self.winwidth)#标题

title(self.musicpage,self.winheight,self.winwidth,"播放音乐")self.Canvaslist = []self.pausebtnlist = []#图片缩略图放置

for n in range(0, len(self.musiclist)):self.Canvaslist.append(

tk.Canvas(self.musicpage,bg="white",width=int(self.photowidth),height=int(self.photoheight)))

self.Canvaslist[n].place(x=self.photomovex,y=n * (self.photoheight + self.photopadding) + self.topheight)

self.Canvaslist[n].create_text(self.photowidth / 2,self.photoheight / 2,text=self.resolvestr(self.musiclist[n])[0],font=("黑体", 25))

self.pausebtnlist.append(tk.Button(self.Canvaslist[n],

image=self.playimg,width=self.pausebtnwidth,height=self.pausebtnwidth,command=self.returnfun(n)))

self.pausebtnlist[n].place(x=self.pausebtnmovex,y=self.pausebtnmovey)

bg.showimage()def back(self):

self.musicpage.destroy()#self.vbar.destory()

#选中要播放的音乐

def choosemusic(self, x):

基于计算机视觉和语音交互的老人看护陪伴系统

第 68 页 共 74 页

if x == self.curid:#暂停正在播放的歌曲

self.curid = -1self.pausebtnlist[x].config(image=self.playimg)self.pausemusic(x)return

self.curid = xfor n in range(0, len(self.pausebtnlist)):

#if n != self.curid:

self.pausebtnlist[n].config(image=self.playimg)else:

self.pausebtnlist[n].config(image=self.pauseimg)if self.pausemusicflag and self.pausemusicid == self.curid:

self.unpausemusic()else:

_thread.start_new_thread(self.playmusic, ("Thread", x))

#返回函数

def returnfun(self, x):return lambda: self.choosemusic(x)

#播放音乐

def playmusic(self, threadname, x):#切换歌曲要恢复设置

self.pausemusicflag = Falseself.pausemusicid = -1filepath = "music/" + self.musiclist[x]py.mixer.init()# 加载音乐

py.mixer.music.load(filepath)py.mixer.music.play(loops=-1, start=0.0)#播放时长,没有此设置,音乐不会播放,会一次性加载完

time.sleep(300)py.mixer.music.stop()#播放完后根据循环方式选择播放方式播放歌曲\

#暂停音乐

def pausemusic(self, x):self.pausemusicid = xself.pausemusicflag = Truepy.mixer.music.pause()

基于计算机视觉和语音交互的老人看护陪伴系统

第 69 页 共 74 页

#恢复播放

def unpausemusic(self):self.pausemusicflag = Falsepy.mixer.music.unpause()

#将字符串处理成可用数据

def resolvestr(self,_str):array = _str.split('.')arrayr = []for item in array:

arrayr.append(item)return arrayr

if __name__ == '__main__':root = tk.Tk()root.attributes("-fullscreen", True)winwidth = root.winfo_screenwidth()winheight = root.winfo_screenheight()photopage(root, winheight, winwidth)root.mainloop()

1-5 心率测量界面

from mttkinter import mtTkinter as tkfrom tkinter import *from PIL import Image, ImageTkfrom background import backgroundfrom backbtn import backbtn #返回按钮

from title import titleimport tkinter.colorchooserimport pygame as pyimport timeimport _threadimport serialimport arrayimport cv2import multiprocessingimport mathclass heartpage():

def __init__(self,mainclass,master,_winheight,_winwidth):self.winheight = _winheightself.winwidth = _winwidthself.master = masterself.mainclass = mainclassself.imagereadlist=[]self.topheight = 130 #顶部标题高度

基于计算机视觉和语音交互的老人看护陪伴系统

第 70 页 共 74 页

self.heartpage =tk.Canvas(self.master,bg="pink",width=self.winwidth,height=self.winheight)

self.heartpage.place(x=0,y=0)self.heartpage.configure(highlightthickness=0)self.startbtnwidth = 350self.working = Falseself.btnimage = ImageTk.PhotoImage(

Image.open("srcimage/startm.jpg").resize((int(self.startbtnwidth), int(self.startbtnwidth))))

self.waiting0 = ImageTk.PhotoImage(Image.open("srcimage/waiting0.jpg").resize(

(int(self.startbtnwidth), int(self.startbtnwidth))))self.waiting1 = ImageTk.PhotoImage(

Image.open("srcimage/waiting1.jpg").resize((int(self.startbtnwidth), int(self.startbtnwidth))))

self.waiting2 = ImageTk.PhotoImage(Image.open("srcimage/waiting2.jpg").resize(

(int(self.startbtnwidth), int(self.startbtnwidth))))self.waiting3 = ImageTk.PhotoImage(

Image.open("srcimage/waiting3.jpg").resize((int(self.startbtnwidth), int(self.startbtnwidth))))

self.waiting4 = ImageTk.PhotoImage(Image.open("srcimage/waiting4.jpg").resize(

(int(self.startbtnwidth), int(self.startbtnwidth))))self.waiting5 = ImageTk.PhotoImage(

Image.open("srcimage/waiting5.jpg").resize((int(self.startbtnwidth), int(self.startbtnwidth))))

#背景

bg = background(self.heartpage,self.winheight,self.winwidth,"call")#返回按钮

backbtn(self.heartpage,self.winheight,self.winwidth)#标题

title(self.heartpage,self.winheight,self.winwidth,"心率测量")self.startbtn = tk.Button(self.heartpage,image =

self.btnimage,width=self.startbtnwidth,height=self.startbtnwidth,command=self.start)

self.startbtn.place(x=(self.winwidth-self.startbtnwidth)/2,y=(self.winheight-self.startbtnwidth)/2)bg.showimage()

def start(self):if not self.working:

_thread.start_new_thread(self.mainclass.bloodpressuretest,(self,"threadname",1))self.working = True_thread.start_new_thread(self.waitinggif,("threadname",1))

基于计算机视觉和语音交互的老人看护陪伴系统

第 71 页 共 74 页

else:pass

def showresult(self,resultlist):self.working = Falseself.startbtn.config(image = self.btnimage)result(self.heartpage,self.winheight,self.winwidth,resultlist)

def showbadresult(self):self.working = Falseself.startbtn.config(image = self.btnimage)result(self.heartpage,self.winheight,self.winwidth,-1)

def waitinggif(self,threadname,x):flag = 0plus = Truewhile self.working:

if flag == 0:self.startbtn.config(image=self.waiting0)

if flag == 1:self.startbtn.config(image=self.waiting1)

if flag == 2:self.startbtn.config(image=self.waiting2)

if flag == 3:self.startbtn.config(image=self.waiting3)

if flag == 4:self.startbtn.config(image=self.waiting4)

if flag == 5:self.startbtn.config(image=self.waiting5)

if plus:flag = flag + 1

else:flag = flag - 1

if flag == 5:plus = False

if flag == 0:plus = True

time.sleep(0.2)class result():

def __init__(self,master,_winheight,_winwidth,resultlist):self.winheight = _winheightself.winwidth = _winwidthself.master = masterself.resultlist = resultlistself.fontpadding = 60self.fontsize = 30

基于计算机视觉和语音交互的老人看护陪伴系统

第 72 页 共 74 页

if self.resultlist!=-1:self.resultpage = tk.Canvas(self.master,bg="greenyellow",width =

self.winwidth,height = self.winheight)self.resultpage.place(x=0,y=0)

self.resultpage.create_text(self.winwidth/2,self.winheight/2-self.fontpadding*3/2,text=" 高

压:"+str(self.resultlist[0]),font=("宋体",self.fontsize))

self.resultpage.create_text(self.winwidth/2,self.winheight/2-self.fontpadding*1/2,text=" 低

压:"+str(self.resultlist[1]),font=("宋体",self.fontsize))

self.resultpage.create_text(self.winwidth/2,self.winheight/2+self.fontpadding*1/2,text=" 心

率:"+str(self.resultlist[2]),font=("宋体",self.fontsize))

self.resultpage.create_text(self.winwidth/2,self.winheight/2+self.fontpadding*7/4,text=" 非 常 健

康,一切良好",font=("宋体",self.fontsize+5))backbtn(self.resultpage,self.winheight,self.winwidth)

else:self.resultpage = tk.Canvas(self.master,bg="red",width = self.winwidth,height =

self.winheight)self.resultpage.place(x=0,y=0)self.resultpage.create_text(self.winwidth/2,self.winheight/2,text="测量出错,请检

查仪器是否佩戴正确",font=("宋体",self.fontsize+10))backbtn(self.resultpage,self.winheight,self.winwidth)

1-6 打电话界面

import tkinter as tkimport tkinter.colorchooserimport pygame as pyimport timeimport _threadfrom tkinter import *import serialimport cv2from PIL import Image, ImageTkimport multiprocessingimport mathfrom backbtn import backbtnfrom title import titlefrom background import backgroundnumber = [18792858682,13075571026,15556502217]class callpage():

def __init__(self,matser,_winheight,_winwidth):self.winheight = _winheight

基于计算机视觉和语音交互的老人看护陪伴系统

第 73 页 共 74 页

self.winwidth = _winwidthself.master = matserself.callpage = tk.Frame(self.master,bg="pink",height = self.winheight ,width

=self.winwidth)self.callpage.place(x=0,y=0)#背景

bg = background(self.callpage,self.winheight,self.winwidth,"call")#返回按钮

backbtn(self.callpage,self.winheight,self.winwidth)#标题

title(self.callpage,self.winheight,self.winwidth,"打电话给家人")self.childlist = ["firstson","secondson","thirdson"]self.avatarimagelist = [] #头像放置

self.avatarlabellist = []#头像高度

self.avatarpadding = 200self.avatarwidth = (self.winwidth -

(len(self.childlist)+1)*self.avatarpadding)/len(self.childlist)self.avatrheight = self.avatarwidth/0.618for n in range(0,len(self.childlist)):

self.avatarimagelist.append(ImageTk.PhotoImage(self.goodimage(self.childlist[n])))

self.avatarlabellist.append(tk.Button(self.callpage,width=self.avatarwidth,height=self.avatrheight,image=self.avatarimagelist[n],command = self.call))

self.avatarlabellist[n].place(x=self.avatarpadding+(self.avatarwidth+self.avatarpadding)*n,y=200)bg.showimage()

#返回合适比例的图片

def goodimage(self,str):tempimage = Image.open("srcimage/"+str+".jpg")imgwidth = tempimage.size[0]imgheight = tempimage.size[1]imgscale = imgwidth/imgheighttempimage = tempimage.resize((int(self.avatarwidth),int(self.avatarwidth/imgscale)))return tempimage

def call(self,x):ser = serial.Serial("/dev/ttyS0",

115200,parity='N',stopbits=1,bytesize=8,timeout=2)

基于计算机视觉和语音交互的老人看护陪伴系统

第 74 页 共 74 页

#拨打电话

ser.write('ATD' + number[x] + ';\n'.encode())serlen = ser.inWaiting()print(ser.read(serlen))