Plugin tutorialThis tutorial will try to explain how to write a plugin for V-REP. The V-REP scene file related to this tutorial is located in V-REP's installation folder's "tutorials\BubbleRobExt". The plugin project files of this tutorial are located in V-REP's installation folder's "v_repExtBubbleRob". V-REP 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 "vrep.exe") at program start-up. V-REP recognizes plugin files with following mask: "v_repExt*.dll" on Windows, "libv_repExt*.dylib" on Mac OS and "libv_repExt*.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 "v_repExtBubbleRob.dll". When testing it, make sure it was properly loaded at V-REP 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 V-REP from within a console. The console window should display something similar to this: Additionally, when looking at V-REP's menu bar, an additional entry "Plugins" and item "BubbleRob Properties" should have appeared (as of now, only the Windows BubbleRob plugin exports a dialog. Under Mac or Linux you will not see a plugin entry in the menu bar, which doesn't mean that the plugin wasn't loaded and isn't active!): Click [Menu bar --> Plugins --> BubbleRob Properties] to open the dialog exported by the plugin: Above plugin dialog is composed by a list that lists up all BubbleRob robots in the current scene. Additionaly, when a BubbleRob in the list is selected, its maximum velocity is displayed and can also be changed. As you already understood, this plugin was written for BubbleRob from the BubbleRob tutorial. Load the related scene file ("tutorials\BubbleRob\BubbleRob.ttt"). Select BubbleRob's vision sensor in the scene hierarchy and delete it. Close the floating view that visualizes the minimum distance between BubbleRob and its obstacles (simply click the red button at the upper right hand side of the floating view). Select all cylinders around BubbleRob, and in the shape dialog change their color to blue (click the Adjust outside color button to adjust the color of one cylinder, then click the corresponding Apply to selection button to have all cylinders with the same color). Now we can easily recognize the original BubbleRob scene from this one. Remove also BubbleRob's attached custom user interface: open the custom user interfaces dialog, select "bubbleCtrl" in the list and press the delete key. Close the dialog. Notice how the BubbleRob dialog doesn't list-up our BubbleRob? This is because our plugin cannot recognize BubbleRob yet. One could associate the plugin with BubbleRob's main object name "bubbleRob". But what if someone renames that object to something different at a later stage? Then BubbleRob wouldn't be recognized again. Obviously this is not the best way to achieve what we want. Instead we will tag individual objects of our BubbleRob model, by attaching custom data to them. That data, which will be saved/loaded/copied together with the object, is identified by a header number as illustrated by following figure: Header numbers identify a company or developer: if you, as an individual or a company, decide to add custom data to a scene object or directly to a scene (refer to simAddObjectCustomData, simGetObjectCustomData, simAddSceneCustomData and simGetSceneCustomData), then you should always use the same header number to write/read your custom data: because only you will know the meaning of that data. Your header number should be the serial number of your (first) V-REP copy. Always keep the same, otherwise you risk clashes with data from other developers. This tutorial uses a header number 0: this means that this plugin will read/write custom data stored under the header number 0. Look at the "Access.h" file of the plugin's project. The header number is identified by DEVELOPER_DATA_HEADER. The plugin reads and writes various data items under the same header number. To do this, it uses a data arrangement that allows identifying various data items, as shown in following figure: You are free to pack/code your data items in a different way (format), but there again, once you have your method, stick to it, otherwise you won't be able to read your data stored in the old format. Best is to have the very first byte of your data represent the format version. The "CAccess" class of the plugin project has several helper functions to insert/extract a specific data item from a data block (insertSerializationData and extractSerializationData). If we now return to the "Access.h" file of the project, we can notice 4 data item IDs: We will now tag various objects of our BubbleRob model. We could do this programmatically by using appropriate API function calls (refer to simAddObjectCustomData and simGetObjectCustomData), but since the tags are not complex, we can proceed in another way: select "bubbleRob" in the scene hierarchy, then open the object common properties dialog. Click View / edit custom data to open the object custom data dialog. Under the Attached custom data item you have a summary of what custom data is currently attached to object "bubbleRob". It should be empty. In the command line below, enter following string "0,0,4,3.1415" and click Add. You just added the BUBBLEROB_DATA_MAXVELOCITY tag to the "bubbleRob" object! (the string was formatted as: "DEVELOPER_DATA_HEADER, BUBBLEROB_DATA_MAXVELOCITY, itemLengthInBytes, bubbleRobMaxVelocity". Before the plugin can recognize a change in the scene, you will have to trigger an event. Do this by copy-pasting any object, then deleting it again. The plugin always reacts to object paste events, erase events, create events, etc. Notice how an object has appeared in the BubbleRob properties dialog: indeed, the plugin is now able to recognize BubbleRob's base object. However the plugin still doesn't know what object is BubbleRob's left/right motor, or proximity sensor. Select the "leftMotor" object in the scene hierarchy and in a similar way you did earlier, add custom data with following string: "0,1,4,0". For the "rightMotor", use following string: "0,2,4,0". Finally, select "sensingNose" and add custom data with following string: "0,3,4,0". The BubbleRob plugin is now fully aware of our BubbleRob model. Try copy/pasting BubbleRob: the plugin directly recognizes the new BubbleRob too! Remove the copy again. The BubbleRob plugin adds 2 new Lua commands (custom Lua commands should follow the convention: "simExt*" for the name): simExtBubbleMoveAndAvoid
simExtBubbleStop
Now let's adjust the child script associated with "bubbleRob": open the script dialog and select in the list "Threaded child script (associated with bubbleRob)" and press the delete key. Then click Insert new script, select "child script (threaded)". Under the Associated object item, select "bubbleRob", then close the dialog. In the scene hierarchy, next to the object icon of "bubbleRob", notice how the script icon has turned blue: the blue color indicates that the child script will launch a new thread. Double-click that icon, the script editor opens. Replace the code with following code: simDelegateChildScriptExecution() The first part of the code is in charge of checking whether the plugin required to run this script ("v_repExtBubbleRob.dll") is available (i.e. loaded). If not, an error message is displayed. Otherwise the custom Lua function is called: "simExtBubbleMoveAndAvoid". The function instructs the plugin to move the BubbleRob model while avoiding obstacles, and this for a duration of 20 seconds. The function is blocking (the omitted third argument is false by default), which means that the call doesn't return until the function has finished (i.e. after 20 seconds). Run the simulation: BubbleRob moves for 20 seconds then stops, as expected. Now save the scene, and leave V-REP. Temporarily rename the plugin to "TEMP_v_repExtBubbleRob.dll" so that V-REP won't load it anymore, then start V-REP again. Load the previously saved scene and run the simulation: an error message now appears, indicating that the required plugin could not be found. Leave V-REP again, rename back the plugin to "v_repExtBubbleRob.dll" and start V-REP again. Let's have a look at how the plugin executes the simExtBubbleMoveAndAvoid command. Open the script dialog and double-click "Main script (default)". A warning message appears informing you that the main script should not be customized if possible. Click "yes" anyway. Notice how "Main script (default)" turned into "Main script (customized)". The script editor now displays the main script. Focus on the meaning and location of following commands: The motor target velocities and the proximity sensor data for BubbleRob are set and read by the plugin when simHandleModule is called in the main script. The command simExtBubbleMoveAndAvoid just passes the movement parameters to the plugin. Why is that? Why not handle BubbleRob's movement directly in simExtBubbleMoveAndAvoid? There are two main reasons for that: The custom Lua command simExtBubbleMoveAndAvoid basically calls back the plugin, which in turn registers the parameters of the command. Then, the plugin directly gives the control back to V-REP (even if simExtBubbleMoveAndAvoid operates in a blocking fashion: indeed, V-REP holds back the thread until a specific value was set when the command is blocking). So, in summary: custom Lua commands can execute directly when they are not linked to a time duration. However, when a custom Lua command requires execution over a certain time duration (like our simExtBubbleMoveAndAvoid command), then the command can only be started (e.g. passing parameters, etc.), but execution should be handled by the simHandleModule command. Close the script editor. Hit the undo toolbar button until the "Main script (customized)" turns back into "Main script (default)". Now open the child script associated with "bubbleRob" and replace the line containing the simExtBubbleMoveAndAvoid command with: result,maxVelocity=simExtBubbleMoveAndAvoid(20,{0.5,0.25},true) Start the simulation and notice the different behavior: because of the simExtBubbleMoveAndAvoid command's 3rd argument, the command is not blocking anymore. So BubbleRob moves for 5 seconds, then simExtBubbleStop is executed and BubbleRob stops. Now, in the BubbleRob properties dialog, select "Object: Associated with bubbleRob". The velocity is 120 RPM. Change it to 240. Save the scene and reload it. Notice how the value was also saved together with the BubbleRob model. Restore it to 120. Start the simulation, and while the simulation is running, copy the BubbleRob model one or two times. Create a new scene. Notice how the BubbleRob properties dialog is empty. Paste one BubbleRob into the scene and run the simulation. Switch back to the original scene: everything operates flawlessly over several scenes/simulations. When designing a plugin for V-REP, it is very important to have, for every object/model that should be recognized by the plugin, a corresponding data structure in the plugin (e.g. the "CBubbleRob" class). Switching scene should recreate the content of the CBubbleRobContainer (and in a similar way, erasing/creating/etc. objects should trigger a refresh of the CBubbleRobContainer). Following figure illustrates the concept for the BubbleRob plugin: Finally, if you have to start new threads from within a plugin, make sure you do not access any of V-REP's API functions from those threads. This limitation should be relaxed in future. |