This is an official document translation, only for personal reference, if there is infringement, please contact to delete
CoppeliaSim is a highly customizable simulator, and almost every simulation step is user-defined. This flexibility can be achieved through an integrated script interpreter. The scripting language is Lua.
Lua scripts can be easily C/C++
Code calls can also call C/C + + functions in turn, which makes Lua widely used in applications. Not only as an extension script, but also as a general configuration file, instead of XML,ini and other file formats, and easier to understand and maintain.
Lua is written by standard C. the code is simple and beautiful. It can be compiled and run on almost all operating systems and platforms.
A complete Lua interpreter is no more than 200k. Among all script engines, Lua is the fastest. All of this determines that Lua is the best choice as an embedded script. (from Baidu Encyclopedia)
https://www.runoob.com/lua/lua-tutorial.html (Rookie League Lua tutorial)
CoppeliaSim extends Lua's commands and adds CoppeliaSim specific commands that can be identified by their SIM prefixes (for example, sim.handleCollision).
CoppeliaSim supports two types of embedded scripts:
Simulation scripts: simulation scripts are scripts that are executed only during a simulation, and are used to customize the simulation or simulation model. The main simulation loop is processed by the main script, and the model / robot is controlled by the child script.
Customization scripts: these scripts can also be executed when the simulation is not running, and can be used to customize the simulation scene or the emulator itself.
Simulation scripts are embedded scripts that are executed only when the simulation is running. There are two types of simulation scripts:
Main script: by default, each scenario has a main script that handles all the functions (it is responsible for calling the subscripts (see below)). Without the main script, the simulation will not run. You can customize the main script, but it's best to do all the customization in the subscript.
Child script: each scene object can be associated with a child script that will handle specific parts of the simulation. A special feature of them is that they can also open up a thread to run. The most common use of subscripts is for them to control models (for example, robots).
Because subscripts are attached to scene objects (that is, they are associated scripts), they will also be copied during copy and paste operations, which is an important feature that makes it easy to extend the simulation scene. Association scripts form the basis of coppliasim distributed control architecture.
By default, there is a main script for each scenario in copeliasim. It contains the basic code that allows the simulation to run. Without the main script, the running simulation will not be able to perform any operations.
The main script contains the functions called by the system. If the given function is not defined, the call is ignored. In addition to the initialization function, all other functions are optional. The default main script usually contains four functions:
Initialization function: syscall init this part is only executed once at the beginning of the simulation. This code is responsible for preparing the simulation, etc.
Driver function: syscall? Execution this part will be executed in each simulation cycle. The code is responsible for handling all actuation functions (inverse kinematics, dynamics, etc.) of the simulator in a general way. Three commands are particularly important: sim.launchThreadedChildScripts, sim.resumeThreads, and sim.handleChildScripts. sim.launchThreadedChildScripts / sim.resumeThreads start / resume the subscript thread, while sim.handleChildScripts calls the syscall ﹐ action function of the offline subscript. Without these commands, the subscript will not be able to execute or perform its startup functions, and specific model functions or behaviors will not run as expected.
Sensing function: syscall? Sensing this part will be executed in each simulation cycle. This code is responsible for handling all sensing functions (proximity sensors, collision detection, etc.) of the simulator in a common way. Two commands are particularly important: sim.resumeThreads and sim.handleChildScripts. sim.resumeThreads recovers the online subscript, while sim.handleChildScripts calls the syscall_sending function of the offline subscript. Without these commands, subscripts will not be able to perform their sensing functions, and specific model functions or behaviors will not run as expected.
Reply function: syscall? Cleanup this part will be executed once before the end of the simulation. This code is responsible for restoring the initial configuration of the object, clearing the sensor status, collision status, etc.
function sysCall_init() -- Initialization part: sim.handleSimulationStart() sim.openModule(sim.handle_all) sim.handleGraph(sim.handle_all_except_explicit,0) end function sysCall_actuation() -- Actuation part: sim.resumeThreads(sim.scriptthreadresume_default) sim.resumeThreads(sim.scriptthreadresume_actuation_first) sim.launchThreadedChildScripts() sim.handleChildScripts(sim.syscb_actuation) sim.resumeThreads(sim.scriptthreadresume_actuation_last) sim.handleCustomizationScripts(sim.syscb_actuation) sim.handleAddOnScripts(sim.syscb_actuation) sim.handleSandboxScript(sim.syscb_actuation) sim.handleModule(sim.handle_all,false) sim.resumeThreads(2) sim.handleMechanism(sim.handle_all_except_explicit) sim.handleIkGroup(sim.handle_all_except_explicit) sim.handleDynamics(sim.getSimulationTimeStep()) end function sysCall_sensing() -- Sensing part: sim.handleSensingStart() sim.handleCollision(sim.handle_all_except_explicit) sim.handleDistance(sim.handle_all_except_explicit) sim.handleProximitySensor(sim.handle_all_except_explicit) sim.handleVisionSensor(sim.handle_all_except_explicit) sim.resumeThreads(sim.scriptthreadresume_sensing_first) sim.handleChildScripts(sim.syscb_sensing) sim.resumeThreads(sim.scriptthreadresume_sensing_last) sim.handleCustomizationScripts(sim.syscb_sensing) sim.handleAddOnScripts(sim.syscb_sensing) sim.handleSandboxScript(sim.syscb_sensing) sim.handleModule(sim.handle_all,true) sim.resumeThreads(sim.scriptthreadresume_allnotyetresumed) sim.handleGraph(sim.handle_all_except_explicit,sim.getSimulationTime()+sim.getSimulationTimeStep()) end function sysCall_cleanup() -- Clean-up part: sim.resetCollision(sim.handle_all_except_explicit) sim.resetDistance(sim.handle_all_except_explicit) sim.resetProximitySensor(sim.handle_all_except_explicit) sim.resetVisionSensor(sim.handle_all_except_explicit) sim.closeModule(sim.handle_all) end function sysCall_suspend() sim.handleChildScripts(sim.syscb_suspend) sim.handleCustomizationScripts(sim.syscb_suspend) sim.handleAddOnScripts(sim.syscb_suspend) sim.handleSandboxScript(sim.syscb_suspend) end function sysCall_suspended() sim.handleCustomizationScripts(sim.syscb_suspended) sim.handleAddOnScripts(sim.syscb_suspended) sim.handleSandboxScript(sim.syscb_suspended) end function sysCall_resume() sim.handleChildScripts(sim.syscb_resume) sim.handleCustomizationScripts(sim.syscb_resume) sim.handleAddOnScripts(sim.syscb_resume) sim.handleSandboxScript(sim.syscb_resume) end
Modifying the default master script is not recommended. The reason is as follows: one of the advantages of CoppeliaSim is that it can copy any model (robot, actuator, sensor, etc.) into the scene and put it into use immediately. When you modify the main script, you run the risk that the model can no longer run as expected (for example, if your main script is missing the sim.handleChildScripts command, all models copied to the scene won't run at all). Another reason is that keeping the default main script makes it easy for the old scene to adjust to new features (for example, if the new version of coppliasim introduces the concise command sim.doMagic(), the old scene will be automatically updated so that the command will also be automatically called).
However, if for some reason you do need to modify the scene's main script, you can do this by double clicking the light red script icon next to the world icon at the top of the scene hierarchy.
From the moment the main script is opened, it will be marked as a custom script and will no longer be updated automatically.
Most commands in the main script behave or operate in similar ways. If the distance calculation function is taken as an example, it has:
sim.handleDistance (sim.handle ﹣ all ﹣ except ﹣ explicit): the purpose of this command is to calculate the minimum distance for all distance objects registered and listed in the distance calculation dialog box. All distance objects, except those marked for explicit processing, are processed (that is, calculated) using this command.
Any new distance objects are automatically processed by the above command (as long as they are not marked for explicit processing). The same mechanism is suitable for collision detection, proximity sensor and visual sensor simulation, inverse kinematics and so on. This is a powerful mechanism to run simple simulations without writing any code.
The most important command in the main script is sim.handleChildScripts, which calls sim.handleChildScripts in the driving function and the sensing function. Without this command, no offline subscripts will be executed. If you look at the default main script, you will notice that the driver function allows you to start or modify scene content (for example, sim.handleIkGroup, sim.handleDynamics, etc.), while the sensor function allows you to sense and detect scene content (for example, sim.handleCollision, sim.handleDistance, sim.handleProximitySensor, etc.). The following describes what happens in the default main script when simulating a mobile robot equipped with proximity sensors:
In consideration of the above order, the subscript will always read the state of proximity sensor (through sim.readProximitySensor) from the sensor of the previous cycle (occurred at the end of the simulation of the previous cycle, inside the main script, through sim.handleProximitySensor), and then make a response to the obstacle.
If you need to explicitly process the sensor, make sure to always operate in the sensor function, otherwise you may encounter a display error, as shown in the following figure:
CoppeliaSim supports an unlimited number of subscripts per scene. Each subscript represents a piece of code written in Lua that allows specific functions to be processed in the simulation. Subscripts are attached to (or associated with) scene objects and can be easily identified from script icons in the scene hierarchy:
Double click the script icon to open the script editor. You can change the properties of a script or associate it with another object through the script dialog box. You can attach a new subscript to an object by selecting the object and then [menu bar > Add > associated child Script].
The association of subscripts with scene objects has an important positive impact:
Good portability: subscripts are saved / loaded with their associated objects. With subscripts, you can create portable code and simulation models without relying on any system specific plug-ins. A full-featured model can be included in a file (it can be used on various platforms without modification), which is not the case for model control relying on plug-ins. In addition, for the same reason, models that rely on subscripts do not need long-term maintenance (for example, new OS versions will not require you to adjust some code or recompile).
Inherent replicability: if an object with additional subscripts is copied, its subscripts will also be copied. The content of the copied subscript will be the same as that of the original subscript, however, the copied subscript will know that it has been copied and correctly redirected object access (for example, if the original subscript is accessing the robot, the copied subscript will automatically add a name suffix after the robot to access the duplicate robot instead of the original robot). For more details, see the section on programmatically accessing generic type objects. Automatic name suffix adjustment allows you to copy objects and behaviors without overriding / adjusting any code.
There is no conflict between different model versions: if you modify a subscript of a given model (for example, customize it as needed), there is no impact on other similar models. This is a more critical aspect when relying on plug-ins rather than subscripts for model control: in fact, for plug-ins, you always run the risk of conflicting with previous plug-in versions.
Very easy synchronization with simulation loops: subscripts can run both threaded and non threaded (see below). Even the thread version of the subscript can be easily synchronized with the simulation loop, which is a powerful feature.
There are two different types of subscripts: non threaded child scripts or threaded child scripts:
The non line subscript contains blocking functions. This means that each time they are called, they should perform some tasks and then return control. If control is not returned, the entire simulation stops. In each simulation cycle, the main script calls the non line program subscript function twice in the driving and sensing functions of the main script. The system will also call the subscript when appropriate (for example, when the subscript initializes, cleans up, or triggers a callback function). As far as possible, non thread scripts should be selected instead of thread scripts.
Non linear subscripts follow an exact call or execution order: by default, subscripts start with leaf objects (or no child objects) and end with root objects (or no parent objects). (？？？) The sim.handleChildScripts command in the default main script handles calls to non line program subscripts.
Imagine an example of a simulation model of an automatic door: the front and back proximity sensors can detect people approaching. When people are close enough, the door will open automatically. The following code shows a typical non line program subscript, which illustrates the above example:
-- Variable does not need to be declared automatically, no local Global variable function sysCall_init() --Run once at the start of the simulation to get the object handle sensorHandleFront=sim.getObjectHandle("DoorSensorFront") sensorHandleBack=sim.getObjectHandle("DoorSensorBack") motorHandle=sim.getObjectHandle("DoorMotor") end function sysCall_actuation() --Run every simulation cycle resF=sim.readProximitySensor(sensorHandleFront) resB=sim.readProximitySensor(sensorHandleBack) --Read sensor status if ((resF>0)or(resB>0)) then sim.setJointTargetVelocity(motorHandle,-0.2) else sim.setJointTargetVelocity(motorHandle,0.2) end end function sysCall_sensing() end function sysCall_cleanup() -- Put some restoration code here end function sysCall_dynCallback(inData) -- See the dynamics callback function section in the user manual for details about the input argument end function sysCall_jointCallback(inData) -- See the joint callback function section in the user manual for details about input/output arguments return outData end function sysCall_contactCallback(inData) -- See the contact callback function section in the user manual for details about input/output arguments return outData end function sysCall_beforeCopy(inData) for key,value in pairs(inData.objectHandles) do print("Object with handle "..key.." will be copied") end end function sysCall_afterCopy(inData) for key,value in pairs(inData.objectHandles) do print("Object with handle "..key.." was copied") end end function sysCall_beforeDelete(inData) for key,value in pairs(inData.objectHandles) do print("Object with handle "..key.." will be deleted") end -- inData.allObjects indicates if all objects in the scene will be deleted end function sysCall_afterDelete(inData) for key,value in pairs(inData.objectHandles) do print("Object with handle "..key.." was deleted") end -- inData.allObjects indicates if all objects in the scene were deleted end
The structure of the non line program subscript is similar to that of the main script.
A thread subscript is a script that will be started in a thread. The startup (and recovery) of a thread subscript is handled by the default main script code through the sim.launchThreadedChildScripts and sim.resumeThreads functions. The startup / recovery of the threaded subscript is executed in precise sequence. When the execution of the line subscript is still in progress, it will not be started again. After a threaded subscript ends, it can only be restarted if the execute once item in the script properties is unchecked. The thread subscript icon in the scene hierarchy appears in light blue instead of white, indicating that it will start in the thread.
Compared with non-linear program subscripts, thread subscripts have several disadvantages: they are more resource intensive, they waste some processing time, and they may have a slightly worse response to the simulation stop command.
A typical threaded subscript code is shown below, but it's not perfect because it wastes valuable computing time in the loop (the code deals with automatic sliding doors from the above example):
function sysCall_threadmain() -- Put some initialization code here: sensorHandleFront=sim.getObjectHandle("DoorSensorFront") sensorHandleBack=sim.getObjectHandle("DoorSensorBack") motorHandle=sim.getObjectHandle("DoorMotor") -- Here we execute the regular thread code: while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do resF=sim.readProximitySensor(sensorHandleFront) resB=sim.readProximitySensor(sensorHandleBack) if ((resF>0)or(resB>0)) then sim.setJointTargetVelocity(motorHandle,-0.2) else sim.setJointTargetVelocity(motorHandle,0.2) end -- this loop wastes precious computation time since we should only read new -- values when the simulation time has changed (i.e. in next simulation step). end end function sysCall_cleanup() -- Put some clean-up code here: end
The thread subscript should be two parts:
Body part: this part will be executed when the thread starts, until shortly before the end of the thread. This can be at the beginning of the simulation or in the middle of the simulation: remember that the objects associated with the subscript can be copied / pasted into the scene at any time, even when the simulation is running. Usually you will put some initialization code and main loop in this part: the code in the loop is responsible for dealing with this difficult specific part (for example, dealing with automatic sliding door). In the specific example above, the loop wastes valuable computing time and runs asynchronously with the main simulation loop.
Recovery part: this part will be executed once before the end of simulation or before the end of thread.
CoppeliaSim uses threads to imitate the behavior of the coroutine, rather than the traditional coroutine, so it has great flexibility and control: by default, the threaded sub script will execute for about 1-2 milliseconds, and then automatically switch to another thread. You can change this default behavior using sim.setThreadSwitchTiming or sim.setThreadAutomaticSwitch. After switching the current thread, it will resume execution in the next simulation cycle (that is, at currentTime + simulationTimeStep time). Thread switching is automatic (occurs after a specified time), but the sim.switchThread command allows you to shorten that time when needed. With the above three commands, excellent synchronization with the main simulation loop can be achieved. The following code (handling the automatic sliding door from the example above) shows the synchronization with the sub script of the main simulation loop:
function sysCall_threadmain() -- Put some initialization code here: sim.setThreadAutomaticSwitch(false) -- disable automatic thread switches sensorHandleFront=sim.getObjectHandle("DoorSensorFront") sensorHandleBack=sim.getObjectHandle("DoorSensorBack") motorHandle=sim.getObjectHandle("DoorMotor") -- Here we execute the regular thread code: while sim.getSimulationState()~=sim.simulation_advancing_abouttostop do resF=sim.readProximitySensor(sensorHandleFront) resB=sim.readProximitySensor(sensorHandleBack) if ((resF>0)or(resB>0)) then sim.setJointTargetVelocity(motorHandle,-0.2) else sim.setJointTargetVelocity(motorHandle,0.2) end sim.switchThread() -- Explicitely switch to another thread now! -- from now on, above loop is executed once in each simulation step. -- this way you do not waste precious computation time and run synchronously. end end function sysCall_cleanup() -- Put some clean-up code here end
Now for each main simulation loop, the while loop will be executed only once, without wasting time reading the sensor state again and again. By default, the thread always resumes when the main script calls sim.resumeThreads (sim.scriptthreadresume ﹣ default). If you need to ensure that the thread only runs when the main script is in the sense phase, you can use the API function sim.setThreadResumeLocation to determine the recovery location of the thread again.
It is not possible to distinguish the CoppeliaSim thread's process like behavior from regular threads, except that if external commands (such as socket communication commands provided by Lua Library) are blocked, CoppeliaSim will also be displayed as blocked. In this case, you can define a non blocking section as follows:
sim.setThreadIsFree(true) -- Start of the non-blocking section http = require("socket.http") print(http.request("http://www.google.com")) -- this command may take several seconds to execute sim.setThreadIsFree(false) -- End of the non-blocking section