pscad仿真数据提取方法

如何把PSCAD的仿真数据导到matlab中?自己写了一个matlab小程序。数据可以保存在硬盘里,想提取哪个就提取哪个,想什么时候提取就什么时候提取,还可以大批量一次性搞定。
今天分享出来,欢迎各位提出改进意见!

用到的函数

  1. 打开和关闭文件
    filename=‘D:\simulation\pscad\1’;
    fid=fopen(filename)

    fclose(fid);

  2. 查找数据
    文件扫描——textscan()
    c1=textscan(fid,’%*s%s%*s%*s%*s%s%*s%*s%*s%n%s%s%[^\n]’,‘delimiter’,’”’,‘headerlines’,6);

  3. 格式转换
    数字转换为字符串——num2str()
    num2str(n,1)
    数字转换为元胞——num2cell()
    num2cell(i+n-1,1)
    字符串转换为浮点数——str2double()
    str2double(sequence{k(i)})+1

  4. 字符串操作
    字符串连接——strcat()
    strcat(name_pro(n2),’_’, num1)
    字符串比较——strcmp()
    strcmp(name{i},str_find)

  5. 数据处理
    取余——rem()
    rem(i-1,10)

思路

首先,需要在PSCAD中开启数据记录,并设置一个保存名称,例如data.out。在仿真中对自己想要采集的数据设置相应的output channel。例如
《pscad仿真数据提取方法》
只有设置了输出通道output channel的数据才能被采集下来,注意一下所采集数据的维度,最好是单维度的,这个原因在后面会讲。
《pscad仿真数据提取方法》

之后,每次运行结束后,PSCAD会在project文件所在的文件夹下生成一个后缀为.gf42的文件夹,如下图所示。仿真过程中记录的数据就保存在这个文件夹里。下次运行时生成的新的.gf42的文件夹会覆盖旧的文件,所以注意重命名。
《pscad仿真数据提取方法》
gf42文件夹里的内容如下所示:
《pscad仿真数据提取方法》

文件夹里的.out文件就是数据了。用记事本可以打开.out文件,但是打开后发现里面一堆的数据,数了数一共11列,第一列是时间序列。而像这样的文件可能有几十个。
那么问题来了,如何知道想找的数据在哪里呢?
《pscad仿真数据提取方法》

好在,后缀为.infx的文档是一个关于数据内容的菜单
《pscad仿真数据提取方法》
《pscad仿真数据提取方法》
经过验证,之前的.out文件中的数据与infx文件中的名称可以一一对应。
这下,有了名称、序列以及相应的数据,剩下的就是找到想要的数据,并把它们分别提取出来。

步骤

1. 从.infx文件中提取出需要的数据

我们需要的数据包括次序,名称以及维度。
利用textscan()函数可以从txt文件中提取出数据。

fid = fopen(filename);  %先打开文件,filename是.infx文件的地址
c1=textscan(fid,'%*s%s%*s%*s%*s%s%*s%*s%*s%n%*s%s%*[^\n]','delimiter','"','headerlines',6);
fclose(fid);
sequence=c1{ 1};
name=c1{ 2};
dim=c1{ 3};
unit=c1{ 4};

采用” ” “作为分隔符,前6行不是我们需要的,因此要跳过。采集出来后分别存好。

2. 处理多维数据

有了名单,找到相应的数据就好办了。例如,我们要找的是数据A,我们可以从之前提取的名单里面找到A的次序编号,然后再根据一个文件里一共有11列,就能算出编号所在的是第几个文件夹里的第几列了。
然而,实际操作发现,有的数据维度是多维的,例如有的电流是三相的,三列数据共同用一个名字。这种情况下,使用之前的序列编号直接计算地址就会出错了。这时当初提取出来的维度数据就有用了。
根据维度数据,可以将实际的各个变量数据的编号计算出来,还可以顺便给它们分别命好名字。(当然,也可以从根源上解决,确保仿真的数据都是单维的)

sequence_pro=cell(sum0,1); %序号也要重新定义
i=1 ;     %数据的个数
n=1;      %多维数据里的次序
x=1 ;     %循环的次数
%由于很多数据是多维的且没有相应的名字,所以要利用维度这个数据对多维的数据重新起名字
%就是在多维的数据的名字后面加上序号
for x=1:size(dim,1)
   num=dim(x);         %根据维度进行判断
   if num>1           %如果该数据的维度不为1
      for n=1:num     %1到最大的维度进行循环
         num1=num2str(n,1); %将该序号转为字符格式,为拼接作准备
         name_pro(i+n-1)=strcat(name(x),'_',num1); %在名字后面加上序号,表示多维数据内容
         sequence_pro(i+n-1)=num2cell(i+n-1,1);    %将序列补充完整
      end
    elseif num<1      %这里发现结尾的维度数据有0出现,当出现0时,说明已经处理完了
       break
    else
       name_pro(i)=name(x); %如果是单维的数据就直接按照之前的名字命名即可
       sequence_pro(i)=num2cell(i,1);  %序号也是一样
    end
    i=i+num;   %数据的个数加上维度数目
    x=x+1 ;    %当前名字的数据的个数已经处理完,进行下一个名字的操作
