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左右,确實比較高效
如各位有更好的解析方法,希望可以共同探讨,本文的作用在于分享思路,共同進步。