WIFI信号状态信息 CSI 特征提取篇之活动片段提取上(五)

在之前的数据处理环节中,用CSI Tool收集到的原始数据信号,经历了数据解析、降噪、插值的处理步骤,变成了干净、完整的信号片段,这是后续做更进一步分析的基础。

在开始阅读本篇博客前,需要说明两个重要的点:

  • 在先前的代码中,为了能够一次性提取出所有重要的信息,振幅和相位数据都进行了计算,被存储到result_matrix这个数组中,但是从本篇博客开始,将仅围绕着振幅值进行分析。具体提以什么值进行分析,请结合自己的研究对象以及相关文献。
  • 后续的特征提取以及研究思路都是基于人类活动识别,CSI感知所涵盖的领域较多,而特征的提取与识别对象本身息息相关,因此需要对比自己所研究的领域,同本博客的是否有相似性。

本环节是特征提取环节,起承上启下的作用:对上,应着数据处理完毕的信号,但是还不能直接进行分析,因为此时信号片段过长,对于活动识别而言,存在非许多目标片段(例如失败不规范的动作、休息间歇等);对下,具体采取何种活动片段提取策略,需要配合后续采取的活动识别算法进行(深度学习/机器学习,CNN/RNN/LSTM等等),不同的算法适配不同的数据形式,例如CNN一般用于识图,不需要关注时间顺序,LSTM则要求保留时间关系等等,所以本博客后续仅介绍几种分类思路。

以CNN识图这种非常简单粗暴的一种识别方法为例,由于最后是需要图像形式的数据集,因此本环节,经历了以下几个步骤:
①标记活动点→②切割信号片段→③筛选活动片段→④转化为图像帧

具体怎么实现上述步骤呢,这里参考了下面这一博客的做法。这个github是我做CSI识别时候的救星,虽然是LSTM算法,但是里面的代码很全,只要提供信号数据文件和标注文件,就可以自动去进行一系列后续的操作。本环节后续许多处理代码都是参考这个博客,在其代码基础上作了一定的修改。
GitHub - ermongroup/Wifi_Activity_Recognition: Code for IEEE Communication Magazine (A Survey on Behaviour Recognition Using WiFi Channle State Information)

在上面这个博客中,一共有三个重要的Python文件,其顺序和作用是:
cross_vali_data_convert_merge.py:从指定路径中的CSV文件中导入数据,并根据一定的条件将数据处理成滑动窗口的形式,然后将处理后的数据保存到新的CSV文件中。
cross_vali_input_data.py:读取前述代码处理好的CSV文件,对数据进行降采样、预处理,根据活动阈值进行筛选,并将处理后的数据存储到字典中。
cross_vali_recurrent_network_wifi_activity.py:LSTM算法部分。

下面将按照代码,详细介绍每一步需要准备的文件和处理流程,实验细节以之前博客所设定的参数为例。

① 标记活动点

在之前的环节中,得到的是一段信号片段result_matrix,由于只需要振幅数值,那么现在result_matrix这个数组的维度是(N,91),N为数据包的个数。图像化展示一段信号的振幅图像如下:

可以看到圈起来的片段波动较大,对应着志愿者实际发生活动,而在活动的间歇间,振幅波动则很小,因此振幅的方差可以作为是否发生活动的一种判定方式,有相关文献是根据此进行自动化的活动片段提取。但是并不是所有的活动数据都能采用这个思路,不怕麻烦的话,人工对照视频监控对数据文件进行标注是最准确的方法,可以手动淘汰不标准的动作。不过改方式要求在先前做实验的时候,记录每次采集数据开始的时间,因为CSI信号数据本身并未存储现实生活中的时间,只有相对时间信息。举例,一段长度为10000的CSI信号,采样频率是1000Hz,可知该段信号记录了10s的活动,如果采集开始时刻已知,即可知道这段信号对应的真实时间段。

首先,将数据处理环节得到的振幅值以csv格式进行存储,每个实验样本的CSI振幅为一个csv文件,期内有90*N个数据,90表示子载波数量,N为该段实验样本的数据包个数,命名为input系列文件。与之相对的,每个实验样本还需要有一个标注文件,命名为annotation系列文件,其内数据维度为1*N,每行的内容即是对应信号数据文件的标注内容。强烈建议从一开始就对所有文件进行规范命名,如下图所示(input/annotation_志愿者代号_活动类型代号_数字编号):

   

② 切割信号片段