end

处理完多维数据的问题,就可以准确定位数据了。

3. 定位并提取数据

搜索名称->记录序列编号->计算所在文件夹个数和数据列数->去相应的地址中提取。

line_name=cell(size(set,2),1); %将线的名称存在数组里
Data_base=ones(length(time),1)*nan;    %存储数据的矩阵预定义
%记录存储数据的数目
n3=1;

str_find=set{ 1,fig_l};
%搜索所需要的数据
n=max(size(name));
k=ones(1,1)*nan;
for i=1:n
    if strcmp(name{ i},str_find)  %从name数组中找相同名字的项
        [b]=i;    %i是name中的该项的位置
        k=[k,b];
    end
end
k(:,1)='';
if k(1,1)< 1    %这里想写一个错误处理,但是没成功,反正如果在这里报错,就看str_find,那个就是没找到的变量
    print='未找到数据!!!';
    pause;
end   

%给没在一起的三相数据命名
if max(size(k))>1                                   %这里有一点不是很好,就是当数据被第二次使用时,依照这种方法就无法找到了,需要加上后缀才行
    for i=1:max(size(k))
        n2=str2double(sequence{ k(i)})+1;
        num1=num2str(i);
        name_pro(n2)=strcat(name_pro(n2),'_',num1); %修改三相数据的名字
    end
end 
%提取相应数据

for u=1:max(size(k))                 % k有可能是三相数据,所以是数组
    n2=str2double(sequence{ k(u)})+1;
    for i=n2:n2+dim(k(u))-1          %从维度开始的序号到维度结束的序号
        pick2=pick;                  %准备删除*号
        txtn=ceil(i/10);             %文件数是除以10再取整,ceil是向上取整
        column=rem(i-1,10)+1;        %列数是除以10再取余,这里i-1是因为当i=10时,取余为0,通过i-1再取余最后+1可以解决这个问题
        pick2(pin(column))='';       %删除*号,产生提取的格式
        fid=fopen(faddress{ txtn});
        c1=textscan(fid,pick2,'headerlines',1);
        fclose(fid);
        a=c1{ 1};
        Data_base=[Data_base,a];                     %保存数据
        line_name(n3)=name_pro(i);   %保存相应数据的名称
        n3=n3+1;                     %数据数目累加
    end
end

4. 补充说明

data文件有多个,提取文档需要用到他们的地址,手动保存会很累,下面的程序可以自动生成他们的地址。

