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 Files\MATLAB\R2010b\toolbox
% path(' D:\Program Files\MATLAB\R2010b\toolbox\voicebox',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