本帖最后由 机器谱 于 2023-12-13 08:39 编辑
作者:刘颖、王浩宇、党玉娟 单位:北京科技大学 指导老师:刘新洋、栗琳 1. 项目背景1.1 行业背景 随着越来越多的服务机器人进入家庭,应用场景呈现多元化和专业化,机器人产业生态体系正在不断完善,服务机器人出现爆发式增长。如何健康养老、助老一直是近几年的热点话题。在国务院印发的《中国制造 2025》战略文件中,国家层面对于智能服务机器人领域如医疗康复、养老服务、快递服务、多功能工业服务等都进行了行动部署。《扩大和升级信息消费三年行动计划》文件中提到要发展家庭服务机器人等智能健康养老服务产品,满足养老需求等。“智慧助老”活动在全国各地开展,助力老人享受数字化、智能化所带来的便利。 数字时代,让老年人“不掉线”。助老产品层出不穷,结合老年人的身心特点,针对性地开发了有温度的适老产品。除了各种数字化助老产品,助老服务机器人也受到大众关注,如何更智能助老也成为助老服务机器人技术发展新方向。“科椅”-智能助老机器人,顺应行业市场的发展方向,响应国家政策。 1.2 需求分析 (1) 智能轮椅机器人需求分析 产品研发的目标受众是行动不便、腿脚不灵活的人群,满足使用者外出进行户外活动的需求。中国社会人口老龄化程度高,逐步进入老龄化社会,许多大城市的老年人比例以接近 20%。与此同时肢体残疾人的人口近6000万,在他们当中,许多人因行走不便需依靠轮椅进行正常生活。而普通轮椅功能单一,远远不能满足其需求,所以智能轮椅机器人的市场有着巨大的发展空间。 (2) 智能轮椅机器人与普通代步车的分析 目前市场上大多的轮椅产品只满足使用者在户外简单活动的需求,绝大部分只支持如电动自行车的功能,并未真正解决行动不便人群的痛点问题。遇到特殊情况时,如需爬楼梯、取高处物体等情况时,使用者仍无法独自解决。与现有轮椅产品不同的是,“科椅”将行走障碍人群以及部分手脚不灵活的人群考虑在内,进行功能设计以及产品设计,有效解决使用者的难题。 2. 作品介绍2.1 作品简介 基于中国社会老龄化带来的市场需求,结合市面上助老轮椅功能简单、难以解决现实痛点的问题分析,本团队设计了一款智能助老轮椅,其功能多样,拥有遥控、语音、手势多种控制方式,对用户更加友好,同时注重安全问题。 机器人模型实物图 作品采用探索者模块进行搭建制作,除了拥有轮椅的基础行进和楼梯攀爬的基础功能外,还拥有人脸智能识别、语音指令控制、蓝牙无线遥控、手势识别控制以及紧急情况提醒五大智能功能,使轮椅智能化贴心化,让老年人也可以享受到科技带来的便利。2.2 功能详述 2.2.1人脸识别解锁功能 智能轮椅前方设有摄像头,启动轮椅电源后,使用者面对智能轮椅摄像头,轮椅自动进行人脸识别。识别用户成功后,智能轮椅解锁,以此增强智能轮椅安全性。
2.2.2 蓝牙无线遥控功能 使用手机下载并安装本作品专用手机APP,安装完成后开启手机蓝牙,并打开APP,进入APP主页后选择“连接设备”。蓝牙连接成功后APP自动提醒蓝牙连接成功,显示如下操作页面,即可触屏控制轮椅。使用蓝牙无线遥控可以实现如下功能: 手机蓝牙APP键盘界面 (1)蓝牙遥控行走 成功连接蓝牙后,单击或长按界面中前进、后退、左转、右转虚拟按键,便可实现轮椅的蓝牙遥控行走。 (2)速度挡位转换 成功连接蓝牙后,单击“速度+”虚拟按键,便可实现轮椅的行走速度挡位切换。本作品共设计四个速度挡位可供选择。 (3)座位升降调节 成功连接蓝牙后,单击“座位上升”或“座位下降”虚拟按键,便可实现轮椅座位升降调节。座椅升降高低可自主控制,无固定挡位。 (4)模拟爬行楼梯 成功连接蓝牙后,控制智能轮椅行走至楼梯台阶处,长按“履带抬起”,待前履带大约与楼梯台阶平行时,松开虚拟按键,后按“前进”虚拟 按键前进适当距离,按“履带落下”将前履带放置在楼梯上同时会将轮椅抬起与楼梯相近的斜度,最后再按“前进”轮椅便可稳步爬上楼梯。 2.2.3 语音指令功能 语音指令功能默认关闭,通过人脸识别后,说出预设的语音密码(如“芝麻开门”)可开启语音指令功能,避免他人语音对控制轮椅的干扰。说出“关闭语音”,即可关闭语音指令功能。 在智能轮椅附近说出“前进”“后退”“停下”“左转”“右转”等指令(指令名称同蓝牙遥控里按键的名称一致),智能轮椅“听到”指令后,会立即做出相对应的动作,实现语音指令功能。 2.2.4 手势识别功能 针对特殊失能人群,提供手势识别控制行走的功能,可提供前后进和左右转的功能。开启轮椅且通过人脸识别后,调整摄像头朝向对准轮椅上的用户,用户在摄像头前做出“前进“停止”等手势,智能轮椅会自动识别手势,并做出相应动作(注:本作品使用的手势训练器由百度智能云提供)。 2.2.5 紧急情况提醒 若轮椅发生意外侧翻,智能轮椅会自动检测到轮椅侧翻,并自动发送邮件给老人子女或紧急联系人,告知智能轮椅发生意外侧翻,请尽快关注。邮件发送人需提前设置,若有需要也可改为短信发送。
3. 设计方案 3.1 机器人系统概述 本作品主要使用探索者模块搭建。机器人系统结构大致分为:机械本体、控制系统、人机交互系统、感知系统、上位机PC。 机械本体由探索者机器人套件组装;控制系统采用stm32F407作为主控板,其作为整个机器人控制系统的核心,接受、处理传感器数据及消息,控制机器人的行为动作;人机交互系统基于蓝牙串口模块及手机App、语音模块实现手机蓝牙遥控功能和语音控制;感知系统由摄像头和MPU6050组成,摄像头采集图像数,经路由器发送给上位机进行图像处理,MPU6050采集姿态数据发送给stm32进行姿态解算;上位机PC,主要负责图像处理,接收到下位机传来的轮椅翻车消息后,发邮件告知老人子女危险情况。 机器人系统结构
3.2 机械本体
机械本体由探索者机器人套件组装,底盘采用双履带底盘,履带式相较轮式抓地性更强,适用环境范围广,双节履带为平稳爬楼创建了可行性,前、后履带都由直流电机驱动。座椅的升降,舵机为驱动动力来源,利用复合的连杆传动,使座位上升、下降。
机械本体
3.3 控制系统
控制系统使用 stm32F407 单片机为主控板,在集成开发环境 keil5 中,使用标准库开发。单片机上电后,处于等待解锁状态,摄像头将图像传给上位机,待上位机传来人脸识别成功消息后,权限解锁,用户可控制机器人。单片机直接控制整个机器人的运动,其指令来源有三个方向:
1)手机端 App 蓝牙
2)语音识别模块,识别人声
3)上位机手势识别
同时,单片机不断获取 MPU6050 采集的原始姿态数据,进行姿态融合解算,检测到轮椅翻车后,将翻车消息经路由器告知上位机。
程序流程
3.4 人机交互系统
交互系统基于HC-05蓝牙串口模块、手机端App蓝牙通信和LD3320语音识别模块实现。人脸识别解锁后,打开手机App,连接HC-05蓝牙,可在手机App上遥控机器人。语音识别模块能识别人的语音,说出预设好的语音密码(如“芝麻开门”)可以开启语音功能,避免他人语音对控制轮椅的干扰,实现语音控制。
3.5 感知系统
感知系统由摄像头、MPU6050组成。摄像头采集图像数据经路由器传给上位机,在上位机完成人脸识别、手势识别。MPU6050,以IIC通信协议,将加速度传感器数据、陀螺仪数据发送给stm32单片机,单片机中程序进行姿态解算,得到轮椅的RPY角;轮椅翻车后,俯仰角或横滚角过大,从而感知到轮椅翻车。
3.6 上位机PC
上位机PC为机载PC,连接路由器WIFI,运行python程序,基于OpenCV实现人脸识别,使用百度云智能提供的手势训练器识别手势,通过套接字编程实现与下位机的以太网TCP通信,将图像处理结果发送给下位机;收到下位机轮椅翻车的消息后,程序立即切换WIFI为能访问internet的WIFI热点,使用SMTP协议发送邮件,告知老人子女危险情况。
4. 创新创意
(1) 市面的轮椅产品几乎都只实现了普通代步车的功能,本产品针对产品受众进行功能设计分析,在代步车的基础上,增加上下楼梯、供老人拿取高处物品、语音控制运动的功能。
(2) 为满足使用者对速度的需求,设计四个变速档位,提供不同的行走速度。
(3) 采用关节双履带爬楼结构设计,在保证前进速度的同时,采用稳定的爬梯步态,实现爬梯需求。
(4) 为防止轮椅的丢失,设定面部解锁功能,未解锁时锁死无法进行行走。
(5) 针对特殊失能人群,提供手势识别控制行走的功能,可提供前后进和左右转的功能。
(6) 为防止意外发生时无人知晓,设置了感知危险并紧急提醒功能,当轮椅翻车或受到剧烈撞击后,及时通知老人亲属。
5. 示例程序 - from socket import *
- import cv2
- from PIL import Image, ImageDraw, ImageFont
- import smtplib
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- import pywifi,time
- from pywifi import const
- import os
- import sys
- import numpy as np
- import time
- import threading
- from aip import AipBodyAnalysis
- import base64
- def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
- if (isinstance(img, np.ndarray)): # 判断是否OpenCV图片类型
- img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
- # 创建一个可以在给定图像上绘图的对象
- draw = ImageDraw.Draw(img)
- # 字体的格式
- fontStyle = ImageFont.truetype(
- "simsun.ttc", textSize, encoding="utf-8")
- # 绘制文本
- draw.text((left, top), text, textColor, font=fontStyle)
- # 转换回OpenCV格式
- return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
- #读取人脸数据
- def read_images(path , sz=None):
- c=0
- X,y =[],[]
- names=[]
- for dirname,dirnames,filenames in os.walk(path):
- for subdirname in dirnames:
- subject_path=os.path.join(dirname,subdirname)
- for filename in os.listdir(subject_path):
- try:
- if(filename==".directory"):
- continue
- filepath=os.path.join(subject_path,filename)
- im=cv2.imread(os.path.join(subject_path,filename),cv2.IMREAD_GRAYSCALE)
- if(im is None):
- print("imges"+filepath+"is None")
- if(sz is not None):
- im=cv2.resize(im,sz)
- X.append(np.asarray(im,dtype=np.uint8))
- y.append(c)
- except :
- print("unexpected error")
- raise
- c=c+1
- names.append(subdirname)
- return [names,X,y]
- def face_rec():
- cap=cv2.VideoCapture(1)
- now=time.time()
- while True:
- ret, frame= cap.read()
- x,y=frame.shape[0:2]
- small_frame= cv2.resize(frame,(int(y/2),int(x/2)))
- result = small_frame.copy()
- gray = cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY)
- faces = face_cascade.detectMultiScale(gray, 1.3, 5)
- for (x, y, w, h) in faces:
- result = cv2.rectangle(result, (x, y), (x + w, y + h), (255, 0, 0), 2)
- roi = gray[x:x + w, y:y + h]
- try:
- roi = cv2.resize(roi, (200, 200), interpolation=cv2.INTER_LINEAR)
- [p_label, p_confidence] = model.predict(roi)
- print("置信度 = ", p_confidence)
- # print(names[p_label])
- if( p_confidence < 6000 ) :
- # cv2.putText(result, names[p_label],(x,y-20),cv2.FONT_HERSHEY_SIMPLEX,1,255,2)
- result=cv2ImgAddText(result,"识别成功,欢迎使用",x,y-20,(0,255,0),20)
- cv2.imshow("recognize_face", result)
- cv2.waitKey(3000)
- cap.release()
- cv2.destroyAllWindows()
- return 1
- except:
- continue
- nnow = time.time()
- cv2.imshow("recognize_face", result)
- # 超时
- if nnow - now > 30:
- result=cv2ImgAddText(result,"超时,识别失败",x,y-20,(0,255,0),20)
- cv2.imshow("recognize_face", result)
- cv2.waitKey(3000)
- cap.release()
- cv2.destroyAllWindows()
- return -1
- if cv2.waitKey(30) & 0xFF == ord('q'):
- break
- cap.release()
- cv2.destroyAllWindows()
- return 0
- read_dir = "./data"
- [names, X, y] = read_images(read_dir)
- y = np.asarray(y, dtype=np.int32) # 生成训练模型
- model = cv2.face.EigenFaceRecognizer_create()
- # 训练模型
- model.train(np.asarray(X), np.asarray(y))
- face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
- if __name__ == "__main__":
- while True:
- zzz=face_rec()
- print(zzz)
- if(zzz==1):
- print('识别成功')
- break
- if(zzz==-1) :
- print('超时')
- continue
- x=eval(input("请输入:"))
- if(x==1):
- continue
- else:
- break
- def wifi_connect_status():
- wifi = pywifi.PyWiFi()
- iface = wifi.interfaces()[0] #acquire the first Wlan card,maybe not
- if iface.status() in [const.IFACE_CONNECTED,const.IFACE_INACTIVE]:
- print("wifi connected!")
- return 1
- else:
- print("wifi not connected!")
- return 0
- def scan_wifi():
- wifi = pywifi.PyWiFi()
- iface = wifi.interfaces()[0]
- iface.scan()
- time.sleep(1)
- basewifi = iface.scan_results()
- for i in basewifi:
- print("wifi scan result:{}".format(i.ssid))
- print("wifi device MAC address:{}".format(i.bssid))
- return basewifi
- def connect_wifi(wifiname="Redmi", ssid="1234abcd"):
- wifi = pywifi.PyWiFi()
- ifaces = wifi.interfaces()[0]
- print(ifaces.name()) #输出无线网卡名称
- ifaces.disconnect()
- time.sleep(3)
- profile = pywifi.Profile() #配置文件
- profile.ssid = wifiname #wifi名称
- profile.auth = const.AUTH_ALG_OPEN #需要密码
- profile.akm.append(const.AKM_TYPE_WPA2PSK) #加密类型
- profile.cipher = const.CIPHER_TYPE_CCMP #加密单元
- profile.key = ssid #wifi密码
- ifaces.remove_all_network_profiles() #删除其它配置文件
- tmp_profile = ifaces.add_network_profile(profile) #加载配置文件
- ifaces.connect(tmp_profile)
- time.sleep(5)
- isok = True
- if ifaces.status() == const.IFACE_CONNECTED:
- print("connect successfully!")
- else:
- print("connect failed!")
- time.sleep(1)
- return isok
- # def main():
- # wifi_connect_status()
- # #scan_wifi()
- # connect_wifi()
- fromaddr = "[email protected]"#发送者的qq邮箱
- toaddr = "[email protected]"#接收者的qq邮箱
- msg = MIMEMultipart()#实例化一个MIMEMultipart
- msg['From'] = fromaddr#设置来源的地址
- msg['To'] = toaddr #设置目的地的地址
- msg['Subject'] = "助老机器人提醒"#设置的邮件的主题
- def clientsend(servername , serverPort, sentence):
- clientSocket=socket(AF_INET, SOCK_STREAM)
- clientSocket.connect((servername , serverPort))
- clientSocket.send(sentence.encode())
- clientSocket.close()
- def clientrec(servername , serverPort) :
- clientSocket=socket(AF_INET, SOCK_STREAM)
- clientSocket.connect((servername , serverPort))
- print('连接成功')
- recvSentence=clientSocket.recv(30)
- print(recvSentence)
- print('收到字符串:'+recvSentence.decode('GBK'))
- return recvSentence.decode('GBK')
- def SMTPsend() :
- body = '【智能助老轮椅反馈】:\n检测到轮椅侧翻,老人可能有危险'
- msg.attach(MIMEText(body, 'plain'))#第二个‘plain’为设置正文的格式
- server = smtplib.SMTP("smtp.qq.com",587)#设置SMTP的服务器和其相对应的SMTP接口
- server.starttls()#开始
- server.login(fromaddr ,"oowcfniaveeidefh")#第二个参数为刚刚保存的邮件发送端的最后一个授权码
- text = msg.as_string()#转换message的格式
- server.sendmail(fromaddr, toaddr, text)#发送邮件
- server.quit()#结束
- print('发送邮件成功')
- # x=50
- # # while x :
- # # clientsend('192.168.8.163', 2001,'rr')
- # # x=x-1
- def accidentRec() :
- while 1:
- # sentence='2'
- # sentence=sentence.encode()
- # clientSocket.send(sentence)
- global clientSocket
- recvSentence=clientSocket.recv(30)
- print(recvSentence)
- print('收到字符串:'+recvSentence.decode('GBK'))
- Rec=recvSentence.decode('GBK')
- if(Rec.strip() =='G'):
- clientSocket.close()
- wifi_connect_status()
- connect_wifi()
- SMTPsend()
- connect_wifi("GL-AR150-97a", "goodlife")
- clientSocket=socket(AF_INET, SOCK_STREAM)
- clientSocket.connect(('192.168.8.1',2001))
- # clientSocket=socket(AF_INET, SOCK_STREAM)
- # clientSocket.connect(("192.168.8.1",2001))
- # print('连接成功')
- # sentence='L'
- # sentence=sentence.encode()
- # clientSocket.send(sentence) #告知下位机已人脸解锁成功
- # print('已发送L')
- # accidentRec()
- # threading.Thread(target=accidentRec).start()
- # wifi_connect_status()
- # connect_wifi()
- # SMTPsend()
- # connect_wifi("GL-AR150-97a", "goodlife")
- # clientSocket=socket(AF_INET, SOCK_STREAM)
- # clientSocket.connect(('192.168.8.1',2001))
- # while True:
- # zzz=face_rec()
- # print(zzz)
- # if(zzz==1):
- # print('识别成功')
- # break
- # if(zzz==-1) :
- # print('超时')
- # continue
- # # x=eval(input("请输入:"))
- # # if(x==1):
- # # continue
- # # else:
- # # break
- # sentence='L'
- # sentence=sentence.encode()
- # clientSocket.send(sentence) #告知下位机已人脸解锁成功
- # print('已发送L,下位机解锁')
- APP_ID = '27841617'
- API_KEY = 'vnUnC94DCH5rQz7WgGEboptk'
- SECRET_KEY = 'ijtL5gcQSaWQmrAUIoh6EnPPoKgxIufd'
- client = AipBodyAnalysis(APP_ID, API_KEY, SECRET_KEY)
- """ 读取图片 """
- def get_file_content(filePath):
- with open(filePath, 'rb') as fp:
- return fp.read()
- cap = cv2.VideoCapture(1)
- while 1:
- ret, frame=cap.read()
- cv2.imwrite('1.jpg',frame)
- image = get_file_content('1.jpg')
- x=client.gesture(image)
- # if x==0 :
- # print("None")
- # else :
- # y=client.gesture(image)
- # print(y)
- if x.get('result') :
- y=x.get('result')
- print(y[0]['classname'])
- sentence=y[0]['classname']
- sentence=sentence.encode()
- #clientSocket.send(sentence)
- cv2.imshow("img",frame)
- if cv2.waitKey(1) == ord('q') :
- break
- cap.release()
- cv2.destroyAllWindows()
- #clientSocket.close()
- #clientsend('192.168.8.1',2001,'gusdfghwn')
- # print('结束')
- # CameraIp = "http://192.168.8.1:8083/?action=stream"
- # cap = cv2.VideoCapture(CameraIp)
- # while 1:
- # ret,frame=cap.read()
- # cv2.imshow("hl", frame)
- # cv2.waitKey(100)
- # while 1:
- # Rec = clientrec('10.38.201.27' , 8080 )
- # if(Rec.strip() == 'warning!!!') :
- # SMTPsend()
- # else:
- # print('不成功')
复制代码
*更多详情请见:【S034】智能助老机器人 https://www.robotway.com/h-col-297.html |