n=max(strfind(filename,'\'));
n1=max(strfind(filename,'.'));
freename=filename(n+1:n1-1);  %提取自定义的存储名
headad=filename(1:n);%提取后面要用到的地址头部,一直到数据文件名前的\
%下面的循环是为了通过拼接产生地址清单,为后面的提取数据作准备
for i=1:total            %循环次数为总的文件的个数,一个文件提取一次
      n=num2str(i);      %整数转换为字符串格式,为拼接作准备
      if i<10           %1-9的文件名下划线后面有0
          faddress_temp=strcat(headad,freename,'_0',n,'.out');  %拼接
      else
          faddress_temp=strcat(headad,freename,'_',n,'.out') ;  
      end
      faddress(i)={ faddress_temp};  %保存地址
end

程序内容

完整的程序内容如下。

%% PSCAD_data_manage.m

%该程序可以提取所需要的PSCAD自动存储的数据

%输入要提取的变量名称,可以参照.infx
set={ '变量1','变量2','变量3','变量4'};

%输入数据存储地址
filename='D:/simulation/1/';

pick='%*f%*f%*f%*f%*f%*f%*f%*f%*f%*f%*f';
pin=[5 8 11 14 17 20 23 26 29 32];
n=max(strfind(filename,'\'));
n1=max(strfind(filename,'.'));
freename=filename(n+1:n1-1);  %提取自定义的存储名
headad=filename(1:n);%提取后面要用到的地址头部,一直到数据文件名前的\

%提取数据的标题
fid = fopen(filename);
c1=textscan(fid,'%*s%s%*s%*s%*s%s%*s%*s%*s%n%*s%s%*[^\n]','delimiter','"','headerlines',6);
fclose(fid);
sequence=c1{ 1};
name=c1{ 2};
dim=c1{ 3};
unit=c1{ 4};

i= ~isnan(dim);
dim=dim(i);                   %去掉里面的nan项,否则无法相加

sum0=sum(dim(:));             %所有的数据条数
sum0=uint32(sum0);
name_pro=cell(sum0,1);     %预定义所有的名称数组空间
sequence_pro=cell(sum0,1); %序号也要重新定义
i=1 ;     %数据的个数
n=1;      %多维数据里的次序
x=1 ;     %循环的次数
%由于很多数据是多维的且没有相应的名字,所以要利用维度这个数据对多维的数据重新起名字
%就是在多维的数据的名字后面加上序号
for x=1:size(dim,1)
   num=dim(x);         %根据维度进行判断
   if num>1           %如果该数据的维度不为1
      for n=1:num     %1到最大的维度进行循环
         num1=num2str(n,1); %将该序号转为字符格式,为拼接作准备
         name_pro(i+n-1)=strcat(name(x),'_',num1); %在名字后面加上序号,表示多维数据内容
         sequence_pro(i+n-1)=num2cell(i+n-1,1);    %将序列补充完整
      end
    elseif num<1      %这里发现结尾的维度数据有0出现,当出现0时,说明已经处理完了
       break
    else
       name_pro(i)=name(x); %如果是单维的数据就直接按照之前的名字命名即可
       sequence_pro(i)=num2cell(i,1);  %序号也是一样
    end
    i=i+num;   %数据的个数加上维度数目
    x=x+1 ;    %当前名字的数据的个数已经处理完,进行下一个名字的操作
end

sum0=double(sum0);
%处理完标题,下面对具体数据的提取作准备
total=ceil(sum0/10);     %计算总的数据存档文件个数
faddress=cell(total,1);  %预定义文件的地址数组空间

%下面的循环是为了通过拼接产生地址清单,为后面的提取数据作准备
for i=1:total            %循环次数为总的文件的个数,一个文件提取一次
      n=num2str(i);      %整数转换为字符串格式,为拼接作准备
      if i<10           %1-9的文件名下划线后面有0
          faddress_temp=strcat(headad,freename,'_0',n,'.out');  %拼接
      else
          faddress_temp=strcat(headad,freename,'_',n,'.out') ;  
      end
      faddress(i)={ faddress_temp};  %保存地址
end
fid = fopen(faddress{ 1});
c1=textscan(fid,'%f%*[^\n]','headerlines',1);
fclose(fid);
time=c1{ 1};
                                                                 %%
                                                %之前为预定义环节,之后才是具体的提取数据

%设置一个矩阵,根据矩阵中的数字,就可以找到相应的数据名

line_name=cell(size(set,2),1); %将线的名称存在数组里
Data_base=ones(length(time),1)*nan;    %存储数据的矩阵预定义
%记录存储数据的数目
n3=1;

for fig_l=1:size(set,2)
    str_find=set{ 1,fig_l};
    %搜索所需要的数据
    n=max(size(name));
    k=ones(1,1)*nan;
    for i=1:n
        if strcmp(name{ i},str_find)  %从name数组中找相同名字的项
           [b]=i;    %i是name中的该项的位置
           k=[k,b];
        end
    end
    k(:,1)='';
    if k(1,1)< 1        %想写错误处理,没成功,如果在这里报错,就看str_find,那个就是没找到的变量
       print='未找到数据!!!';
       pause;
    end   

    %给没在一起的三相数据命名
    if max(size(k))>1                                   %这里有一点不是很好,就是当数据被第二次使用时,依照这种方法就无法找到了,需要加上后缀才行
        for i=1:max(size(k))
    %             n2=str2num(sequence{ k(i)})+1;               %要知道次序数组里的该位置对应的次序数才能知道该项的name_pro数组里的名字
            n2=str2double(sequence{ k(i)})+1;
            num1=num2str(i);
            name_pro(n2)=strcat(name_pro(n2),'_',num1); %修改三相数据的名字
        end
    end 
    %提取相应数据

    for u=1:max(size(k))                 % k有可能是三相数据,所以是数组
    %         n2=str2num(sequence{ k(u)})+1;    %找到相应的包含有维度的数据在表格中的序列号
        n2=str2double(sequence{ k(u)})+1;
        for i=n2:n2+dim(k(u))-1          %从维度开始的序号到维度结束的序号
            pick2=pick;                  %准备删除*号
            txtn=ceil(i/10);             %文件数是除以10再取整,ceil是向上取整
            column=rem(i-1,10)+1;        %列数是除以10再取余,这里i-1是因为当i=10时,取余为0,通过i-1再取余最后+1可以解决这个问题
            pick2(pin(column))='';       %删除*号,产生提取的格式
    %             [a]=textread(faddress{ txtn},pick2,'delimiter',' ','headerlines',1);%提取数据
            fid=fopen(faddress{ txtn});
            c1=textscan(fid,pick2,'headerlines',1);
            fclose(fid);
            a=c1{ 1};
            Data_base=[Data_base,a];                     %保存数据
            line_name(n3)=name_pro(i);   %保存相应数据的名称
            n3=n3+1;                     %数据数目累加
        end
    end
end

Data_base(:,1)='';                   %去掉空列                

如果帮到了大家,点个赞再走呗~

    原文作者:什么网名好记
    原文地址: https://blog.csdn.net/qq_36976807/article/details/105439321
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