如代码所示,整理好的input系列文件和annotation系列文件被放在filepath1和filepath2中,然后依次被送进dataimport函数处理。先统一用滑动窗口的方式把一段长的实验样本切割为若干个等长的小样本,代码中设置的小样本长度为0.5s。同时为了便于管理,每种分类的类别都以数字编码。统计每一样本内部各个类别出现的频次,若有类别频次大于设定的活动阈值,则标记该样本为相应类别。输出的文件为xx和yy的csv文件,前者是重整后的信号数据,维度是(M,90*500),90*500即一个样本的所有振幅数据,M是切割后的样本数量;后者是标记文件,维度是(M,10),在这个矩阵中,发生活动的时间点,对应列数会被标记为“1”,否则第一列为“2”。

需要注意的是,由于CNN算法不关注时间顺序,所以这里对实验样本的顺序并没有做出严格管理,只是简单粗暴的按照类别把实验样本放在一起。

import numpy as np,numpy
import csv
import glob
import os

window_size = 500  # 滑动窗口大小
threshold = 80  # 活动阈值
slide_size = 400 # 滑动窗口每次滑动长度,建议设置大小不超过window_size


# 定义数据导入函数dataimport
# 三个输入参数:filepath1 表示输入文件路径模式,filepath2 表示注释文件路径模式,slide_size 表示滑动窗口的大小。
def dataimport(filepath1, filepath2, slide_size):
	xx = np.empty([0,window_size,90], dtype=np.float16)
	yy = np.empty([0,10],float) # 10=分类数量(9)+1

	###Input data###
	#data import from csv
	input_csv_files = sorted(glob.glob(filepath1))
	for f in input_csv_files:
		print("input_file_name=",f)
		data = [[ float(elm) for elm in v] for v in csv.reader(open(f, "r"))]
		tmp1 = np.array(data, dtype=np.float16)
		x2 = np.empty([0, window_size, 90], dtype=np.float16)

		#data import by slide window
		k = 0
		while k <= (len(tmp1) + 1 - 2 * window_size):
			x = np.dstack(np.array(tmp1[k:k+window_size, 0:90]).T)
			x2 = np.concatenate((x2, x),axis=0)
			k += slide_size

		xx = np.concatenate((xx,x2),axis=0)
	xx = xx.reshape(len(xx),-1)
	xx = xx.astype(np.float16)

	###Annotation data###
	#data import from csv
	annotation_csv_files = sorted(glob.glob(filepath2))
	for ff in annotation_csv_files:
		print("annotation_file_name=",ff)
		ano_data = [[ str(elm) for elm in v] for v in csv.reader(open(ff,"r"))]
		tmp2 = np.array(ano_data)

		#data import by slide window
		y = np.zeros(((len(tmp2) + 1 - 2 * window_size)//slide_size+1,10))
		k = 0
		while k <= (len(tmp2) + 1 - 2 * window_size):
			y_pre = np.stack(np.array(tmp2[k:k+window_size]))
            # 所非分类的系列活动
			liedown = 0
			write = 0
			read = 0
			walk = 0
			clean = 0
			armtrain = 0
			squat = 0
			run = 0
			jump = 0
			noactivity = 0
			for j in range(window_size):
				if y_pre[j] == "LieDown":
					liedown += 1
				elif y_pre[j] == "Write":
					write += 1
				elif y_pre[j] == "Read":
					read += 1
				elif y_pre[j] == "Walk":
					walk += 1
				elif y_pre[j] == "Clean":
					clean += 1
				elif y_pre[j] == "ArmTrain":
					armtrain += 1
				elif y_pre[j] == "Squat":
					squat += 1
				elif y_pre[j] == "Run":
					run += 1
				elif y_pre[j] == "Jump":
					jump += 1
				else:
					noactivity += 1


            # 根据活动时间点占该滑动窗口片段的比例,判定是否为活动片段
			if liedown > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,1,0,0,0,0,0,0,0,0])
			elif write > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,1,0,0,0,0,0,0,0])
			elif read > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,1,0,0,0,0,0,0])
			elif walk > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,1,0,0,0,0,0])
			elif clean > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,0,1,0,0,0,0])
			elif armtrain > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,0,0,1,0,0,0])
			elif squat > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,0,0,0,1,0,0])
			elif run > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,0,0,0,0,1,0])
			elif jump > window_size * threshold / 100:
				y[int(k/slide_size),:] = np.array([0,0,0,0,0,0,0,0,0,1])
			else:
				y[int(k/slide_size),:] = np.array([2,0,0,0,0,0,0,0,0,0])
			k += slide_size

		yy = np.concatenate((yy, y),axis=0)
	print(xx.shape,yy.shape)
	return (xx, yy)


#### Main ####
if not os.path.exists(r"...\input_files"):
    os.makedirs(r"...\input_files")

