系统功能:
系统功能展示视频
【基于STM32的老人健康及跌倒检测系统(阿里云 android)-哔哩哔哩】 https://b23.tv/LdFUEKp
1.能实时检测老人的心率血氧,血压,体温,身体姿态。
2.能通过GPS定位实时定位获取老人所在的经纬度。
3.数据实时上传阿里云通过云流转传输到android APP界面上。
4.当老人心率血氧,血压,体温异常时设备会进行语音报警,并将异常数据传输到手机APP上,手机收到异常数据会发出震动以及铃响。
5.当老人发生摔倒时,设备会大约15秒的时间报警(蜂鸣器响和语音播报),如老人身体无碍,则可以进行按键取消报警;同时老人在需要时可以通过手动报警按键进行报警求助,手机APP会收到老人摔倒以及求助的信息。
6.设备带OLED显示器可显示老人的身体情况信息;手机APP附带地图显示老人的实时位置
7.手机APP可设置老人身体数据异常的阈值。
1、系统概述
据研究所知,人口老龄化[1][2][3]是社会也正逐渐共同面临的问题,据2020年全国人口第七次普查结果显示,我国超过60岁(含60岁)人数达到了2.64亿人,在80岁及以上人口数量达到了3660万,预计到2050年将增长到1.59亿人,我国老龄化人口数量即将达到总人口的34.9%[4],年纪大的老人健康问题将会越来越突出。
截至2020年底调查统计,我国有1.9亿超过60岁(含60岁)的老年人患有慢性病。据了解,老年人更容易突发心脑血管等相关疾病,伴随着年纪越来越大,老人身体机能也在逐渐衰退,每年都有数不胜数的老人发生了跌倒。因此,对于老人特别是独居老人在突发疾病或跌倒时能否得到及时救助是及其重要的,设计出一种可以针对老年人身体健康和跌倒检测的装置,当老人的身体情况发生异常时或者跌倒后,能够快速进行报警求助在老人身边的人且通知其家人,并且能够找到老人所在的位置,使其能够及时的得到救助,从而降低老人受到的伤害。
2、系统功能概述
-
2.1硬件功能
- 硬件功能需求:首先系统能通过各类(心率血氧,血压,体温,MPU6050陀螺仪加速度)传感器检测老人的身体健康情况和姿态情况,随后将传感器检测到的数据传输到STM32主控芯片进行分析处理,处理后显示在OLED液晶屏上。系统设置各传感器(心率血氧,血压,体温)的正常阈值和姿态阈值,当身体情况发生异常超过阈值或者老人发生跌倒异常 ,系统会做出相应的操作(LED闪烁、蜂鸣器报警、语音播报),并将各个设备的数据及状态显示在OLED液晶显示屏上。
-
2.2 软件功能
- 软件功能需求:从需求的角度,要达到远程通知并且报警的要求,系统将采集到的传感器信息通过STM32单片机分析处理后,通过ESP-01S WIFI模块上传到阿里云物理网平台,在平台将数据进行云产品流转,移动客户端则通过设计的APP连接到阿里云物联网平台,从而获取老人异常数据的信息,并实现当数据发生异常时能通过手机震动和发出提示音来提醒和告知用户,用户且可以通过APP的百度地图精确的找到老人所在的位置。
-
2.3 系统框架
-
2.4 跌倒判定
-
在发生跌倒时老人的身体姿态、加速度都会产生比较剧烈的变化,考虑到老人在日常生活中可能存在跑步或者跳,跑步和跳的运动的姿态变化较大,采用单一的姿态判断容易混淆。为提高跌倒判定的精准度,使用其产生的加速度进行二次判断。首先根据姿态角度去获取老人的姿态特征,然后再通过分析加速度的瞬间变化去判断老人的运动状态,最后综合姿态和加速度进而判断老人是否发生跌倒
-
加速度判定:在老人健康及跌倒检测系统中,主要通过三维空间对老人的姿态进行分析,如图4-7所示,右边为X轴,向上为Z轴,与纸面垂直向外的为Y轴。
-
-
以老人的身体为中心点,设ax为X轴方向的加速度,ay为Y轴方向的加速度,az为Z轴方向的加速度,其计算合加速度为a=ax+ay+az,因此,得出如下公式。
-
SVM是人体加速度向量的变化幅值,其用来区分人体的运动状态。当SVM的值较小时,运动的剧烈程度就越小,反之,越大则运动就越激烈。当老人发生跌倒时,和地面在进行接触时会有一个相撞的过程,这时人体会产生一个反向的加速度,加速度达到了峰值,比一般老人的动作行为会更加的明显,因此可以通过设定相应的姿态加速度阈值来判断老人是否发生了跌倒。
-
-
3.关键代码
-
3.1 STM32
-
int main(void) { double sqrt( double x); unsigned short timeCount = 0; //发送间隔变量 unsigned char *dataPtr = NULL; //定义接收变量 Hardware_Init(); //开始连接WIFI ESP8266_Init(); //初始化ESP8266 while(OneNet_DevLink()) //接入MQTT协议 delay_ms(500); BEEP=1; delay_ms(250); //蜂鸣器响一声表示接入成功 BEEP=0; OneNet_Subscribe(Sub_topics, 1); //开始订阅消息,返回OK表示订阅成功 TIM2_Int_Init(4999,7199); //定时器2初始化,0.5秒 //语音进行播报 IO0=0; delay_ms(2000); IO0=1; while(1) { Menu_key_set(); //OLED菜单栏显示 mpu_dmp_get_data(&pitch,&roll,&yaw); //得到姿态传感器值 MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据 a=aacx*aacx+aacy*aacy+aacz*aacz; svm=sqrt(a); //跌倒判断逻辑 if(roll<-70 || roll>70 ||pitch<-70 || pitch>70) { if(svm>25000){ oledshow=1; //OLED显示老人异常界面 Flag=1; } } //按键报警和取消 key=KEY_Scan(0); if(key==1){ Flag=2; IO2=1; //语音播报关闭 nor1=0; //报警上传标志 1.异常 0.正常 nor2=0; nor3=0; nor4=0; nor5=0; oledshow=2; } //手动报警上报 if(key==2){ nor5=1; //手动报警上传标志 1.异常 0.正常 IO3=0; }else { IO3=1; } if(timeCount % 60 ==0) { MAX30102_get(&HR,&SPO2); ADCX(); //获取血压 Temp=SMBus_ReadTemp(); //获取体温 parseGpsBuffer(); //GPS数据解析 printGpsBuffer(); //获取GPS数据 //经纬度定位成功赋值 latitude=atof(Save_Data.latitude); longitude=atof(Save_Data.longitude); //身体健康数据异常时报警 if(HR>=Heart || SPO2>Bloodox){ IO1=0; nor2=1; //心率或血氧报警上传标志 1.异常 0.正常 }else if(Temp>TempY){ IO1=0; nor3=1; //体温报警上传标志 1.异常 0.正常 }else if(blood>Bloodpr){ IO1=0; nor4=1; //血压报警上传标志 1.异常 0.正常 }else{ IO1=1; // nor2=0; // nor3=0; // nor4=0; } printf("温度: %.2f 心率:%3d 血氧:%3d 血压:%d 经度:%f 纬度:%f\n\r",Temp,HR,SPO2,blood,latitude,longitude); } if(++timeCount >= 90) //发送间隔,大概1.5秒上传一次 { printf("发布消息\r\n"); sprintf(PUT_BUF,"{\"heart\":%3d,\"Bloodox\":%3d,\"temp\":%.1f,\"Bloodpr\":%d,\"longitude\":%f,\"latitude\":%f,\ \"nor1\":%d,\"nor2\":%d,\"nor3\":%d,\"nor4\":%d,\"nor5\":%d}",HR,SPO2,Temp,blood,longitude,latitude,nor1,nor2,nor3,nor4,nor5); //将数据发送到服务器 OneNet_Publish(Pub_topics, PUT_BUF); //打包上传 timeCount = 0; ESP8266_Clear(); LED0=!LED0; } dataPtr = ESP8266_GetIPD(3); //8266接收返回值 if(dataPtr != NULL) OneNet_RevPro(dataPtr); delay_ms(10); //延迟 } }
3.2 Android APP
-
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bottomNavigationView = findViewById(R.id.bottomNavigationView); bottomNavigationView.setSelectedItemId(R.id.immerse); bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.immerse: return true; case R.id.test: Intent intent = new Intent(MainActivity.this,mapActivity.class); intent.putExtra("latitude",s1); intent.putExtra("longitude",s2); startActivity(intent); return true; } return false; } }); on_init(); fas_1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String s1 = yuz_1.getText().toString(); publishmessageplus(Mqtt_pub_topic,"{\"Heart\":"+s1+"}"); Toast.makeText(MainActivity.this, "心率阈值设置成功", Toast.LENGTH_SHORT).show(); } }); fas_2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String s2 = yuz_2.getText().toString(); publishmessageplus(Mqtt_pub_topic,"{\"Bloodox\":"+s2+"}"); Toast.makeText(MainActivity.this, "血氧阈值设置成功", Toast.LENGTH_SHORT).show(); } }); fas_3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String s3 = yuz_3.getText().toString(); publishmessageplus(Mqtt_pub_topic,"{\"Temp\":"+s3+"}"); Toast.makeText(MainActivity.this, "体温阈值设置成功", Toast.LENGTH_SHORT).show(); } }); fas_4.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String s4 = yuz_4.getText().toString(); publishmessageplus(Mqtt_pub_topic,"{\"Bloodpr\":"+s4+"}"); Toast.makeText(MainActivity.this, "血压阈值设置成功", Toast.LENGTH_SHORT).show(); } }); img_6.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { publishmessageplus(Mqtt_pub_topic,"{\"Cancell\":1}"); Toast.makeText(MainActivity.this, "报警清除", Toast.LENGTH_SHORT).show(); } }); Mqtt_init(); startReconnect(); handler = new Handler() { @SuppressLint("SetTextI18n") public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: //开机校验更新回传 break; case 2: // 反馈回传 break; case 3: //MQTT 收到消息回传 UTF8Buffer msg=new UTF8Buffer(object.toString()); System.out.println(msg.obj.toString()); // 显示MQTT数据 String jsonobj= msg.obj.toString().substring(msg.obj.toString().lastIndexOf("{")); parseJsonobj(jsonobj); Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show(); break; case 30: //连接失败 Toast.makeText(MainActivity.this,"连接失败" ,Toast.LENGTH_SHORT).show(); break; case 31: //连接成功 Toast.makeText(MainActivity.this,"连接成功" ,Toast.LENGTH_SHORT).show(); try { client.subscribe(Mqtt_sub_topic,1); Toast.makeText(MainActivity.this,"订阅成功" ,Toast.LENGTH_SHORT).show(); } catch (MqttException e) { e.printStackTrace(); } break; default: break; } } }; }
3.3 阿里云数据转发
-
4.展望
-
老人健康及跌倒检测系统具有远距离、低功耗、高定位等特点,相比传统的老人跌倒检测具有更大的优势。本系统基于STM32的老人健康及跌倒检测系统,该系统正是顺应当今人口发展的趋势,使用新型的技术,帮助老人特别是空巢老人减少了身体受到的伤害。相信在未来的发展中,能凭借低成本、低功耗、远距离、便携性的优势应用于更多的场景,减少对老人的身体健康损害,提高老人的生活质量。