×

基于AWS IoT EduKit的睡眠监测系统

消耗积分:0 | 格式:zip | 大小:0.04 MB | 2022-12-06

李巍

分享资料个

描述

使命

该项目始于将睡眠监测系统与机器学习能力相结合的想法。这个想法不仅是生成和可视化睡眠数据,而且最终创建一个人的睡眠预测。项目本身可以分解为以下三个目标:

  • 拥有一个低调、电池供电的设备,可以在睡眠和环境温度时向 AWS 报告运动。
  • 能够在 Web 应用程序中存储、处理和可视化传入的数据。
  • 提出一个基于长期数据的可行模型,可以预测个人的睡眠模式

 

poYBAGOIvgOAJQNOAACv8MHX0oI028.png
睡眠监测系统的理论过程
 

 

描述

首先我要说的是,这绝对是一项正在进行的工作,但我希望迄今为止在设备上完成的工作对处理 AWS Iot EduKit 的其他人有用。在使命宣言中提到的目标中,只有一个已经实现,而且还是有点问题:

  • 拥有一个低调、电池供电的设备,可以在睡眠和环境温度时向 AWS 报告运动。

因此,我将介绍正在使用的代码以及采样方法背后的一些逻辑,并将后两个要点留作进一步工作的悬念。

如前所述,所使用的设备是 AWS IoT EduKit,被选为亚马逊网络服务重塑健康空间竞赛的一部分。该设备可以安装在床架上,以监测房间状况和床上任何运动引起的振动。起初,我们的想法是测量尽可能多的参数:噪音、温度、旋转和加速度、湿度、压力、环境光……但是,为了简单起见,我从温度、加速度和旋转开始。这些是使用 EduKit 板载的 MPU6886 芯片功能测量的。

MPU6886 的加速度计和陀螺仪测量值旨在作为床上运动的指示,这是我对睡眠质量的代表。在确定这个重要变量时,可能有多种方法可以采用,但市场上许多测量睡眠的商业产品都是通过测量运动来实现的。因此,这似乎是一个有效的、简约的解决方案,特别是因为这些功能已经存在于 EduKit 上。

 

代码

设备固件的框架是 EduKit GitHub 存储库中的 Blinky-Hello-World.c 示例

所有的修改都在Blinky 项目的main.c中完成,同时在主目录下的CMakeLists.txt文件中进行了一个小修改:

set(COMPONENT_REQUIRES "nvs_flash" "esp-aws-iot" "esp-cryptoauthlib" "core2forAWS" "json")

包含“json”以提取用于打包要发送到 AWS 的有效负载的 JSON 库。

在 main.c 中,必须包含 cJSON.h:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "cJSON.h" // Included for creating the JSON file payloads

接下来,需要定义一些参数以供稍后在代码中使用:

#define STARTING_ROOMTEMPERATURE 0.0f

/* The time between each MQTT message publish in milliseconds */
#define PUBLISH_INTERVAL_MS 5000

/* The time between each sensor data gathering period (in seconds)*/
#define SENSOR_READING_AVERAGE_DELAY 4.6

/* The time between reading the sensor data once, before the next read (in seconds).
This should be a factor of SENSOR_READING_AVERAGE_DELAY, otherwise you risk doing sensor readings beyond 5 seconds*/
#define SENSOR_READING_DELAY 0.2

/* Specify variable for keeping track of time in vTaskDelayUntil */
TickType_t xLastWakeTime;

并且需要初始化一些变量:

/* Initialize all the variables to be used for gathering sensor data and averages */
float temperature = 0;
float gx = 0;
float gy = 0;
float gz = 0;
float ax = 0;
float ay = 0;
float az = 0;

float avg_temperature = 0;
float avg_gx = 0;
float avg_gy = 0;
float avg_gz = 0;
float avg_ax = 0;
float avg_ay = 0;
float avg_az = 0;

float Sum_Temperature = 0;
float Sum_Gyro_x = 0;
float Sum_Gyro_y = 0;
float Sum_Gyro_z = 0;
float Sum_Accel_x = 0;
float Sum_Accel_y = 0;
float Sum_Accel_z = 0;

 

