[Matlab脚本]如何解析标准CAN报文

matlab版本:2016b
操作系统:win7
CAN卡:国产CAN卡
采集软件:ECANTOOL

测试过程中需要现场采集CAN报文数据,并对数据进行解析;
数据的组成如下:
标准帧;
发送周期不一致;
存在多种不同的报文;
不同报文信息需要不同的解析方法
可对特定的数据进行绘图处理

CAN报文的格式:

index time Name ID Type Format Len Data
00003E5D 0.004.922 接收 252 DATA STANDARD 8 06 00 06 00 00 00 00 00

首先,通过CAN卡软件采集获得的数据,保存为文本,故需要先按照字符串的方式对数据进行读取。

	fileName = 'example.txt';
	fileID = fopen(fileName);
	Data_Input = textscan(fileID,'%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s ');

读取后将ID号,8个字节的数据进行分组,对每组进行十六进制转十进制操作,可以获得转换后的数据,且此时保存的数据为十进制数值,将CAN报文数据拼接成完整的数据,并删除临时数据

	%报文的序号,暂时无用途
	CAN_Num = Data_Input{1, 1};
	%报文的时间,其计算方式为接收报文的间隔时间(相邻的报文)
	CAN_Time = Data_Input{1, 2};
	%报文的ID号数值,用于分组ID号
	CAN_ID = Data_Input{1, 4};
	%CAN_ID可以通过hex2num()函数转换成数组,索引的方式和元组有区别,需要注意
	%查找报文的ID号的种类,方便后续创建变量使用
	CAN_ID_Unique = unique(CAN_ID);
	%十六进制转十进制
	CAN_DATA_0 = hex2dec(Data_Input{1,8});
	CAN_DATA_1 = hex2dec(Data_Input{1,9});
	CAN_DATA_2 = hex2dec(Data_Input{1,10});
	CAN_DATA_3 = hex2dec(Data_Input{1,11});
	CAN_DATA_4 = hex2dec(Data_Input{1,12});
	CAN_DATA_5 = hex2dec(Data_Input{1,13});
	CAN_DATA_6 = hex2dec(Data_Input{1,14});
	CAN_DATA_7 = hex2dec(Data_Input{1,15});
	%拼接数组
	CAN_DATA = [CAN_DATA_0 CAN_DATA_1 CAN_DATA_2 CAN_DATA_3 CAN_DATA_4 CAN_DATA_5 CAN_DATA_6 CAN_DATA_7  ];

首先根据ID号和ID号数组的成分,确认各ID创建数组的大小,对各个数组的大小进行初始化,此初始化的初始值为1,采用zeros()函数创建后加1获得全为1的数组,因为数组的索引时从1开始的,需要记住
方法1:创建所有的ID号数据数组,查找CAN_ID,通过switch匹配对应的ID号,并在对应的数组的后面添加新的元素

	for i = 1:m
	 switch CAN_ID{i} %如果此处CAN_ID为数组,修改为CAN_ID(i)
	     case '150'
		     Data_150(end+1,:)=CAN_DATA(i,:);
		 case ...
		     ...
		 ...
		 otherwise 
	end

此方法的问题:在每个循环中都会改变数据数组的大小,操作时间长,并且拓展不方便,如果需要添加解析数据,需要修改多处。在解析样本(100兆左右)时耗时23s左右

方法2:做法1中循环中动态修改数组的大小,耗时长,如果可以提前知道数组的最终大小,提前分配好存储空间,操作改为索引的方式,可以节省时间,但是需要添加对应的counter用于计数,counter需要初始化为1

	result_0=tabulate(CAN_ID);
	result=result_0(:,1);
	count=result_0(:,2);
	for i = 1:size(result)
		case '150'
		  DATA_150=zeros(8, count{i});
		  Counter_150=1;
		case '151'
		  ...
		...
		otherwise
	end 

	for i = 1:m
	 switch CAN_ID{i} %如果此处CAN_ID为数组,修改为CAN_ID(i)
	     case '150'
		     Data_150(Counter_150,:)=CAN_DATA(i,:);
		     Counter_150=Counter_150+1;
		 case ...
		     ...
		 ...
		 otherwise 
	end

方法2改进了无需动态分配,节省了时间,但仍存在扩展等问题,如需要解析新的报文仍需要多处修改。在解析样本(100兆左右)时耗时14s左右

方法3:首先将CAN_ID进行了十六进制到十进制的转换,获得了CAN_ID的数组,创建ID号的计数器数组,并根据ID号和数量,创建对应ID号的数组;对输入的ID号在ID数组中进行索引,读取对应ID号的计数器数值,根据ID号,计数器数值将数据存放到指定的元组中

	temp_size=size(CAN_ID_Unique);
	Counter=zeros(1,temp_size(1))+1;%ID号的计数器,初始化为1
	clear temp_size
	% DATA_TEMP=zeros(1);
	for i = 1:size(result)
		%根据不同ID,不同的大小进行初始化
	    DATA_TEMP{i}=zeros(8, count(i));
	end
	
	for i = 1:m
	  temp_ID=CAN_ID(i);
	  %查找输入的ID号在ID数组中的索引号
	  index = find(temp_ID==CAN_ID_Unique);
	   %读取对应ID号的计数器数值
	  index_1=Counter(index);
	  %将对应的数据存储到对应ID号的指定计数器索引位置
	  DATA_TEMP{index}(index_1,:)=CAN_DATA(i,:);
	  %计数器自加1,自动偏移
	  Counter(index)= Counter(index)+1;
	end

此方法模糊了固定ID号,而采用的是ID数组和数据元组的方式进行存储,如需要读取相应的ID号数据则只需要

	index=find(hex2dec('190')==CAN_ID_Unique);
	DATA_190=DATA_TEMP{index};

就可以读取对应的ID的数据值,后期可以将上述的操作简化成一个函数,直接通过函数的调用,最终输出一个ID数组和数据数组,通过ID数组可以进行索引。此方法在样本解析过程中用时7s左右,确实比较高效

如各位有更好的解析方法,希望可以共同探讨,本文的作用在于分享思路,共同进步。

你可能感兴趣的:(Matlab编程)