天天看點

[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左右,确實比較高效

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

繼續閱讀