接下来,我创建了一个名为 sensorData() 的函数,用于实际从 MPU6886 中提取数据,并在将其发送到发布之前对其进行处理。这确实是代码的核心。为了减少发布频率以及随后的 AWS MQTT 费用,此功能将定期向 AWS 报告特定时间段内的平均值,但不经常报告。该函数将指针类型变量作为输入,因此当它被调用时,这些地址的平均值可以用于未来的操作。及时收集数据以适应发布频率窗口。我们需要在那个时间段内的数据总和,所以这些值首先被初始化。

void sensorData(float *Avg_Gyro_x, float *Avg_Gyro_y, float *Avg_Gyro_z, float *Avg_Accel_x, float *Avg_Accel_y, float *Avg_Accel_z, float *Avg_Temperature){

/* Reset all summing variables and the counter to zero */
Sum_Temperature = 0;
Sum_Gyro_x = 0;
Sum_Gyro_y = 0;
Sum_Gyro_z = 0;
Sum_Accel_x = 0;
Sum_Accel_y = 0;
Sum_Accel_z = 0;
int count_i = 0;

 

需要以对手头任务有意义的方式对数据进行平均。对于温度,只需将一段时间内的测量值相加,然后除以测量次数就足够了。

然而,运动数据有点棘手。由于振动可能对每个正值都有一个紧密匹配的负旋转/加速度,因此不能只添加这些值 - 它们必须是无符号的。此外,因为我们希望无运动大运动之间的传播范围很广,所以测量值在求和之前先平方。这允许更明显地检测微弱运动,以及更强烈地指示大运动。

/* Create a basic timer to control the data collection loop, reporting averages according to SENSOR_READING_AVERAGE_DELAY */
clock_t begin_Time = clock();

while ( ((unsigned long) (clock() - begin_Time)/CLOCKS_PER_SEC) < SENSOR_READING_AVERAGE_DELAY){

MPU6886_GetTempData(&temperature);
Sum_Temperature += (temperature * 1.8)  + 32 - 80;


/*Taking the square of each value so that averaging makes more sense with both positives and negatives coming in,
and so motion is weighted more (trying to pick up smaller motion)*/

MPU6886_GetAccelData(&ax, &ay, &az);
Sum_Accel_x += ax*ax;
Sum_Accel_y += ay*ay;
Sum_Accel_z += az*az;

MPU6886_GetGyroData(&gx, &gy, &gz);
Sum_Gyro_x += gx*gx;
Sum_Gyro_y += gy*gy;
Sum_Gyro_z += gz*gz;

count_i++;

/* To keep from accumulating huge numbers in the Sum_xxx variables, the while loop below delays for SENSOR_READING_DELAY
This is probably not the best implementation, but should work for the sleep monitor's purposes*/
clock_t start_Time = clock();
while ( ((unsigned long) (clock() - start_Time)/CLOCKS_PER_SEC) < SENSOR_READING_DELAY){}
}

请注意这部分代码中计时器的实现。一个 while 循环跟踪平均周期约 4-5 秒,另一个 while 循环在每次测量之间提供延迟,以免淹没设备内存。为这些计时器选择的值是 4.6 秒(总收集时间)和 0.2 秒(延迟)。4.6s 允许测量后的时间每 5 秒平均、打包和发布数据。

**对其他制造商的警告:有人怀疑定时器实现(while 循环而不是警报或中断)在设备运行一段时间后导致 malloc 错误。

 

采集数据后,需要打包发布。这是在 publisher() 函数中完成的。创建一个 JSON 有效负载,其中包含所有温度、加速度和陀螺仪测量值。然后使用服务质量 1 发送消息,以便设备需要来自 AWS 的 ack 以确认接收。

static void publisher(AWS_IoT_Client *client, char *base_topic, uint16_t base_topic_len){

IoT_Publish_Message_Params paramsQOS1;
paramsQOS1.qos = QOS1;
paramsQOS1.isRetained = 0;

cJSON *payload = cJSON_CreateObject();

cJSON *temperature_value = cJSON_CreateNumber(avg_temperature);
cJSON_AddItemToObject(payload, "temperature_value", temperature_value);

cJSON *accel_x_value = cJSON_CreateNumber(avg_ax);
cJSON *accel_y_value = cJSON_CreateNumber(avg_ay);
cJSON *accel_z_value = cJSON_CreateNumber(avg_az);
cJSON_AddItemToObject(payload, "accel_x_value", accel_x_value);
cJSON_AddItemToObject(payload, "accel_y_value", accel_y_value);
cJSON_AddItemToObject(payload, "accel_z_value", accel_z_value);

cJSON *gyro_x_value = cJSON_CreateNumber(avg_gx);
cJSON *gyro_y_value = cJSON_CreateNumber(avg_gy);
cJSON *gyro_z_value = cJSON_CreateNumber(avg_gz);
cJSON_AddItemToObject(payload, "gyro_x_value", gyro_x_value);
cJSON_AddItemToObject(payload, "gyro_y_value", gyro_y_value);
cJSON_AddItemToObject(payload, "gyro_z_value", gyro_z_value);

const char *JSONPayload = cJSON_Print(payload);
paramsQOS1.payload = (void*) JSONPayload;
paramsQOS1.payloadLen = strlen(JSONPayload);

// Publish and check if "ack" was sent from AWS IoT Core
IoT_Error_t rc = aws_iot_mqtt_publish(client, base_topic, base_topic_len, ¶msQOS1);

if (rc == MQTT_REQUEST_TIMEOUT_ERROR) {
ESP_LOGW(TAG, "QOS1 publish ack not received.");
rc = SUCCESS;
}
}

 

最后要考虑的代码是在 aws_iot_task 中调用这两个函数,并设置 5 秒计时器。为此,在 app_main 中,唤醒时间被初始化,以便与 vTaskDelayUntil() 一起使用,这将在上次执行后等待 5 秒,然后再次开始 aws_iot_task while 循环(如果连接到 AWS,则无限期运行的循环是好的)。

app_main 补充:

/* Initialize the tick time for use in vTaskDelayUntil*/
xLastWakeTime = xTaskGetTickCount();

以及 aws_iot_task 循环中的 publisher()、sensorData() 和 vTaskDelayUntil():

sensorData(&avg_gx, &avg_gy, &avg_gz, &avg_ax, &avg_ay, &avg_az, &avg_temperature);
publisher(&client, base_publish_topic, BASE_PUBLISH_TOPIC_LEN);
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(PUBLISH_INTERVAL_MS));

 

