仿真时间推进

仿真中的时间推进是指在仿真过程中,通过什么方法推进仿真时间。

时间推进法的分类

常见的仿真时间推进方法有三大类:

  • 事件调度法:事件调度法是一种基于事件的仿真方法,它通过对仿真系统中各个事件进行排序,按照事件发生的先后顺序依次推进时间执行活动,进而实现仿真。
  • 固定增量推进法:固定增量推进法是指在仿真过程中,固定一个时间增量。在设定起始时间后,在起始时间的基础上根据时间的增量来对时间进行推进仿真。
  • 活动事件扫描法:针对不确定性较强的系统,通过扫描系统中发生的事件并执行相应的活动实现时间推进。感觉实现起来比较难(容易出现时间推进不准(有误差)的问题,可能是我的水平不够😥)

实例

事件调度法

事件调度法是一种基于事件的仿真方法,它通过对仿真系统中各个事件进行排序,按照事件发生的先后顺序来进行仿真。在仿真过程中,仿真系统根据当前仿真时间和剩余事件,将时间推进到下一个事件,执行相应的活动

事件调度法主要分为以下3个主要步骤,这3个步骤也是控制事件调度法的主控程序的主要步骤:

  • 时间扫描:确定下一事件发生时间并将仿真时钟推进到该时刻
  • 事件辨识:正确地辨识当前要发生的事件
  • 事件执行:正确地执行当前发生的事件

事件调度法基本原理流程图如下:

事件调度法在MicroCityWeb中的具体实现参见 离散事件仿真和程序控制 - 协程

固定增量推进法

固定增量推进法是指在仿真过程中,从起始时间开始,每次推进一个固定步长的时间。在每个步长内,若无事件发生,则仿真钟再推进一个单位时间T;若在该步内有若干个事件发生,则依次执行,且认为这些事件均发生在该步的结束时刻。

固定增量推进法

例题

代码流程示例

while scene.render() do
    t = t + dt
    if t % cycle ~= work_time then
        d = d + v * dt
        print("出发后", t, "小时")
    else
        print("出发后", t, "小时,休息")
    end

    car:setpos(CastToLine(d)) --直线轨迹

    os.sleep(200)
end

此为代码主要流程,无法直接使用

自动化仓库仿真思路的最后一部分提到了改进空间,实现了全局的仿真钟,最终实现了时间推进法。最终还实现了非固定增量推进,详细见下文介绍。

自动化仓库仿真向主导时钟推进法的改变

具体来说,只在原来的基础上做了如下改动:

  • 删除大部分与事件调度法有关的 os.sleep()。由于仿真流程比较简单,保留了与装卸货有关的事件调度法,优化资源占用。
  • 设置全局仿真时钟,将 Agv:Move() 函数的思路从“固定步长执行任务并刷新场景”的改为“监测仿真时钟时长变化,根据时长变化执行任务并刷新场景”
  • 新增仿真速度调整。

从以上的改动可以看出,仿真的核心从事件任务转向了仿真时钟。其中,场景刷新的部分使用了 os.clock() 实现。

非固定增量推进法

下面是一个有关于时间推进法和 os.clock() 函数的简单的示例,修改自MicroCityWeb中内置的方块旋转案例。其中,os.clock() 用于计算CPU运行时间,以此实现仿真时间与真实世界时间成一定比例。而具体的比例可以通过 simspeed (仿真速度)进行调整。

local obj = scene.addobj('box')

-- 初始位置
local x = 1
local y = 1
local z = 0

local rx, ry = 0.1, 0.1 -- x方向和y方向的旋转速度
local simspeed = 10 -- 仿真速度

local t = 0 -- 全局仿真时钟
local t0 = os.clock() -- 记录仿真开始的时间
while scene.render() do
    local dt = os.clock() - t0 -- 计算自上次记录时间以来的时间差
    t = t + dt -- 仿真时钟走过相应时间差长度的时间
    
    -- 设置方块此刻的旋转位置
	x = x + rx * dt * simspeed 
	y = y + ry * dt * simspeed 
	obj:setrot(x, y, z)
	
    t0 = os.clock() -- 记录仿真时间
end

相关说明

  • 当仿真速度 simspeed 为1时,表示仿真时钟与真实世界时钟的速度相同。simspeed 可以视作加速倍率。
  • dt 表示两次记录时间之间的时间差,仿真中的时间步进根据 dt*simspeed 计算得到。

    相关信息

    需要注意的是,时间增量 dt 的数值在每个循环周期一般都不相同,dt 的具体大小一般取决于电脑的运算能力

    提示

    有时两次记录时间之间没有太多耗时的操作,计算得到的 dt 可能为0。一般两次时间记录之间存在一个 scene.render(),这样一般能够保证两次采样的时间之间能够计算得到一个不为0的时间差值。

Last Updated:
Contributors: huuhghhgyg