Plugin tutorial
This tutorial describes how to write a plugin for CoppeliaSim. The CoppeliaSim scene file related to this tutorial is located in scenes/tutorials/BubbleRobExt. The plugin project files of this tutorial can be found here.
这个教程介绍了如何为CoppeliaSim写一个插件。和本教程小关的CoppeliaSim场景文件是 scenes/tutorials/BubbleRobExt。插件工程文件可以在这里找到(https://github.com/CoppeliaRobotics/simExtBubbleRob)。
CoppeliaSim automatically loads all plugins that it can find in its folder (i.e. the installation folder, or the same folder as the one that contains coppeliaSim.exe) at program start-up. CoppeliaSim recognizes plugin files with following mask: "simExt*.dll" on Windows, "libsimExt*.dylib" on Mac OS and "libsimExt*.so" on Linux. Additionally a plugin's filename should not contain any underscore (except the one at the beginning obviously). The plugin file of this tutorial is simExtBubbleRob.dll. When testing it, make sure it was properly loaded at CoppeliaSim start-up: switch the console window to visible by unchecking the Hide console window item in the user settings dialog ([Menu bar --> Tools --> Settings]). This option is only available in the Windows version. On Mac, have a look at the system's console, and on Linux try to start CoppeliaSim from within a console. The console window should display something similar to this:
CoppeliaSim在启动时会在它的目录下(也就是安装目录,或者是包含coppeliaSim.exe的目录)寻找并装在所有的插件。在Windows系统下,CoppeliaSim识别形如:“simExt*.dll”的文件为插件,在Mac OS上是"libsimExt*.dylib",Linux上是 "libsimExt*.so"。
另外,插件的文件名不能包含下划线(当然除了开头那个)。
本教程中的插件文件是simExtBubbleRob.dll。测试的时候,确保在CoppeliaSim启动时它被正确加载了:通过取消勾选user settings 对话框([Menu bar --> tools --> Settings])中的Hide console window项,切换console window为可见。这个选项只在windows中可见。在Mac中,查看系统console,在linux中,通过一个console启动CoppeliaSim。Console window 看来像这样:
As you already understood, this plugin was written for BubbleRob from the BubbleRob tutorial. Load the related scene file (scenes/tutorials/BubbleRobExt/BubbleRobExt.ttt). The BubbleRob plugin adds 4 new Lua commands (custom Lua commands should follow the convention: "simXXX.YYY" for the name, e.g. simRob.start):
由于你已经知道,这个插件是写给BubbleRob教程中的BubbleRob的。加载相关场景文件(scenes/tutorials/BubbleRobExt/BubbleRobExt.ttt)。
BubbleRob插件添加了4个新的Lua命令(自定义的Lua命令需要遵循以下命名规范: "simXXX.YYY",例如simRob.start)
simBubble.create
Description 描述 | Creates an instance of a BubbleRob controller in the plugin. 创建一个BubbleRob控制器插件的实例 |
Lua synopsis Lua 概要 | number bubbleRobHandle=simBubble.create(table[2] motorJointHandles,number sensorHandle,table[2] backRelativeVelocities)
|
Lua parameters Lua 参数 | motorJointHandles: a table containing the handles of the left and right motor joints of the BubbleRob you wish to control. motorJointHandles:包含想要控制的BubbleRob的左右电机关节的句柄的表 sensorHandle: the handle of the proximity sensor or the BubbleRob you wish to control sensorHandle: 想要控制的BubbleRob的接近传感器的句柄 backRelativeVelocities: when BubbleRob detects an obstacle, it will move backwards for some time. relativeBackVelocities[1] is the relative velocity of the left wheel when moving back. relativeBackVelocities[2] is the relative velocity of the right wheel when moving back 当BubbleRob检测到一个障碍物,它会向后移动一段时间。relativeBackVelocities[1]是向后移动时左轮的相对速度,relativeBackVelocities[2]是向后移动时右轮的相对速度。 |
Lua return values Lua 返回值 | result: -1 in case of an error, otherwise the handle of the plugin's BubbleRob controller. 返回:如果发生错误返回-1,其他情况下返回插件的BubbleRob控制器的句柄。 |
simBubble.destroy
Description 描述 | Destroys an instance of a BubbleRob controller previously created with simBubble.create. 销毁之前由SIMBubble.create创建的BubbleRob控制器的一个实例。 |
Lua synopsis Lua 概要 | boolean result=simBubble.destroy(number bubbleRobHandle) |
Lua parameters Lua 参数 | bubbleRobHandle: the handle of a BubbleRob instance previously returned from simBubble.create. bubbleRobHandle:之前由simBubble.create返回的BubbleRob实例的句柄 |
Lua return values Lua 返回值 | result: false in case of an error 结果:如果发生错误就返回false |
simBubble.start
Description | Sets a BubbleRob into an automatic movement mode 设置 BubbleRob 进入自动移动模式 |
Lua synopsis | boolean result=simBubble.start(number bubbleRobHandle) |
Lua parameters | bubbleRobHandle: the handle of a BubbleRob instance previously returned from simBubble.create. BubbleRob 实例的句柄 |
Lua return values | result: false in case of an error 结果:如果发生错误就返回false |
simBubble.stop
Description | Stops the automatic movement of a BubbleRob 停止 BubbleRob 的自动运动 |
Lua synopsis | boolean result=simBubble.stop(number bubbleRobHandle) |
Lua parameters | bubbleRobHandle: the handle of a BubbleRob instance previously returned from simBubble.create. BubbleRob 实例的句柄 |
Lua return values | result: false in case of an error 结果:如果发生错误就返回false |
Now open the threaded child script attached to the BubbleRob model in the scene (e.g. double-click the script icon next to object bubbleRob in the scene hierarchy). Inspect the code:
现在打开场景中BubbleRob模型中附加的子脚本(例如:在场景树状结构中双击bubbleRob对象旁边的script图标),你会看到如下代码:
function sysCall_init()
corout=coroutine.create(coroutineMain)
end
function sysCall_actuation()
if coroutine.status(corout)~='dead' then
local ok,errorMsg=coroutine.resume(corout)
if errorMsg then
error(debug.traceback(corout,errorMsg),2)
end
end
end
function coroutineMain()
-- Check if the required plugin is there:(第一部分代码)
moduleName=0
moduleVersion=0
index=0
bubbleRobModuleNotFound=true
while moduleName do
moduleName,moduleVersion=sim.getModuleName(index)
if (moduleName=='BubbleRob') then
bubbleRobModuleNotFound=false
end
index=index+1
end
if bubbleRobModuleNotFound then
local msg='BubbleRob plugin was not found.\nSimulation will not run properly.'
sim.displayDialog('Error',msg,sim.dlgstyle_ok,true)
else
--获取左右电机的关节句柄,形成一张表
local jointHandles={sim.getObjectHandle('leftMotor'),sim.getObjectHandle('rightMotor')}
--获取传感器的句柄
local sensorHandle=sim.getObjectHandle('sensingNose')
--create 控制器插件的实例,robHandle就是控制器插件实例的句柄
local robHandle=simBubble.create(jointHandles,sensorHandle,{0.5,0.25})
--创建失败会返回-1,如果创建成功
if robHandle>=0 then
simBubble.start(robHandle) -- start the robot 让机器人自动运动
local st=sim.getSimulationTime()
sim.wait(20) -- run for 20 seconds
simBubble.stop(robHandle) --20s之后停下来
simBubble.destroy(robHandle) --销毁插件控制器
end
end
end
The first part of the code is in charge of checking whether the plugin required to run this script (i.e. simExtBubbleRob.dll) is available (i.e. was found and successfully loaded).If not, an error message is displayed.Otherwise, joint and sensor handles are retrieved and given to the custom Lua function that creates a controller instance of our BubbleRob in the plugin.If the call was successfull, then we can call simBubble.start. The function instructs the plugin to move the BubbleRob model while avoiding obstacles. Run the simulation: BubbleRob moves for 20 seconds then stops, as expected.
第一部分代码是为了检查运行脚本需要的插件(即:simExtBubbleRob.dll)是不是可以获取(即:找到并成功装载了)。如果不能获取,显示一个错误信息。否则,将检索关节和传感器句柄,并将其提供给自定义Lua函数(simXXX.create),该函数在插件中创建BubbleRob的控制器实例。 如果调用create函数成功,那么我们可以调用simBubble.start。这个函数指示插件控制器移动BubbleRob模型,运动的同时进行避障。运行仿真,BubbleRob会运动20s后停止,如我们所料。
Now leave CoppeliaSim. Temporarily rename the plugin to TEMP_simExtBubbleRob.dll so that CoppeliaSim won't load it anymore, then start CoppeliaSim again. Load the previous scene and run the simulation: an error message now appears, indicating that the required plugin could not be found. Leave CoppeliaSim again, rename back the plugin to simExtBubbleRob.dll and start CoppeliaSim again.
现在leave CoppeliaSim。暂时将插件命名为TEMP_simExtBubbleRob.dll,这样CoppeliaSim就不会再装载它了,然后重启CoppeliaSim。加载之前的场景,运行仿真:可以看见出现了一条错误信息,显示要求的插件没有找到。关闭CoppeliaSim,重新将插件命名为 simExtBubbleRob.dll,再次启动CoppeliaSim。
Let's have a look at how the plugin registers and handles the above 4 custom Lua functions. Open the BubbleRob plugin project, and have a look at file simExtBubbleRob.cpp:Notice the 3 required plugin entry points: simStart, simEnd, and simMessage: simStart is called once when the plugin is loaded (initialization), simEnd is called once when the plugin is unloaded (clean-up), and simMessage is called on a regular basis with several type of messages.
让我们来看看插件是如何注册和处理上述4个自定义Lua函数的。打开 BubbleRob plugin工程,看一下文件simExtBubbleRob.cpp:看到3个必要的插件入口点:simStart,simEnd,以及simMessage。simStart在插件加载时(初始化)被调用一次,simEnd在插件被卸载时(清空)被调用一次,simMessage通过几种类型的消息定期调用。
During the initialization phase, the plugin loads the CoppeliaSim library (in order to have access to all CoppeliaSim's API functions), then registers the 4 custom Lua functions. A custom Lua function is registered by specifying:
在初始化的时候,插件加载 CoppeliaSim 库(为了获取CoppeliaSim的API应用程序接口函数),然后注册了4个自定义Lua函数。一个自定义的Lua函数通过指定以下内容进行注册:
- a function name
- 一个函数名
- a calling tip string
- 调用提示字符串
- a list of expected arguments
- 预期参数的列表
- a callback address
-
回调地址
When a script calls the specified function name, then CoppeliaSim will try to convert the provided arguments to what is expected by the callback, then calls the callback address. The most difficult task inside of a callback function is to correctly read the input arguments, and correctly write the output values. To ease the task, two helper classes are used, that will be in charge of that: CLuaFunctionData and CLuaFunctionDataItem, located in programming/common and programming/include.
当一个脚本调用了一个特定的函数名,CoppeliaSim会尝试将提供的参数转换为callback期望的样子,然后调用callback地址。回调函数中最难的任务的是正确地读取输入参数,然后正确地写入输出值。为了简化任务,使用了两个助手类,分别负责:CLuaFuctionData和CLuaFuctionDataItem,分别位于 programming/common and programming/include.
When writing your own custom Lua functions, try to use the same code layout/skeleton as was done in file simExtBubbleRob.cpp.
当编写你自己的自定义Lua函数时,尝试使用类似于 simExtBubbleRob.cpp相同的代码布局/框架结构。
Control of a BubbleRob instance does not happen in any of the 4 custom Lua function callbacks: the callbacks just initialize/destroy/update data structures. The control happens in simMessage, with message sim_message_eventcallback_modulehandle: that message is called for all plugins when the main script calls sim.handleModule(sim.handle_all,false), which happens once per simulation pass.
在4个自定义Lua函数回调中,对BubbleRob实例的控制都不会发生:回调只是初始化/销毁/更新数据结构。控制在 simMessage中发生,使用message sim_message_eventcallback_modulehandle:当主脚本调用sim.handleModule(sim.handle_all,false)时,会为所有插件调用该消息,每次模拟过程都会发生一次。
In general, callback routines should execute as fast as possible, and control should then be given back to CoppeliaSim, otherwise the whole simulator will halt.
一般来说,回调例程应该尽可能快地执行,然后控制权应该返回给CoppeliaSim,否则整个模拟器就会停止。