(二)数据预处理
在准备阶段,我们取得了突破性的进展,全面掌握了这个题目的来源和做法,下面开始做第一问。
解题思路
明确技术路线
开始做题目之前,在前面的基础工作开展后,就可以明确汽车行驶工况构建的基本研究技术路线了:
在前面两个研究内容上,题目已经给出了最终的成功,也就是数据给我们了。但是首先得弄明白:数据怎么得来的?数据采集自哪个地区?驾驶员是哪一类人?车辆是什么类型的?
这些从所给文件,以及题目介绍的内容,加上文献的中一些信息,整合就可以得到。即:
研究人员采集的是福建省福州市和莆田市,采集的对象是轻型汽车,涉及的应该是三个不同的驾驶员驾驶的不同汽车。
明确数据内容
题目给了三个文件,每个文件采自自同一辆车,采集时间基本上都是连续的一个星期。每个文件的数据也大致一样多。
文件 | 数据量 | 采集起始时间 | 采集终止时间 | 采集区域 |
---|---|---|---|---|
文件1 | 185725 | 2017/12/18 13:42:13 | 2017/12/24 13:37:49 | 福建省福州市 |
文件2 | 145825 | 2017/11/01 19:11:50 | 2017/11/07 19:09:48 | 福建省莆田市 |
文件3 | 164914 | 2017/12/01 19:43:57 | 2017/12/06 19:40:20 | 福建省福州市 |
可以看到,在起止时间范围内,按照每秒采集一组数据(采集频率为1Hz),那么必然数据量不止这么点!中间缺失了大量的因为时间不连续而丢失掉的数据!
此外,数据文件中的所有字段的运动学意义必须弄明白:
- 时间 这个好理解,就是每一秒记录一组数据,这个在时间维度上帮助我们分析数据是否存在异常丢失,是个很关键的字段。
- GPS车速 这个字段应该由GPS设备采集到经纬度地理位置后,根据采样时间间隔计算出来的,所以当车速异常时,极大可能就是GPS设备异常了,需要认真观察。
- XYZ三轴加速度 这个字段真的不太好确定具体含义,我的理解是,可能是车上的某个部位贴的一个传感器,XY方向为贴片贴合面两个方向,Z方向为垂直于贴片的方向。当汽车发生上下颠簸时,X方向会有加速度;当汽车发生急速转弯时,因为惯性会有侧边的Y方向加速度,而汽车前进的方向则是Z方向的加速度。不知道理解的对不对,但是其实这个参数意义不大,车的加速度可以直接由GPS车速除以时间间隔就行了。
- 发动机转速 一般来说,发动机转速增加意味着加速;当汽车发动机转速低于一定值时,可能就是怠速运行了。即此时汽车发动机在运转,但对外不输出功率;发动机转速长期为0,那就是汽车熄火了。
- 扭矩百分比 这个发动机转速和扭矩百分比是研究发动机的重要指标,但从数据上来看,基本上变化不大。
- 瞬时油耗 汽车在启动的瞬间,油耗会非常高,或者怠速和刹车时,也会比较高。
- 油门踏板开度 简单的理解就是,你踩油门的深度,油门越大,喷油越多,汽车加速。也就是我们所说的踩油门。
- 空燃比 这个是空气和燃料的混合百分比,具体怎么用,不是特别清楚。
- 发动机负荷百分比 发动机在某一转速下,当时发动机发出的功率与同一转速下所可能发出的最大功率之比,以百分数表示。。
- 进气量 进气量指汽车发动机进入外界清新空气的流量 。是发动机在工作时氧气的进入,才能保证正常运行。暂态工况下缸进气量的准确估计是提高发动机空燃比控制精度的有效措施之一
此外,一般汽车运行时,一般都会经历这么几种工作运行状况(简成工况),可以看作一个完整的周期运动:
数据预处理解题过程
问题分析
在题目中,题目给出了数据预处理中存在的几个需要处理的数据异常判断标准。在这里我们还是要再重新整理下,转变成可以建模的依据:
时间不连续问题
首先,因为GPS的问题,造成数据采集时间不连续,而题目的数据全部是连在一起的,所以第一步就需要把数据离散到对应的时间节点上,我称之为 ”时间对齐“ 处理。
GPS丢失问题
造成GPS丢失问题的原因一般就两个。
- 一个是由于设备自身故障,比如电路异常等,造成的设备短时异常,使得设备会在短暂的1s内,采集不到数据,造成时间不连续,这种情况持续时间很短;对于这样的数据,可以通过插值补齐(说白了就是前后数据的求均值)。
- 另外一个是由于外部原因,比如高层建筑物遮挡、屏蔽,过隧道等等,设备信号直接被屏蔽了,也就采集不到,造成时间不连续,这种情况持续时间比较长。可以直接剔除掉不考虑。
经纬度异常问题
根据还原经纬度发现,经纬度有存在全为0的情况,这样的数据要直接剔除掉。
长期停车(熄火/不熄火)问题
因为设备采集系统是安装在汽车上的,驾驶员是否熄火停车,将会直接影响到设备是否能供电的问题。若停车不熄火,设备有电,但是此时没必要采集数据,也还是采集了大量无效的数据,这样的数据直接剔除掉;若汽车停车也熄火,那么设备没有供电,也就采集不到数据,如果文件中有,那就直接剔除掉。
加速度/速度异常问题
加速度异常其实可以和速度异常放到一起,如果速度异常,那么很有可能加速度也会异常。一般来说轻型汽车速度不会超过120km/h。对于加速度题目也给出了判断依据,加速度不超过4米每二次方秒,减速度不超过8每二次方秒。对于上面这种问题数据,直接予以剔除。
长期堵车、低速行驶问题
因为堵车导致汽车时而加速,时而减速,但速度都不大(不超过10km/h),这种情况下的数据,按照怠速处理,也就说直接算成 速度为0。
怠速异常问题
速度为0的情况下,假若持续时间超过了180s,一般认为此时汽车已经停车了,这养的数据是无效的,予以剔除。
毛刺问题处理
所谓的毛刺数据就是指,速度不为零,但是持续时间不超过4s,这样的数据一般速度都不会很大,属于测量误差造成的毛刺数据。这样的数据一般发生在怠速段,前后速度都一直为0,唯独中间那几秒速度大于0,这种情况下,直接将速度抹零,做怠速处理。
以上,基本上就是采集数据后可能出现的各种异常数据的定义和预处理方法了。
编程建模
时间对齐处理
首先,对于题目中所给的数据,获取到第一列的时间后,我们就需要找出采集时间的起止时间点了。然后计算出这期间应该有多少秒,那么就应该有多少条数据理论上被采集到。然后创建一个与与理论采集数据量对应的元胞,用来存储数据。当时间对应上时,则将原始数据的那一组(行)数据读取到,放到对应的时间那一行。这样写一个循环后。能够匹配上的数据,就找到了自己应该呆的时间点位置。以上处理方法我称之为 时间对齐
处理。
这里面涉及到如何将字符串时间转换成数字时间的问题,下面展示代码。
- 字符时间转数字代码(元胞类型)
function y = date2second2(x_cell)
m = length(x_cell);
y = zeros(m,1);
for i = 1:m
x =x_cell{i};
x_day =str2double(x(9:10));
x_hour =str2double(x(12:13));
x_min =str2double(x(15:16));
x_second =str2double(x(18:19));
y(i) = x_day*60*60*24 + ...
x_hour*60*60 + ...
x_min*60 + ...
x_second;
end
end
对于一个存有时间的字符串数组,他一般长这样: 2017/12/01 13:41:28.000
为了避免转成数字后,数值过大,浪费内存,我们只需要从天数开始,将时间转成秒单位。因为采集的时间是一个月里的连续几天。(以上代码也可以去掉循环,转成单一字符转数字的函数)。
- 字符时间转数字代码(字符串数组类型)
function y = date2second(x)
x_day =str2double(x(9:10));
x_hour =str2double(x(12:13));
x_min =str2double(x(15:16));
x_second =str2double(x(18:19));
y = x_day*60*60*24 + ...
x_hour*60*60 + ...
x_min*60 + ...
x_second;
end
- 时间对齐处理
function datanew = timeAlign(data)
% 计算有记录GPS的时间
record_time = date2second2(data(2:end,1))';
% 计算起止点
time_start = date2second(data{2,1});
time_end = date2second(data{end,1});
time = time_start:time_end;
% 时间对齐,查找对齐序号
[~,col] = ismember(record_time,time);
errodataNum = length(time) - length(col);
text1 = sprintf('因GPS设备断电或者异常导致数据缺失了%d条数据,正在补齐...\n',errodataNum);
fprintf(text1);
% 构造修复后元胞
data_deal = cell(length(time)+1,size(data,2));
data_deal(:) = {nan};
data_deal([1,col+1],:) = data;
datanew = data_deal;
% 时间还原
datanew(2:end,1) = second2date(data{2,1},time);
fprintf('时间对齐完成!\n');
end
基本原理就是找到对应的行号,将数据存进去。但是要注意一点,初始化时,先全部赋值为NAN,这样画图的时候,,没有数据的会是空白的点,方便观察。此外,为了对整个操作可视化,穿插了一些提示性的文字,如 ”因GPS设备断电或者异常导致数据缺失了%d条数据,正在补齐“。
除了对齐,还需要将数字还原成与表一模一样的时间,这里需要反操作。
- 数字转时间
function time1 = second2date(firstdate,time)
m = length(time);
dateStart = firstdate;
time1 = cell(m,1);
for i = 1:m
day = fix(time(i)/(60*60*24));
hour = fix(rem(time(i),(60*60*24))/(60*60));
min = fix(rem(rem(time(i),(60*60*24)),(60*60))/60);
second = fix(rem(rem(rem(time(i),(60*60*24)),(60*60)),60)/1);
time_now = [day,hour,min,second];
time_text = cell(4,1);
for j = 1:4
if time_now(j) < 10
time_text{j} = sprintf('0%d',time_now(j));
else
time_text{j} = sprintf('%d',time_now(j));
end
end
text = sprintf('%s%s %s:%s:%s.000.',firstdate(1:8),time_text{1},time_text{2},time_text{3},time_text{4});
time1{i} = text;
end
基本原理就是找到起点时间,然后计算差值,还原成天、时、分、秒。
经纬度异常处理
经纬度在数据的第6列和第7列,我们要把数据全为0的那一行数据全部剔除,即除了时间外所有数据全部变成NaN。
- 经纬度异常处理
function datanew = locatErroDetct(data)
datanew = data;
% 提取经纬度数据
data_temp =sum( cell2mat(datanew(2:end,6:7)),2);
% 找到全为0的数据,将其剔除(置为nan)
erro_index = find(data_temp==0);
text1 = sprintf('找到经纬度异常数据%d条,正在剔除...\n',length(erro_index));
fprintf(text1);
datanew(erro_index+1,2:end) = {nan};
fprintf('经纬度异常处理完成!\n');
end
长期停车处理
长期停车研究的是速度、转速、油门这几个关键字段的数据。将连续速度为0,转速小于某个均值,油门开度为0的,且持续时间大于180s的数据,变成NaN。
- 长期停车处理
function datanew = carStopDtect(data)
datanew = data;
% 提取速度、转速、油门数据
data_temp = cell2mat(datanew(2:end,[2,8,11]));
% 找出速度为0的数据
v_index = find(data_temp(:,1)==0);
% 找出油门开度为0的数据
throttle_index = find(data_temp(:,3)==0);
% 找出速度和油门约束下的可疑数据
deal_index = intersect(v_index,throttle_index);
r_min = mean(data_temp(deal_index,2)); % 取转速均值作为怠速转速
% 找出怠速转速最小值
r_index = find(data_temp(:,2)<= r_min);
% 找到满足情况的数据
deal_index2 = intersect(deal_index,r_index);
% 找出连续的数据
deal_index3 = is_continue(deal_index2);
% 统计连续片段长度
for i = 1:length(deal_index3)
index3_length(i) = length(deal_index3{i});
end
% 找出超过180s的数据段
deal_index4 = find(index3_length > 180);
% 异常数据记录
erro_part = length(deal_index4);
erro_num = sum(index3_length(deal_index4));
text1 = sprintf('找到长期停车(熄火或者不熄火)片段%d个,共包含%d条数据,正在剔除...\n',erro_part,erro_num);
fprintf(text1);
% 对应连续数据段进行剔除(数据抹成nan)
for i = 1:length(deal_index4)
datanew(cell2mat(deal_index3(deal_index4(i)))+1,2:end) = {nan};
end
fprintf('长期停车异常处理完成!\n');
end
这里面需要判断数据是不是连续的这个,这个号判断,直接根据时间来,因为前面我们做了时间对齐处理!即只要时间序号上不连续,那就是不连续的!
- 判断数据连续
function data_slice = is_continue(data)
m = length(data);
k = 1;
data_slice = cell(m,1);
data_slice{1,1} = data(1);
for i = 2:m
if data(i) - data(i-1) == 1
data_slice{k,1} = [ data_slice{k,1},data(i)];
else
k = k+1;
data_slice{k,1} = data(i);
end
end
% 删除多余的空数据行
data_slice(all(cellfun(@(x) isempty(x),data_slice),2),:)=[];
end
这里的判断方法很好理解。对于一组数据,只要他满足递增,那就把他放到同一个元胞里,一旦检测到不是连续,那就转到下一个元胞,继续存储,直到循环结束。这时元胞会空出多余的行,把他们全部干掉就行了。
长期堵车、低速运行处理
长期堵车、低速运行一般是指,速度小于10km/h,连续时间超过180s的这样的数据。需要将他们全部变成NaN。
- 长期堵车、低速运行处理
function datanew = carTrafficDetect(data)
datanew = data;
% 提取速度
data_temp = cell2mat(datanew(2:end,2));
% 找出速度小于10km/h的数据
v_index = find(data_temp(:,1) < 10);
% 找出连续的片段
deal_index1 = is_continue(v_index);
% 统计连续片段长度
for i = 1:length(deal_index1)
index1_length(i) = length(deal_index1{i});
end
% 找出超过180s的数据段
deal_index2 = find(index1_length > 180);
% 异常数据记录
erro_part = length(deal_index2);
erro_num = sum(index1_length(deal_index2));
text1 = sprintf('找到堵车造成的持续低速片段%d个,共包含%d条数据,正在剔除...\n',erro_part,erro_num);
fprintf(text1);
% 对应连续数据段进行怠速处理(数据抹成0)
for i = 1:length(deal_index2)
datanew(cell2mat(deal_index1(deal_index2(i)))+1,2) = {0};
end
fprintf('堵车怠速异常处理完成!\n');
end
GPS瞬时丢失处理
GPS存在仅丢失1s的数据,这样的数据直接前后均值代替其即可。它满足前后不为空,但中间为空。还是不得不感谢时间对齐带来的编程上的好处啊!
- GPS瞬时丢失处理
function datanew = gpsMissDetect(data)
datanew = data;
% 提取速度
data_temp = cell2mat(datanew(2:end,2));
% 短时GPS丢失修复(2s均值插补法)
fix_num = 0; % 记录处理的数据个数
for i = 2:length(data_temp)-1
if ~isnan(data_temp(i-1)) && isnan(data_temp(i)) && ~isnan(data_temp(i+1))
data_temp(i) = mean([data_temp(i-1),data_temp(i+1)]);
fix_num = fix_num + 1;
end
end
for i = 2:length(data_temp)+1
datanew{i,2} = data_temp(i-1);
end
fprintf('找到GPS短时丢失数据(丢失1s)%d条,已通过插值修复!\n', fix_num);
end
加速度异常处理
加速度正负都有限制,通过速度可以求出来,找到异常的直接剔除。
- 加速度异常处理
function datanew = accelerateErrorDetect(data)
datanew = data;
% 提取速度
data_temp = cell2mat(datanew(2:end,2));
a = data_temp;
% 构造加速度计算数组
a_before = a(1:end-1);
a_after = a(2:end);
% 计算加速度
a_delta = a_after - a_before;
% 正加速度上限值
p_v = 100/7;
% 负加速度上限值
n_v = -8*3600/1000;
% 记录加速度异常值
p_index = [];
n_index = [];
for i = 1:length(a_delta)
if a_delta(i) > p_v
p_index = [p_index,i];
elseif a_delta(i) < n_v
n_index = [n_index,i];
end
end
% 序号挪到异常的位置
p_index = p_index + 1;
n_index = n_index + 1;
% 异常数据记录
fprintf('找到加速度异常数据%d条,其中异常加速%d条,异常刹车%d条,正在剔除...\n',...
length([p_index,n_index]),length(p_index),length(n_index));
erro_index = [p_index,n_index]+1;
for i = 1:length(erro_index)
datanew(erro_index(i),2:end) = {nan};
end
fprintf('加速度异常处理完成!\n');
end
速度异常处理
速度最大不超过120km/h,超过的直接剔除。
- 速度异常处理
function datanew = speedErrorDetect(data)
datanew = data;
% 提取速度
data_temp = cell2mat(datanew(2:end,2));
erro_index = find(data_temp > 120 );
text1 = sprintf('找到速度异常数据%d条,正在剔除...\n',length(erro_index));
fprintf(text1);
datanew(erro_index+1,2:end) = {nan};
fprintf('速度异常处理完成!\n');
end
毛刺数据处理
毛刺数据是速度大于0但是持续时间不大于4s的,直接剔除。
- 毛刺数据异常处理
function datanew = burrDetect(data)
datanew = data;
% 提取速度
data_temp = cell2mat(datanew(2:end,2));
% 找出速度大于0的数据
v_index = find(data_temp(:,1)>0);
% 找出连续的片段
deal_index1 = is_continue(v_index);
% 统计连续片段长度
for i = 1:length(deal_index1)
index1_length(i) = length(deal_index1{i});
end
% 找出长度小于4s的数据段
deal_index2 = find(index1_length < 4);
% 异常数据记录
erro_part = length(deal_index2);
erro_num = sum(index1_length(deal_index2));
text1 = sprintf('找到毛刺片段%d个,共包含%d条数据,正在怠速处理...\n',erro_part,erro_num);
fprintf(text1);
% 对应连续数据段进行怠速处理(数据抹成0)
for i = 1:length(deal_index2)
datanew(cell2mat(deal_index1(deal_index2(i)))+1,2) = {0};
end
fprintf('毛刺异常处理完成!\n');
end
单个文件数据处理
每一步的异常数据处理,输入和输出一模一样,这样就可以实现程序之间的对接了,我们把他们写到一起,组成一个完整的异常数据处理程序:
- 数据预处理
function dealFile(filename)
% 载入数据
path = filename;
[~,~,data] = xlsread(path);
fprintf('%s导入成功!一共导入%d条数据\n',filename,length(data)-1);
% 时间对齐处理
data1 = timeAlign(data);
% 经纬度异常处理
data2 = locatErroDetct(data1);
% 长期停车不熄火处理
data3 = carStopDtect(data2);
% 堵车怠速处理
data4 = carTrafficDetect(data3);
% GPS短时丢失处理
data5 = gpsMissDetect(data4);
% 加速度异常处理
data6 = accelerateErrorDetect(data5);
% 速度异常处理
data7 = speedErrorDetect(data6);
% 毛刺异常处理
data8 = burrDetect(data7);
% 存储导出数据
datanew = data8;
% 保存异常数据处理后的数据
save([filename,'数据预处理后'],'datanew');
fprintf('%s数据预处理完成!\n',filename);
end
最后得到的 data8
就是我们处理后的数据了,与原理的数据一模一样,但是该处理的都处理过了。
一共有三个文件,因此还需要写一个处理三个文件的主程序
处理前后对比
为了直观感受处理前后,数据的具体变化,还需要将数据处理前后的图画出来对比。代码如下
- 画处理前后数据 v-t 图
function drawVTpic(filename)
set(gcf,'outerposition',get(0,'screensize'))
% 导入异常处理前数据
[~,~,data1] = xlsread(filename);
plotdata1 = cell2mat(data1(2:end,2));
subplot(211)
plot(plotdata1)
grid on
set(gca,'fontsize',24)
xlabel('时间(s)'),ylabel('车速(km/h)');
set(gca,'GridLineStyle','--','GridColor','k','GridAlpha',1)
title([filename,'数据预处理前']);
% 导入异常处理后数据
load([filename,'数据预处理后'],'datanew');
data2 = datanew;
plotdata2 = cell2mat(data2(2:end,2));
x = data2{2,1};
x_hour =str2double(x(12:13));
x_min =str2double(x(15:16));
x_second =str2double(x(18:19));
t1 = (23-x_hour)*3600 + (59-x_min)*60 + (59-x_second)*1 +1;
subplot(212)
plot(plotdata2)
grid on
set(gca,'xtick',[1,t1:86400:length(plotdata2)],'xticklabel',0:1:6,'fontsize',24)
xlabel('时间(以天为网格单位)'),ylabel('车速');
set(gca,'GridLineStyle','--','GridColor','k','GridAlpha',1)
title([filename,'数据预处理后'])
print(gcf,'-djpeg','-r300',[filename,'数据预处理前后对比图']);
fprintf('%s数据预处理前后对比图绘制完成!\n',filename)
end
主程序
- 主程序
%% 准备存储空间
clc,clear,close all
filename = {'文件1','文件2','文件3'};
tic
for i = 1:length(filename)
dealFile(filename{i});
figure(i)
drawVTpic(filename{i});
fprintf('----------------------\n');
end
fprintf('所有文件数据异常处理完成!\n');
toc
计算结果
根据以上编程的模型代码,运行后输出结果如下:
可以看到,运行结束后,所有的结果保存为了对应的mat文件,供下一问使用。整个过程计算用时361.621267 秒。
结果输出文字如下:
文件1导入成功!一共导入185725条数据
因GPS设备断电或者异常导致数据缺失了332412条数据,正在补齐...
时间对齐完成!
找到经纬度异常数据3条,正在剔除...
经纬度异常处理完成!
找到长期停车(熄火或者不熄火)片段1个,共包含3174条数据,正在剔除...
长期停车异常处理完成!
找到堵车造成的持续低速片段12个,共包含6003条数据,正在剔除...
堵车怠速异常处理完成!
找到GPS短时丢失数据(丢失1s)110条,已通过插值修复!
找到加速度异常数据29条,其中异常加速27条,异常刹车2条,正在剔除...
加速度异常处理完成!
找到速度异常数据0条,正在剔除...
速度异常处理完成!
找到毛刺片段243个,共包含425条数据,正在怠速处理...
毛刺异常处理完成!
文件1数据预处理完成!
文件1数据预处理前后对比图绘制完成!
----------------------
文件2导入成功!一共导入145825条数据
因GPS设备断电或者异常导致数据缺失了372454条数据,正在补齐...
时间对齐完成!
找到经纬度异常数据300条,正在剔除...
经纬度异常处理完成!
找到长期停车(熄火或者不熄火)片段2个,共包含402条数据,正在剔除...
长期停车异常处理完成!
找到堵车造成的持续低速片段35个,共包含10529条数据,正在剔除...
堵车怠速异常处理完成!
找到GPS短时丢失数据(丢失1s)1722条,已通过插值修复!
找到加速度异常数据414条,其中异常加速332条,异常刹车82条,正在剔除...
加速度异常处理完成!
找到速度异常数据0条,正在剔除...
速度异常处理完成!
找到毛刺片段659个,共包含1077条数据,正在怠速处理...
毛刺异常处理完成!
文件2数据预处理完成!
文件2数据预处理前后对比图绘制完成!
----------------------
文件3导入成功!一共导入164914条数据
因GPS设备断电或者异常导致数据缺失了266870条数据,正在补齐...
时间对齐完成!
找到经纬度异常数据0条,正在剔除...
经纬度异常处理完成!
找到长期停车(熄火或者不熄火)片段6个,共包含1765条数据,正在剔除...
长期停车异常处理完成!
找到堵车造成的持续低速片段40个,共包含11277条数据,正在剔除...
堵车怠速异常处理完成!
找到GPS短时丢失数据(丢失1s)341条,已通过插值修复!
找到加速度异常数据154条,其中异常加速115条,异常刹车39条,正在剔除...
加速度异常处理完成!
找到速度异常数据313条,正在剔除...
速度异常处理完成!
找到毛刺片段376个,共包含664条数据,正在怠速处理...
毛刺异常处理完成!
文件3数据预处理完成!
文件3数据预处理前后对比图绘制完成!
----------------------
所有文件数据异常处理完成!
时间已过 361.621267 秒。
>>
下一阶段任务
完成第二问,点击查看