The main script and the child scripts, which are simulation scripts, play the central role in each simulation: while the main script contains the simulation loop code, child scripts contain the typical code to control models (e.g. robots, sensors or actuators).html
By default, each scene has a main script that handles all the functionality. Without main script, a simulation cannot run. The main script can be customized, but it is preferable to do all the customization work in a child script.Each scene object can be associated with a child script that will handle a specific part of a simulation, in an independent and distributed fashion. The most common use for child scripts is to have them control a model.react
Following are the main differences between the main script and a child script:多线程
VREP中的仿真能够经过工具栏上的开始、暂停、中止按钮进行控制:app
[Simulation start/pause/stop toolbar buttons]less
在程序内部,仿真过程分为多种中间状态(Internally, the simulator will use additional intermediate states in order to correctly inform scripts or programs about what will happen next),下图反映了这些有限状态间的转变: ide
[Simulation state diagram]函数
函数simGetSimulationState()能够返回当前仿真状态,返回值有下面几种:工具
仿真进程按照固定的时间步长循环执行。The simulator operates by advancing the simulation time at constant time steps. Following figure illustrates the main simulation loop:oop
[Main simulation loop]测试
须要注意的是VREP中的仿真时间与真实时间并不一致。If the real-time mode is not enabled, then the V-REP simulation time will not move at the same rate as the time on your watch. Simulation will try to run as fast as possible. This could be faster or slower than real-time, depending on the complexity of the simulation. Real-time simulation is supported by trying to keep the simulation time synchronized with the real time:
[Real-time simulation loop]
By default, each simulation cycle is composed by following sequential operations:
- Executing the main script
- Rendering the scene
仿真速度主要取决于仿真时间步长和渲染一步须要的仿真次数(the number of simulation passes for one rendering pass),在仿真过程当中能够经过工具栏上的仿真速度调节按钮控制仿真速度:
[Simulation speed adjustment toolbar buttons]
仿真时间步长和渲染一帧的仿真次数ppf能够在仿真对话框中进行设置,仿真设置按钮位于界面左侧的工具栏上:
[Simulation toolbar button]
选择custom,不然不能进行自定义:
[Simulation settings dialog]
因为渲染操做很耗费时间,会致使仿真周期变长,减慢仿真速度。咱们能够经过增大ppf的值来提升仿真速度,但有时仍不能知足实时性的要求。The rendering operation will always increase the simulation cycle duration, thus also slowing down simulation speed. The number of main script executions per scene rendering can be defined, but this is not enough in some situations, because rendering will still slow down every xth simulation cycle (which can be handicapping with real-time requirements). For those situations, a threaded rendering mode can be activated via the user settings, or via the following toolbar button:
[Threaded rendering toolbar button]
When the threaded rendering mode is activated, a simulation cycle will only consist in execution of the main script, thus simulations will run at maximum speed. Rendering will happen via a different thread, and not slow down the simulation task. 即打开多线程渲染时,图形的渲染工做将会在新的线程中进行,原先的simulation cycle将只执行main script,所以计算速度会大大加快。但这也会带来许多问题,好比渲染和仿真的不一样步可能会致使视觉差错的产生。
VREP中的每个场景都默认带一个主脚本(main script)文件,它包含了基本的控制代码,用于控制仿真进程(Without main script, a running simulation won't do anything)。
[Main script icon]
main script中的代码按照功能可分为初始化部分、常规部分和结束清理部分:
下面是一个典型的main script:
if (sim_call_type==sim_mainscriptcall_initialization) then -- Initialization part: simHandleSimulationStart() simOpenModule(sim_handle_all) simHandleGraph(sim_handle_all_except_explicit,0) end if (sim_call_type==sim_mainscriptcall_regular) then -- Actuation part: simResumeThreads(sim_scriptthreadresume_default) simResumeThreads(sim_scriptthreadresume_actuation_first) simLaunchThreadedChildScripts() simHandleChildScripts(sim_childscriptcall_actuation) simResumeThreads(sim_scriptthreadresume_actuation_last) simHandleCustomizationScripts(sim_customizationscriptcall_simulationactuation) simHandleModule(sim_handle_all,false) simResumeThreads(2) simHandleMechanism(sim_handle_all_except_explicit) simHandleIkGroup(sim_handle_all_except_explicit) simHandleDynamics(simGetSimulationTimeStep()) simHandleMill(sim_handle_all_except_explicit) -- Sensing part: simHandleSensingStart() simHandleCollision(sim_handle_all_except_explicit) simHandleDistance(sim_handle_all_except_explicit) simHandleProximitySensor(sim_handle_all_except_explicit) simHandleVisionSensor(sim_handle_all_except_explicit) simResumeThreads(sim_scriptthreadresume_sensing_first) simHandleChildScripts(sim_childscriptcall_sensing) simResumeThreads(sim_scriptthreadresume_sensing_last) simHandleCustomizationScripts(sim_customizationscriptcall_simulationsensing) simHandleModule(sim_handle_all,true) simResumeThreads(sim_scriptthreadresume_allnotyetresumed) simHandleGraph(sim_handle_all_except_explicit,simGetSimulationTime()+simGetSimulationTimeStep()) end if (sim_call_type==sim_mainscriptcall_cleanup) then -- Clean-up part: simResetMilling(sim_handle_all) simResetMill(sim_handle_all_except_explicit) simResetCollision(sim_handle_all_except_explicit) simResetDistance(sim_handle_all_except_explicit) simResetProximitySensor(sim_handle_all_except_explicit) simResetVisionSensor(sim_handle_all_except_explicit) simCloseModule(sim_handle_all) end
子脚本是一种仿真脚本(Simulation scripts are embedded scripts that are only executed while a simulation is running),VREP支持无限数量的子脚本。每一个子脚本文件包含一小段Lua控制代码并附在场景中的物体上,用于实现特定功能(主要是用于控制场景中的模型):
[Child scripts associated with NAO]
Child scripts能够分为两类: non-threaded child scripts 和 threaded child scripts:
[non-threaded child script icon (left), threaded child script icon (right)]
Non-threaded child scripts are pass-through scripts. This means that every time they are called, they should perform some task and then return control. If control is not returned, then the whole simulation halts. Non-threaded child scripts operate as functions, and are called by the main script twice per simulation step: from the main script's actuation phase, and from the main script's sensing phase. This type of child script should always be chosen over threaded child scripts whenever possible.
若是场景中有多个Non-threaded子脚本,则它们会按照父子关系链的顺序逐级向下执行,即会先从模型的根节点(或没有父节点的物体)开始,逐级向下,直到叶节点(或没有子节点的物体)结束。 Child scripts are executed in a cascaded way: child scripts are executed starting with root objects (or parentless objects), and ending with leaf objects (or childless objects). 能够简单地验证一下,在场景中添加几个物体并建立父子关系链,以下图所示:
在每个物体的子脚本中加入信息输出代码:simAddStatusbarMessage(string),参数分别为'0'、'1'、'2'、'3',则仿真时会在状态栏中看到输出以下:
non-threaded脚本代码按功能可分为如下4部分:
下面是一个自动旋转门的non-threaded控制代码,旋转门的先后装有接近传感器,当检测到有人靠近时将门自动打开:
if (sim_call_type==sim_childscriptcall_initialization) then sensorHandleFront=simGetObjectHandle("DoorSensorFront") sensorHandleBack=simGetObjectHandle("DoorSensorBack") motorHandle=simGetObjectHandle("DoorMotor") end if (sim_call_type==sim_childscriptcall_actuation) then resF=simReadProximitySensor(sensorHandleFront) resB=simReadProximitySensor(sensorHandleBack) if ((resF>0)or(resB>0)) then simSetJointTargetVelocity(motorHandle, 0.2) else simSetJointTargetVelocity(motorHandle, 0) end end if (sim_call_type==sim_childscriptcall_sensing) then end if (sim_call_type==sim_childscriptcall_cleanup) then -- Put some restoration code here end
Threaded子脚本会在一个新的线程中运行,没必要像Non-threaded脚本那样执行后返回。The launch of threaded child scripts is handled by the default main script code, via the simLaunchThreadedChildScripts function, in a cascaded way. When a threaded child script's execution is still underway, it will not be launched a second time. When a threaded child script ended, it can be relaunched only if the execute once item in the script properties is unchecked. 脚本属性对话框能够经过左侧工具栏上的Scripts按钮打开:
[Script toolbar button]
[Script dialog]
开始仿真,状态栏输出结果以下:
Threaded child scripts have several weaknesses compared to non-threaded child scripts if not programmed appropriately: they are more resource-intensive, they can waste some processing time, and they can be a little bit less responsive to a simulation stop command.
V-REP uses threads to mimic the behavior of coroutines, instead of using them traditionally, which allows for a great deal of flexibility and control: by default a threaded child script will execute for about 1-2 milliseconds before automatically switching to another thread. Once the current thread was switched, it will resume execution at next simulation pass. The thread switching is automatic (occurs after the specified time), but the simSwitchThread command allows to shorten that time when needed.
Following code shows child script synchronization with the main simulation loop:
-- Put your main loop here: threadFunction=function() while simGetSimulationState()~=sim_simulation_advancing_abouttostop do resF=simReadProximitySensor(sensorHandleFront) resB=simReadProximitySensor(sensorHandleBack) if ((resF>0)or(resB>0)) then simSetJointTargetVelocity(motorHandle, 0.2) else simSetJointTargetVelocity(motorHandle, 0) end simSwitchThread() -- Switch to another thread now!(resume in next simulation step) -- from now on, above loop is executed once every time the main script is about to execute. -- this way you do not waste precious computation time and run synchronously. end end -- Put some initialization code here: simSetThreadAutomaticSwitch(false) sensorHandleFront=simGetObjectHandle("DoorSensorFront") sensorHandleBack=simGetObjectHandle("DoorSensorBack") motorHandle=simGetObjectHandle("DoorMotor") -- Here we execute the regular thread code: res,err = pcall(threadFunction) -- pcall to trap errors in a lua function call if not res then simAddStatusbarMessage('Lua runtime error: '..err) end -- Put some clean-up code here: -- ADDITIONAL DETAILS: -- ------------------------------------------------------------------------- -- If you wish to synchronize a threaded loop with each simulation pass, -- enable the explicit thread switching with -- -- simSetThreadAutomaticSwitch(false) -- -- then use -- -- simSwitchThread() -- -- When you want to resume execution in next simulation step (i.e. at t=t+dt) -- -- simSwitchThread() can also be used normally, in order to not waste too much -- computation time in a given simulation step -- -------------------------------------------------------------------------
Above while loop will now execute exactly once for each main simulation loop and not waste time reading sensors states again and again for same simulation times. By default, threads always resume when the main script calls simResumeThreads.
假如上面的代码中没有调用simSwitchThread()与main script进行同步,则线程中的while循环会以最大速度一直执行。即若是main script以默认速度50ms执行一次,而threaded child script中的while循环可能不到1ms就执行了一次,这样还没等场景中其它物体发生改变(好比人尚未走近),就已经查询传感器状态和设置关节速度不少次,致使资源浪费。
下面能够进行一个简单的测试:新建一个场景,在某个物体下添加一个threaded child script,并加入下面代码向状态栏输出信息:
-- Put some initialization code here simSetThreadAutomaticSwitch(false) index = 0 -- Put your main loop here: while simGetSimulationState()~=sim_simulation_advancing_abouttostop do str = string.format("%d", index) simAddStatusbarMessage(str) index = index + 1 simSwitchThread() -- resume in next simulation step end -- Put some clean-up code here
打开仿真设置对话框,修改仿真时间步长为1000ms,并在工具栏上点击按钮打开real-time模式:
开始仿真,能够看到状态栏会每隔1s依次输出0、一、二、3...等数字,这意味着threaded child script与main script的执行同步了。而若是在程序的while循环中将simSwitchThread()注释掉,再次进行仿真,则会在瞬间输出大量数字,即threaded child script比main script跑的要快...
另外须要注意的是必须开启real-time模式,不然仿真时也会瞬间就输出大量数字,由于正常模式下仿真会以极快的速度运行,与真实时间并不一样步。
参考: