简单介绍一下MATLAB中的imshow函数。
imshow(array,[low,high])
其中,array为输入矩阵,array中数值 <= low的元素将在figure窗口中显示为黑色,而 >= high的元素将显示为白色,介于low和high之间的元素则显示为不同程度的灰色,越靠近low则越趋近于黑色。
用MATLAB函数写的NaSch单车道模型的可视化就全靠imshow函数。
还有一个必须要说的特殊值是NaN(nan),即not a number的缩写,nan有几个特殊性。
第一,nan与任何数参与运算结果任然为nan。
第二,nan不是一个数字或数值,即,nan和nan是有区别的,就如同全体实数和全体偶数两个无穷也是有区别的一样,在MATLAB中任意两个nan是不同的。
第三,nan也可以做函数使用,nan()的使用方法同zeros()或ones()。
因为nan的特殊性所以可以用nan表示元胞无车状态,0表示静止的车,这样就可以将无车和静止的车区分开。
除此以外nan做imshow函数的输入参数任然具有特殊性。
用一个简单的例子说明nan在imshow函数中参数的特殊性。
可以看到在imshow(array,[low,high]),当arry中含有nan时,nan将永远视为比low小(实际nan和任何一个数比较返回的逻辑值都为0),之前已将说了用nan表示元胞无车状态,所以当将nan作为imshow输入参数时车道无车位置就永远为黑色,这显然不是我们要的结果。
解决方法是除v(储存车辆速度信息的矩阵)外再定义一个3*space_range(1,3,行为路侧,2行为车道)的space矩阵对v进行改造就可以了进行以下操作就可以作为imshow的输入参数了,其中,space_range为道路长度。
所以我们用imshow函数可视化的基本思想就是用v储存车辆速度信息,其中nan表示无车,0表示静止车辆,数字表示车辆速度,再用space作为imshow输入参数,其中space是将v中的值为nan的元素设为1,再令space第二行(车道)非nan元素全部取反(迎合imshow第二个特性),这样这样因为space取反后必定1(之后会将nan转换为1来配合imshow输出)最大,所以无车为空,而车辆颜色则随着速度变大而颜色趋近于黑色。
以下简述操作过程。
创建v和space矩阵
v = NaN (1,space_range); %创建v来存放time时间步的各元胞上的车辆速度
%% 创建用于储存输出图片时用的space
space=zeros(3,space_range);
space([1,3],:) = 0.5; %车道显示为灰色
这样我们就可以输出初始的道路情况(车辆投放,参数设置后面代码中有,在此不再赘述)
%% 显示初始交通流图
figure('name','NaSch单车道模型','position',[241 132 560 420],'doublebuffer','on');%窗口名称,位置,双缓存
space(2,:) = -1*space(2,:);
H=imshow(space,[]);
title('NaSch单车道模型','color','red');
这里需要注意的是,space要取反,否则无车为黑,有车为白,与我们一般习惯不同。
最后在时间步循环中的最后再一行输出图像
%% 显示交通流图
space(2,:) = v; %space第二行为车道
space = -1*v; %因为imshow函数特性,space中数值取反再输出
space(isnan(space)) = 1; %无车处显示为白色
set(H,'CData',space);
%% 输出图片变换间隔时间
pause(time_span);
这里注意
第一,isnan(array)函数可以返回和array类型相同的逻辑矩阵,其中array元素为nan处值为1,否则为0。
第二,set函数的调用格式为:
set(句柄,属性名1,属性值1,属性名2,属性值2,…)
即set函数可以更改句柄(可以理解为高级指针)指向对象的属性值
第三,pause(a)函数的作用是程序执行到此暂停a秒继续执行。
我贴出完整代码
clear;clc;
%% 参数设置
space_range=100; %空间范围(车道长)
MinTime = 0; %最小时间步
MaxTime = 1000; %最大时间步
v_max = 5; %允许最大车速
pro_slowdown=0.3; %慢化概率
time_newcar=1; %每隔time_newcar时间步放一辆车
time_span = 0.1; %图片输出时间间隔
%% 准备空间
memory=[]; %创建memory来储存每个时间步产生的数据
v = NaN (1,space_range); %创建v来存放time时间步的各元胞上的车辆速度
%% 创建用于储存输出图片时用的space
space=zeros(3,space_range);
space([1,3],:) = 0.5; %车道显示为灰色
%% 显示初始交通流图
figure('name','NaSch单车道模型','position',[241 132 560 420],'doublebuffer','on');%窗口名称,位置,双缓存
space(2,:) = -1*space(2,:); %根据imshow函数特性space取反
H=imshow(space,[]);%默认参数Wie[min(a),max(a)],这样因为space取反后必定1最大,所以无车为空,而车辆颜色则随着速度变大而颜色趋近于黑色
title('NaSch单车道模型','color','red');
%% 沿车道以v_max为间隔投放速度为最大速度一半的车辆
for a=1:v_max:space_range
v (a) = fix(v_max/2);
end
%%
v_next = v; %v_next用来存放车辆的变动情况即time+1时间步的车辆速度
% 注意:以下循环中所有的速度变动都用v_next,而现时间步状态判断则用v
%% NaSch四步创建车辆模型
%% 时间模块
for time = 1:1:MaxTime
%% 储存数据
memory = [memory;v]; %每个时间步的v都作memory的一行
%% 遍历车道并对有车辆的元胞进行NaSch四步
for cell_i = 1:1:space_range
if ~isnan( v(cell_i) )
%% 加速
v_next(cell_i) = min(v_next(cell_i)+1,v_max);
%% 获取cell_i前空元胞数以及是否符合满足周期循环
[cycle,empty]=get_empty(cell_i,v,v_max,space_range,v_next);
%% 判断cell_i车辆是否在get_empty函数中已经发生移动
if cycle
%% 减速
v_next(cell_i) = min( v_next(cell_i) , empty );
%% 概率慢化
if (rand(1) <= pro_slowdown)
v_next(cell_i) = max(v_next(cell_i)-1,0);
end
%% 位置更新
cell_next = cell_i + v_next(cell_i);
v_next(cell_next) = v_next(cell_i);
v_next(cell_i) = NaN; %位置更新前元胞变为无车
else
%% 周期边界条件将头车以原速度回到车道十分之一长度最左边第一个为空处
for cell_front = 1:fix(space_range/10)
if isnan( v_next(cell_front) ) %此处车辆变动影响的是time+1时间步而非time时间步
v_next( cell_front ) = v_next( cell_i );
v_next(cell_i)=nan;
end
end
end
end
end
v =v_next; %v_next作为下一时间步的v继续循环
%% 显示交通流图
space(2,:) = v; %space第二行为车
space = -1*v; %因为imshow函数特性,space中数值取反再输出
space(isnan(space)) = 1; %无车处显示为白色
set(H,'CData',space);
%% 输出图片变换间隔时间
pause(time_span);
end
%%
%用于求出cell_i与前方元胞空元胞数以及将下一时间步可能到space_range-1的头车
...移回最左边首个空元胞处且返回cycle,返回值为1则头车移动,为0则未移动
% 输出EmptyFront为cell_i(元胞序号)处元胞前方空元胞数(超过v_max按v_max算)
% v_max 最大车速
% space_range 空间范围(车道长)
function [cycle,EmptyFront] = get_empty(cell_i,v,v_max,space_range,v_next)
EmptyFront = 0;
cycle=1;
if (cell_i+v_max) < (space_range-v_max ) %判断元胞是否到边界(space_range-v_max)
%% 求出cell_i前方空元胞数
for front= ( cell_i + 1 ) : ( cell_i + v_max )
if ~isnan( v( front ) )
EmptyFront = front- (cell_i+1);
break;
end
EmptyFront = v_max;
end
else
%% 判断是否是头车
first = 1;
for front= min( ( cell_i + 1 ),space_range ) : min( ( cell_i + v_max ),space_range )
if ~isnan( v( front ) )
EmptyFront = front- (cell_i+1); %不是头车就输出EmptyFront
first =0;
break;
end
end
if first
%% 判断头车下一时间步若可能移动到space_range-1处
if ( v_next(cell_i)+cell_i ) >= (space_range-1)
cycle = 0;
else
EmptyFront =v_max; %只要头车下一秒不到达space_range前方空元胞数按v_max记
end
end
end
end
最后注意的是,本程序由两个函数组成。