clients = ['A', 'B', 'C', ..., 'Z'] # 实验志愿者代号
activities = ["LieDown", "Write", "Read", "Walk", "Clean", "ArmTrain", "Squat", "Run", "Jump"] # 活动类别代号

for client in clients:
    for activity in activities:
        # 导入信号数据文件和标注文件
        filepath1 = fr"...\INPUT\input_{client}_*{activity}*.csv"
        filepath2 = fr"...\ANNOTATION\annotation_{client}_*{activity}*.csv"
        
        if glob.glob(filepath1) and glob.glob(filepath2):
            # 输出切割重整后的样本文件至指定地址
            outputfilename1 = fr"...\input_files\xx_{client}_{window_size}_{threshold}_{slide_size}_{activity}.csv"
            outputfilename2 = fr"...\input_files\yy_{client}_{window_size}_{threshold}_{slide_size}_{activity}.csv"

            x, y = dataimport(filepath1, filepath2, slide_size)

            with open(outputfilename1, "w") as f:
                writer = csv.writer(f, lineterminator="\n")
                writer.writerows(x)
            with open(outputfilename2, "w") as f:
                writer = csv.writer(f, lineterminator="\n")
                writer.writerows(y)

            print(f"{client}_{activity} finish!")
        else:
            print(f"No files found for {client}_{activity}, skipping.")

Jupyter中运行如下图所示:

友情提示,如果采样频率很高,中间文件的大小会很大,注意内存管理。 

输出得到的文件如下图所示。这份代码的作用简单来说,就是把长的信号段切割为小的样本,并根据标记频次,判定小样本属于哪一类活动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/578907.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot + Vue实现的家政服务管理系统设计与实现+毕业论文+答辩PPT+指导搭建视频(包运行成功)

目录 项目介绍 论文展示 资源获取 项目介绍 家政服务管理平台是一个管理信息系统&#xff0c;为了宣传的需要&#xff0c;为了给用户提供方便快捷的服务&#xff0c;从而设计了家政服务管理平台。管理员可以通过这个系统把家政服务信息发布出去&#xff0c;可以方便用户快…

RK3568平台开发系列讲解(Linux系统篇)芯片手册的使用:GPIO的寄存器说明

🚀返回专栏总目录 文章目录 一、查找复用寄存器二、查找方向寄存器三、查找数据寄存器沉淀、分享、成长,让自己和他人都能有所收获!😄 📢寄存器GPIO 进行配置, 一般情况下需要对 GPIO 的复用寄存器, 方向寄存器, 数据寄存器进行配置。 GPIO0_B0 配置为例: 一、查…

《十一》Qt各种对话框之QInputDialog

QInputDialog QInputDialog 用于方便快捷地获取一个用户输入数据&#xff0c;支持整数 int、浮点数 double、文本 QString 三种数据。按照 QInputDialog 内部的输入控件&#xff0c;又可以分为整数输入控件 QSpinBox、浮点数输入控件 QDoubleSpinBox、单行文本输入控件 QLineE…

C++|stack-queue-priority_queue(适配器+模拟实现+仿函数)

目录 一、容器适配器 1.1容器适配器概念的介绍 1.2stack和queue的底层结构 1.3deque容器的介绍 1.3.1deque的缺陷及为何选择他作为stack和queue的底层默认实现 二、stack的介绍和使用 2.1stack的介绍 2.2stack的使用 2.3stack的模拟实现 三、queue的介绍和使用 …

mysql download 2024

好久没在官网下载 mysql server 安装包。今天想下载发现&#xff1a; 我访问mysql官网的速度好慢啊。mysql server 的下载页面在哪里啊&#xff0c;一下两下找不到。 最后&#xff0c;慢慢悠悠终于找到了下载页面&#xff0c;如下&#xff1a; https://dev.mysql.com/downlo…

Qt:学习笔记一

一、工程文件介绍 1.1 main.cpp #include "widget.h" #include <QApplication> // 包含一个应用程序类的头文件 //argc&#xff1a;命令行变量的数量&#xff1b;argv&#xff1a;命令行变量的数组 int main(int argc, char *argv[]) {//a应用程序对象&…

揭示C++设计模式中的实现结构及应用——行为型设计模式

简介 行为型模式&#xff08;Behavioral Pattern&#xff09;是对在不同的对象之间划分责任和算法的抽象化。 行为型模式不仅仅关注类和对象的结构&#xff0c;而且重点关注它们之间的相互作用。 通过行为型模式&#xff0c;可以更加清晰地划分类与对象的职责&#xff0c;并…

使用Umbrello学习工厂模式

工厂方法模式之所以有一个别名叫多态性工厂模式是因为具体工厂类都有共同的接口&#xff0c; 或者有共同的抽象父类。 当系统扩展需要添加新的产品对象时&#xff0c;仅仅需要添加一个具体对象以及一个具体工厂对 象&#xff0c;原有工厂对象不需要进行任何修改&#xff0c;也不…

小程序设计二

八、使用背景图片&#xff08;background-image) 注意&#xff1a; url不能指定为本地地址可以将图片转化为base64方式使用。使用网络地址&#xff1a; background-image: url(https://img1.baidu.com); 考虑到版权问题&#xff0c;这里没有放具体路径。 使用base64格式指…

【Vue】可拖拽排序表格组件的实现与数据保存

1、描述 使用el-table-draggable组件来创建一个可拖拽的表格。 表格中的数据存储在tableData数组中&#xff0c;每个对象都有sortOrder、id、name和age属性。 当用户拖拽表格行并释放时&#xff0c;handleRowOnEnd方法会被调用&#xff0c; 更新tableData中每个对象的sortO…

SpringBoot 缓存

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 目录 一、缓存的作用二、SpringBoot启用缓存三…

腾讯云邮件推送如何设置?群发邮件的技巧?

腾讯云邮件推送功能有哪些&#xff1f;怎么有效使用邮件推送&#xff1f; 腾讯云邮件推送以其稳定、高效的特点&#xff0c;受到了众多企业的青睐。那么&#xff0c;腾讯云邮件推送如何设置呢&#xff1f;又有哪些群发邮件的技巧呢&#xff1f;下面AokSend就来详细探讨一下。 …

Express进阶升级

Express进阶升级&#x1f199; 本篇文章&#xff0c;学习记录于&#xff1a;尚硅谷&#x1f3a2; 文章简单学习总结&#xff1a;如有错误 大佬 &#x1f449;点. 前置知识&#xff1a;需要掌握了解&#xff1a; JavaScript基础语法 、Node.JS环境API 、前端工程\模块化、Expr…

nvm基本使用

nvm基本使用 文章目录 nvm基本使用1.基本介绍2.下载地址3.常用指令 1.基本介绍 NVM是一个用于管理 Node.js 版本的工具。它允许您在同一台计算机上同时安装和管理多个 Node.js 版本&#xff0c;针对于不同的项目可能需要不同版本的 Node.js 运行环境。 NVM 主要功能&#xff…

【Redis | 第十篇】Redis与MySQL保证数据一致性(两种解决思路)

文章目录 10.Redis和MySQL如何保证数据一致性10.1双写一致性问题10.2数据高度一致性10.3数据同步允许延时10.3.1中间件通知10.3.2延迟双删 10.Redis和MySQL如何保证数据一致性 10.1双写一致性问题 Redis作为缓存&#xff0c;它是如何与MySQL的数据保持同步的呢&#xff1f;特…

PHP 错误 Unparenthesized `a ? b : c ? d : e` is not supported

最近在一个新的服务器上测试一些老代码的时候得到了类似上面的错误&#xff1a; [Thu Apr 25 07:37:34.139768 2024] [php:error] [pid 691410] [client 192.168.1.229:57183] PHP Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : …

『FPGA通信接口』串行通信接口-SPI

文章目录 1.SPI简介2.控制时序3.Dual、Qual模式4.例程设计与代码解读5.SPI接口实战应用5.1时序要求5.2仿真时序图5.3代码设计 6.传送门 1.SPI简介 SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;通常说SPI接口或SPI协议都是指SPI这…

将文件导入数据库

#include <stdio.h> #include <sqlite3.h> #include <string.h> int main(int argc, const char *argv[]) { //打开数据库 sqlite3 *db NULL; if(sqlite3_open("./dict.db",&db) ! SQLITE_OK){ printf("sqlite…

5G随身WiFi推荐测评:品速5G VS 格行5G随身WiFi,随身wifi哪个品牌网速好?性价比更高?

玩游戏卡顿遭吐槽&#xff0c;直播掉线成笑柄&#xff0c;4G网络已难满足需求。5G随身wifi虽受追捧&#xff0c;但价格较高令人犹豫。面对众多品牌&#xff0c;随身WiFi哪个品牌靠谱呢&#xff1f;性价比高呢&#xff1f;今天就来测评一下口碑最好的无线随身WiFi格行5G随身wifi…

新能源车载芯片分析

新能源汽车市场正迸发出巨大的活力&#xff0c;传统主机厂和新势力都纷纷推出各种车型&#xff0c;打起了价格战&#xff0c;各种新技术让人眼花缭乱。当前&#xff0c;战场硝烟弥漫&#xff0c;新能源汽车公司犹如春秋时期的各诸侯国。车载芯片作为新能源汽车的关键组成部分&a…