这样做是为了代码。值得注意的是,时序实现似乎并没有理论上的那么精确。数据最终被打包并发送到 AWS。

 

示例数据

以下是设备放置在床头板顶部的示例,在 (1)最小限度地移动和 (2)辗转反侧

无动作示例 -

{ "temperature_value": 25.075153350830078, "accel_x_value": 0.0004551649035420269, "accel_y_value": 0.00015581845946144313, "accel_z_value": 1.1685764789581299, "gyro_x_value": 17.144529342651367, "gyro_y_value": 0.4716217517852783 , "gyro_z_value": 23.804603576660156 }

辗转反侧的例子——

{ "temperature_value": 24.038557052612305, "accel_x_value": 0.0009434580570086837, "accel_y_value": 0.0001554012269480154, "accel_z_value": 1.16836678981781, "gyro_x_value": 137.39096069335938 , "gyro_y_value": 19.99363136291504 , "gyro_z_value": 565.6026000976562 }

 

向前进

显然,在这个概念上还有大量工作要做。我确实设法完成了大部分设备固件,并创建/证明了一种测量睡眠运动和环境条件的方法。然而,预测睡眠的新概念仍然需要数据工程和应用程序工作。未来的工作将包括:

  • 在 AWS IoT 分析中存储数据
  • 创建 Web 应用程序,或利用 AWS 服务来可视化和关联睡眠数据
  • 执行机器学习模型以提供睡眠预测并改进数据表示。

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !