1 表面肌电研究——端点检测(活动段检测)

2019-04-13 14:39发布

1 表面肌电研究——端点检测(活动段检测)
1 基本知识
基于语音端点检测的方法有很多,从历史的发展来看。
首先是基于短时能量和短视过零率的端点检测=〉各变换域=〉人工神经网络=〉基于倒谱距离的检测算法=〉基于谱熵的方法=〉几何门限的方法=〉sigma函数=〉近些年的从分形技术和混沌理论引入的端点检测。
注:此段出自于“https://blog.csdn.net/ziyuzhao123/article/details/8932336”
表面肌电信号和语音信号具有较高的相似性,因此可参考相应的思路和方法,本文使用的是基于短时能量和方差和的端点检测,因为并不是所有的肌电信号均存在过零。
2 程序
本程序和网上的一些程序存在着一定的相似性,但根据其它文献和自己反复运行,对部分内容进行了纠错和改进
2.1 主程序 %表面肌电端点检测研究主程序 %说明: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %1.信号获取(原始数据) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %(1)传感器组成、数量、位置、编号 %肌电传感器,4通道, %肱二头肌(BB),01通道; %肱三头肌(TB),02通道; %桡侧腕屈肌(MFER),03通道; %指伸肌(EMF),04通道; %采样频率:2000(则采样时间间隔为0.0005s) %(2)关节、动作、次数(按动作顺序)、实验数据文档编号 %肘关节,屈曲(LULEF),3次,2018083001,并分成3部分,分别为2018083001-n,n=1,2,3.; clear;clc; lulef_ys=xlsread('2018083001-3.xlsx'); %导入数据 LData=length(lulef_ys); %查看原始数据长度,即采样点个数 lulef_ys_time=lulef_ys(:,1); %获取原始数据中的时间,时间为第1列 lulef_ys_bb=lulef_ys(:,4); %获取原始数据中01通道的数据 lulef_ys_tb=lulef_ys(:,5); %获取原始数据中02通道的数据 lulef_ys_mefr=lulef_ys(:,6); %获取原始数据中03通道的数据 lulef_ys_emf=lulef_ys(:,7); %获取原始数据中04通道的数据 lulef_ys1=[]; %先建立一个空矩阵,然后将获取原始数据中各个通道的数据放到一个矩阵中 lulef_ys1=[lulef_ys_bb,lulef_ys_tb,lulef_ys_mefr,lulef_ys_emf];%将获取原始数据中各个通道的数据放到一个矩阵中 figure(1); %用图形查看原始数据,这里是把4个通道分开查看,也可放在一起 subplot(4,1,1) plot(lulef_ys_time,lulef_ys_bb); subplot(4,1,2) plot(lulef_ys_time,lulef_ys_tb); subplot(4,1,3) plot(lulef_ys_time,lulef_ys_mefr); subplot(4,1,4) plot(lulef_ys_time,lulef_ys_emf); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %2.信号采样(端点检测) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 说明: %(1)将端点检测的程序到end_detection.m中,便于简化程序; %(2)本程序主体也是从网上下载来的,但对于我采集到的肌电信号不适用,又进行了修改,同时将原程序的算法亦作修改 %(3)下面是每次动作检测一次,这就是为什么上面分成9部分 % 基于短时能量及过零率的端点检测方法1 lulef_ys_start=ones(1,1); %用来储存endpoint_detection.m解算的来的起点值,因为一次一次的检测,所以是1,1 lulef_ys_end=ones(1,1); %用来储存endpoint_detection.m解算的来的终点值,与上同理 [lulef_ys_start(1),lulef_ys_end(1)]=endpoint_detection(lulef_ys1);%使用endpoint_detection.m解算的来的起点、终点值 lulef_cy1=lulef_ys1(((lulef_ys_start(1)-1)*20):((lulef_ys_end(1)-2)*20+64),:);%用解算得到的起点、终点值来截取数据段 lulef_ys_time_start=zeros(1,1); %用来储存对应起点值的时间 lulef_ys_time_end=zeros(1,1); %用来储存对应终点值的时间 for i=1:1 %截取数据段的时间段起点、终点时间值 lulef_ys_time_start(i)=(lulef_ys_start(i)-1)*20*0.0005+lulef_ys_time(1,1)%这里的+lulef_ys_time(1,1)是为了每一次都加上之前的时间段,保持连续 lulef_ys_time_end(i)=((lulef_ys_end(i)-2)*20+64)*0.0005+lulef_ys_time(1,1) end lulef_cy_time=lulef_ys_time_start(1):0.0005:lulef_ys_time_end(1);%截取数据段时间值 LData2=length(lulef_cy1(:,1)) %查看截取数据段数据长度 figure(3); %用图形查看截取数据段数据 plot(lulef_cy_time,lulef_cy1); 2.2 端点检测程序 function [x1,x2] = endpoint_detection(x) %%==运行程序之前先在命令窗口运行下段代码,用以添加voicebox工具箱==%% % cd D:Program FilesMATLABR2010b oolbox % path(' D:Program FilesMATLABR2010b oolboxvoicebox',path) %%==运行程序之前先在命令窗口运行上段代码,用以添加voicebox工具箱==%% %注:voicebox工具箱的添加方式有两种,其中一种是永久的,具体方法详见: %https://blog.csdn.net/jojozhangju/article/details/19337165 %幅度归一化到[-1,1],是为了加快数据收敛 %本试验使用的是4通道,根据具体情况可整体修改 x = double(x); x(:,1) = x(:,1) / max(abs(x(:,1))); %01通道数据进行幅度归一化处理 x(:,2) = x(:,2) / max(abs(x(:,2))); %02通道数据进行幅度归一化处理 x(:,3) = x(:,3) / max(abs(x(:,3))); %03通道数据进行幅度归一化处理 x(:,4) = x(:,4) / max(abs(x(:,4))); %04通道数据进行幅度归一化处理 nx=length(x(:,1)); %获取01通道数据长度 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %这里使用的是加窗的方式,即将原来的数据分成多个小窗,并添加一定的窗移,这样就能这 %段信号从头到尾处理一遍 %常数设置 FrameLen = 64; %帧长为64点,可按实际情况而定 FrameInc = 20; %帧移为20点,可按实际情况而定 energy =0.02; %初始短时能量低门限,这个是自己随便 %定的,仿真过程中可自行修改,查看下其规律 sum_s=0.3; %表征波动的方差和低门限,这个同样是 %自己随便定的 %对4通路开始信号分帧 channal1 = enframe(x(1:end-1,1), FrameLen, FrameInc);%enframe为分帧函数,是对x(1:end-1)分帧,帧长FrameLen,帧移FrameInc, channal2 = enframe(x(1:end-1,2), FrameLen, FrameInc);%返回值channal1是nf(帧数)x FrameLen(帧长)的数组,每一行是一帧 channal3 = enframe(x(1:end-1,3), FrameLen, FrameInc); channal4 = enframe(x(1:end-1,4), FrameLen, FrameInc); nf = fix((nx-FrameLen+FrameInc)/FrameInc);%nf帧数 % x_nf=nf; energy_frame=zeros(1,nf); sum_s_frame=zeros(1,nf); channal_energy=zeros(1,4); %要与上面的数量一致,这里是4; channal_sum_s=zeros(1,4); %要与上面的数量一致,这里是4; for i=1:nf-1 %求8路通道的每帧能量 channal_energy(1)=sum(channal1(i,:).^2); channal_energy(2)=sum(channal2(i,:).^2); channal_energy(3)=sum(channal3(i,:).^2); channal_energy(4)=sum(channal4(i,:).^2); energy_frame(i)=sum(channal_energy)/64;%energy_frame数组存放着每帧数据的短时能量,因为之前的帧长是64,所以为了求每帧的帧长,则需要再除以一个64 channal_sum_s(1)=sum((channal1(i,:)-mean(channal1(i,:))).^2);%这个原来网上常见的程序存在个小错误,此处已改正 channal_sum_s(2)=sum((channal2(i,:)-mean(channal2(i,:))).^2); channal_sum_s(3)=sum((channal3(i,:)-mean(channal3(i,:))).^2); channal_sum_s(4)=sum((channal4(i,:)-mean(channal4(i,:))).^2); sum_s_frame(i)=sum(channal_sum_s)/64.0;%sum_s_frame数组存放着每帧数据的方差和 end %调整能量门限 %说明,之前网上的程序使用的门限是energy、sum_s,这里进行了修改,通过研究发现 %energy对起止点选择不如sum_s的大,且可以选取不同的sum_s值,使得起点、终点的条件不同,更灵活一些 energy=min(energy,0.02*max(energy_frame)) %里面的0.02及下面的0.01、0.28是可以自己改的,如果可以的话,也可以采用自适应的方法,自动调整参数 sum_s1=min(sum_s,0.01*max(sum_s_frame)) sum_s2=min(sum_s,0.28*max(sum_s_frame)) %开始端点检测 x1=0; x2=0; for k=1:nf-1 if energy_frame(k)>energy&sum_s_frame(k)>sum_s2 x1=k; break; end end for m=x1:nf-1 if energy_frame(m) 3 结语
从仿真运行的情况来看,如若能将双门限自适应选择,就可以不能手动再进行修改,请大家尝试~ 本人也还是小学生一枚,上面难免有错误之处,如有,大家多多批评指正~
接下来将继续研究其他的端点检测方法,如大家有比较好的资料可交流分享,谢谢~
特别说明:
如需样本数据和其他材料,可在此页面下载:https://download.csdn.net/download/weixin_43409521/10719097