-
The main and child scripts
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).
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.
Following are the main differences between the main script and a child script:
- There can only be one main script. There can be an unlimited number of child scripts.
- The main script is independent and should preferably not be customized. Child scripts are associated with scene objects and should be customized.
- The main script is never duplicated in a copy/paste operation of scene objects. Child scripts will duplicate themselves together with their associated scene object.
- The main script cannot be threaded. Child scripts can be threaded or non-threaded.
-
Simulation
VREP中的仿真可以通過工具欄上的開始、暫停、停止按鈕進行控制:
[Simulation start/pause/stop toolbar buttons]
在程序內部,仿真過程分為多種中間狀態(Internally, the simulator will use additional intermediate states in order to correctly inform scripts or programs about what will happen next),下圖反映了這些有限狀態間的轉變:
[Simulation state diagram]
函數simGetSimulationState()可以返回當前仿真狀態,返回值有下面幾種:
仿真進程按照固定的時間步長循環執行。The simulator operates by advancing the simulation time at constant time steps. Following figure illustrates the main simulation loop:
[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]
- Time step: the simulation time step. Each time the main script was executed, the simulation time is incremented by the simulation time step. Using large time steps results in fast but inaccurate/unstable simulations. Small time steps on the other hand will (generally) lead to more precise simulations, but will take more time. It is highly recommended to keep a default time step.
- Simulation passes per frame (ppf): the number of simulation passes for one rendering pass. A value of 10 would mean that the main script is executed 10 times (10 simulation steps) before the screen is refreshed. ppf值越大,仿真速度將越快(因為圖形渲染要耗費很多時間)。
由於渲染操作很耗費時間,會導致仿真周期變長,減慢仿真速度。我們可以通過增大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,因此計算速度會大大加快。但這也會帶來許多問題,比如渲染和仿真的不同步可能會導致視覺差錯的產生。
-
The main script
VREP中的每一個場景都默認帶一個主腳本(main script)文件,它包含了基本的控制代碼,用於控制仿真進程(Without main script, a running simulation won't do anything)。
[Main script icon]
main script中的代碼按照功能可分為初始化部分、常規部分和結束清理部分:
- the initialization part: this part will be executed one time just at the beginning of a simulation. The code is in charge of preparing a simulation, etc.
- the regular part: this part will be executed at each simulation pass. The code is in charge of handling all the functionality of the simulator (inverse kinematics, proximity sensors, collision detection, dynamics, etc.) in a generic way. Two commands are of particular interest: simLaunchThreadedChildScripts and simHandleChildScripts. simLaunchThreadedChildScripts launches threaded child scripts, while simHandleChildScripts runs non-threaded child scripts. Without those commands, child scripts won't be executed, and specific model functionality or behavior won't operate. The regular part is divided into an actuation (or action/reaction) section and a sensing (or probing) section.
- the restoration part: this part will be executed one time just before a simulation ends. The code is in charge of restoring object's initial configuration, clearing sensor states, collision states, etc.
下面是一個典型的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
-
Child scripts
子腳本是一種仿真腳本(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部分:
- the initialization part: this part will be executed just one time (the first time the child script is called). This can be at the beginning of a simulation, but also in the middle of a simulation. Usually you would put some initialization code as well as handle retrieval in this part. 這一部分通常會包含一些初始化代碼和句柄獲取代碼。
- the actuation part: this part will be executed in each simulation step, during the actuation phase of a simulation step.
- the sensing part: this part will be executed in each simulation step, during the sensing phase of a simulation step.
- the restoration part: this part will be executed one time just before a simulation ends, or before the script is destroyed.
下面是一個自動旋轉門的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]
- Disabled: indicates whether the script is enabled or disabled.
- Execute just once: this item applies only to threaded child scripts. When this item is unchecked, then a thread that ended will be relaunched by the main script.
- Associated object: object that is currently associated with the script.
- Execution order: specifies the execution order for a scripts. The execution order only has an effect on scripts located on similar hierarchy level. 即處於同一層級的幾個腳本可以在這里設置執行順序。還是用之前的模型進行一下驗證,現在將Object2~Object4移到同一層級下,將Object2的執行順序設為last,Object3的執行順序設為normal,Object4的執行順序設為first:
開始仿真,狀態欄輸出結果如下:
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、1、2、3...等數字,這意味着threaded child script與main script的執行同步了。而如果在程序的while循環中將simSwitchThread()注釋掉,再次進行仿真,則會在瞬間輸出大量數字,即threaded child script比main script跑的要快...
另外需要注意的是必須開啟real-time模式,否則仿真時也會瞬間就輸出大量數字,因為正常模式下仿真會以極快的速度運行,與真實時間並不同步。
參考: