V-rep學習筆記:main script and child scripts

  • 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).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:多線程

  1. There can only be one main script. There can be an unlimited number of child scripts.
  2. The main script is independent and should preferably not be customized. Child scripts are associated with scene objects and should be customized.
  3. 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.
  4. The main script cannot be threaded. Child scripts can be threaded or non-threaded.
  • Simulation

  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:

  1. Executing the main script
  2. 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 controlIf 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、一、二、3...等數字,這意味着threaded child script與main script的執行同步了。而若是在程序的while循環中將simSwitchThread()註釋掉,再次進行仿真,則會在瞬間輸出大量數字,即threaded child script比main script跑的要快...

  另外須要注意的是必須開啓real-time模式,不然仿真時也會瞬間就輸出大量數字,由於正常模式下仿真會以極快的速度運行,與真實時間並不一樣步。

 

 

參考:

Lua 協同程序(coroutine)

相關文章
相關標籤/搜索