BubbleRob tutorial

This tutorial will try to introduce quite many V-REP functionalities while designing the simple mobile robot BubbleRob. The V-REP scene file related to this tutorial is located in V-REP's installation folder's "tutorials\BubbleRob" folder. Following figure illustrates the simulation scene that we will design:


First of all, freshly start V-REP. The simulator displays a default scene. We will start by designing BubbleRob's eyes first. Instead of importing shapes, we will directly create them as primitives.

Right click in the scene and quickly release the button: a popup menu appears. Select [Popup menu --> Add --> Primitive shape --> Sphere] (alternatively you can also select [Menu bar --> Add --> Primitive shape --> Sphere]), deselect Create pure shape, then enter following values:


Click ok. Move closer to the newly added shape by using the mouse wheel. Clicking with the left button allows you to shift the camera you are currently looking through along the clicked point. Right click the newly added shape and move the mouse while keeping the right mouse button down: this allows you to rotate the camera about the clicked point.

If you deselected the newly added shape, select it again by simply clicking it with the left mouse button. Now enter the triangle edit mode. The shape is now in triangle edit mode:


Switch to page 6 with the appropriate toolbar button:


We now see the scene from above, and in orthographic projection mode. Move closer to the shape. Keeping the shift button down, select several triangles at a time until you selected all triangles except for a ring as displayed in following sequence:


Remember that a shift selection will select all triangles within the selection square, also those that are located on the other side and not directly visible. Try double-clicking the floating view to swap its content with the main view. Make sure that only desired triangles are selected. Now, with the triangles selected, click Extract shape in the triangle edit mode dialog. This just generated another shape with the selected triangles. Press the delete key to remove the selected triangles. The added shape now becomes visible. Leave the edit mode by closing the shape edit mode dialog. Switch back to page 1.

Select the ring shape. All objects in the scene are also visible in the scene hierarchy. The ring shape appears there too and is highlighted (since selected). Double click its icon to open the shape dialog. In the dialog that just opened, click Adjust outside color. Another dialog will open and then select Adjust ambient component. This allows you to adjust the ambient color component of the ring shape. Once done, select the inner shape (the one you extracted in the edit mode) and adjust its color in the same way as you did with the ring shape. This is what you should have by now:


Next we will add another spheroid with following dimensions: (0.02,0.02,0.015): add a primitive sphere and in the dialog, enter 0.02 for the X and Y-sizes, and 0.015 for the Z-size. Here also make sure you deselect the Create pure shape option beforehand. Adjust the color of the newly added shape by selecting it, then selecting the ring shape (hold the ctrl-key down to select more than one object), and in the shape dialog click the Apply to selection button in the colors section.

Now select all 3 shapes in the scene and click [menu bar --> Edit --> Grouping/Merging --> Group selected shapes]. You can notice that all 3 shapes are now grouped and appear as a single object in the scene hierarchy (the icon looks like it is composed by several items). The simulator also sees and handles the 3 shapes as a single object from now on (as a grouped shape).

What we now have is an eye:


Next we will add the body of BubbleRob. Add two primitive spheres of diameter 0.2 (in the dialog, enter 0.2 for the X, Y and Z-sizes). When adding the second sphere, make sure the Create pure shape option stays selected. You will notice in the scene hierarchy that the pure shape is represented with a different icon (a cube). Pure shapes perform much better during dynamics calculations (i.e. faster and more stable), they however cannot be very complex or look very detailed. For that reason, it is often preferable to have the underlying dynamic content of a scene invisible. This is what we will do next: send the pure shape into the visibility layer 9 (disabled by default). For that, select the pure sphere you just added (select it in the scene hierarchy), then open the object common properties dialog.

While the pure shape is selected, activate the 9th layer and deactivate the first layer in the Visibility layers section. The pure shape is now not visible anymore, since V-REP displays by default the first 8 layers and hides the last 8. Scene layers can be turned on/off in the layers dialog [Menu bar --> Tools --> Layers] or by clicking the appropriate toolbar button:


In the object common properties dialog notice how the pure shape has all object special properties disabled by default. Now select the non-pure sphere and notice in the same dialog how most object special properties are enabled by default. In the shape dynamics dialog (go to the shape dialog, then click Show dynamic properties dialog), notice also how the pure sphere has the Static option unchecked and the Respondable option checked. The non-pure shape on the other hand has the Static option checked and the Respondable option unchecked by default.

Now open the position and translation dialog, select the two spheres representing BubbleRob's body, and in the dialog's section 3, enter -0.1 for Along Z. Make sure the Relative to-item is set to World. Then click Translate selection. This translates all selected objects by -0.1 along the absolute Z-axis. Now you can also change the color of the visible sphere (the non-pure sphere).

Deselect all the objects (either by pressing the esc-key, or by clicking no specific object in the scene). Select the grouped shape in the scene hierarchy (the eye actually), then in the same dialog but in section 2 this time, enter 0.1 for Z-coordinate (make sure the Relative to item is set to World). This will change the absolute Z-coordinate of all selected objects to 0.1. The eye of BubbleRob appears at the top of the sphere. In the orientation and rotation dialog, in section 3, enter 60 for Around Y, then click Rotate selection.

Now copy the eye with [Popup menu --> Edit --> Copy selected objects]. You can also copy it with the ctrl-c key combination. Paste the copy with [Popup menu --> Edit --> Paste buffer] (for that operation you could also use the ctrl-v key combination). Select one of the two superposed eyes (it doesn't matter which one you pick) then in the orientation and rotation dialog, in section 3, enter 30 for around Z, clear the previously entered 60 for around Y (enter zero), then click Rotate selection. Select the other visible eye, enter -30 for around Z, then click Rotate selection. The eyes are in place now. This is what you should have:


Just as a side-note: in above figures you can notice that the underlying dynamic model does not take into account the eyes.

Next we will add a proximity sensor so that BubbleRob knows when it is approaching obstacles: click [Menu bar --> Add --> Proximity sensor --> Cone type]. In the orientation and rotation dialog, in section 3, enter 90 for Around Y and for Around Z, then click Rotate selection. In the position and translation dialog, in section 2, enter 0.1 for X-coordinate. The proximity sensor is now correctly positioned relative to BubbleRob's body. Double-click the proximity sensor's icon in the scene hierarchy to open its properties dialog. Click Show volume parameter dialog to open the proximity sensor volume dialog. Set Angle to 30 and Range to 0.15. Then, in the proximity sensor dialog, click Adjust detection parameters. This opens the proximity sensor detection parameter dialog. Uncheck Don't allow detections if distance smaller than, and check the Limited angle detection item. By doing so you inform the simulator that the sensor will only detect surfaces whose angle between their normal vector and detection ray is smaller than the indicated value (typically, ultrasonic sensors will behave like this). Uncheck Occlusion check. Close that dialog. In the scene hierarchy, double-click the proximity sensor's name, so that you can edit its name. Enter "sensingNose" and press enter.

Rename the invisible body sphere to "bubbleRob" (invisible objects' names appear in grey instead of black in the scene hierarchy). Group the eyes with the visible body sphere, and rename the new grouped shape to "body". Select [Menu bar --> Edit --> Bounding box alignment --> Align selected shape's coordinate frame with world]. This can only be done with non-pure shapes and will align the bounding box for the shape with the world (by default, the bounding boxes are always automatically chosen so that their volume are smallest).

Select "body" and open the shape dynamics dialog. Notice how the Static item is checked and the Respondable item is unchecked. This indicates that the dynamics engine will ignore this shape; indeed, we only want the dynamics engine to simulate the shapes that we put to layer 9, not the other ones. Open the object common properties dialog, and while "body" is still selected notice how the Collidable, Measurable, Renderable and Detectable items are checked. This indicates that "body" is enabled for collision detection, distance calculation, and is visible to vision sensors as well as proximity sensors.

Select "body", "sensingNose" then "bubbleRob" and click [Menu bar --> Edit --> Make last selected object parent]. This attaches "body" and "sensing nose" to "bubbleRob" (have a look at the scene hierarchy). The same operation can directly be performed in the scene hierarchy by dragging "body" onto "bubbleRobb", then dragging "sensingNose" onto "bubbleRob". Select "bubbleRob" and translate it by 0.12 along the world's Z axis (use the Coordinates and transformations dialog). This is what you should have by now:


Next we will take care of BubbleRob's wheels. Create a new scene with [Menu bar --> File --> New scene].

We will use the second scene in order not to be disturbed by the already existing objects in the first scene. Individual scenes in V-REP are fully independent, however they share a same copy buffer. Add a pure primitive cylinder with dimensions (0.08,0.08,0.02). With the cylinder selected, activate its 9th layer (but this time keep the first layer also activated: the cylinder will now be visible in layer 1 and layer 9). In the shape dialog, adjust the wheels's color. In the object common properties dialog, check the Collidable, Measurable, Renderable and Detectable items. Set the cylinder's absolute position to (0.05,0.1,0.04) and its absolute orientation to (-90,0,0) (you can do this in the Coordinates and transformations dialog). Change the name to "leftRespondableWheel". Copy and paste the wheel, and set the absolute Y coordinate of the copy to -0.1. Rename the copy to "rightRespondableWheel". Select the two wheels, copy them, then switch back to scene 1. Paste the wheels.

We now need to add joints (or motors) for the wheels. Click [Menu bar --> Add --> Joint --> Revolute] to add a revolute joint to the scene. Most of the time, when adding a new object to the scene, the object will appear at the origin of the world. Keep the joint selected and also select "leftRespondableWheel". In the position and translation dialog, in section 2, click the Apply to selection button at the bottom of that section, then in the orientation and rotation dialog, in section 2 also, do the same. This sets the joint to the exact same position and orientation as "leftRespondableWheel". Rename the joint to "leftMotor". Double-click the joint's icon in the scene hierarchy to open the joint dialog. Then click Show dynamic parameters to open the joint dynamics dialog. Click Motor enabled. Copy and paste the joint and rename the copy to "rightMotor". Set the right motor to the same position as "rightRespondableWheel" (in the same way as you did with the left motor).

Select "leftRespondableWheel" then "leftMotor" and click [Menu bar --> Edit --> Make last selected object parent]. Do the same with "rightrespondableWheel" and "rightMotor". Now make "bubbleRob" parent of the left and right motors.

Next we will add a small slider (or caster) for the third contact point to the ground. Create a new scene (scene 3) and add a pure primitive sphere with diameter 0.05. Keep the layer 1 activated for the sphere, but in addition activate also layer 9. Make the shape collidable, measurable, renderable and detectable as you did for the two wheels earlier. Adjust its color, and set the Friction parameters to 0 (you can do this in the shape dynamics dialog, by clicking Adjust engine specific properties. Remember that there are distinct properties for the Bullet or the ODE physics engines). Rename the shape to "respondableCaster". Add a force sensor object with [Menu bar --> Add --> Force sensor]. Rename it to "bodyCasterLink" and shift it up by 0.05. Make "bodyCasterLink" parent of "respondableCaster". Copy both objects, switch back to scene 1 and paste the objects. Select "bodyCasterLink" and set its absolute x coordinate to -0.07. Make "bubbleRob" parent of "bodyCasterLink". Deactivate all layers except layer 12 for "bodyCasterLink". If you now carefully observe "respondableCaster" and "bubbleRob" you will notice that they slightly interfere. To avoid strange effects during dynamics simulation, we have to inform V-REP that both objects do not mutually collide, and we do this in following way: in the shape dynamics dialog, for "respondableCaster" set the local respondable mask to 00001111, and for "bubbleRob" set the local respondable mask to 11110000. This is what you should have now:


Run the simulation with [Menu bar --> Simulation --> Start simulation]. Notice how BubbleRob slightly moves. Stop the simulation with [Menu bar --> Simulation --> Stop simulation]. Alternatively you can also start/pause/stop simulations with the corresponding toolbar buttons:


Stability of dynamic simulations is tightly linked to masses and inertias of the involved non-static shapes (e.g. masses should not differ too much from each other, etc.). Select "leftRespondableWheel", "rightRespondableWheel" and "respondableCaster", and in the shape dynamics dialog click three times M=M*2 (for all selected shapes). The effect is that all selected shapes will have their masses multiplied by 8. Run the simulation and notice how stability has improved. Stop the simulation. In the joint dynamics dialog, set Target velocity to 50 for both motors. Run the simulation. BubbleRob now moves forward and eventually falls off the floor. Stop the simulation and reset the Target velocity item to zero for both motors.

The object "bubbleRob" is at the base of all objects that will later form the BubbleRob model. We will define the model a little bit later. In the mean time, we want to define a collection of objects that represent BubbleRob. For that we define a collection object. Click [Menu bar --> Tools --> Collections] to open the collection dialog. Alternatively you can also open the dialog by clicking the appropriate toolbar button:


In the collection dialog, click Add new collection. A new collection object appears in the list just below. For now the newly added collection is still empty (not defined). While the new collection item is selected in the list, select "bubbleRob" in the scene hierarchy, and then click Add in the collection dialog. Our collection is now defined as containing all objects of the hierarchy tree starting at the "bubbleRob" object (the collection's composition is displayed in the Composing elements and attributes section). To edit the collection name, double-click it. Rename the collection to "bubbleRobCollection". Close the collection dialog.

At this stage we want to be able to track the minimum distance between BubbleRob and any other object. For that, open the distance dialog with [Menu bar --> Tools --> Calculation module properties]. Alternatively you can also open the calculation module properties dialog with the appropriate toolbar button:


In the distance dialog, click Add new distance object and select a distance pair: "[collection] bubbleRobCollection" - "all other measurable objects in the scene". This just added a distance object that will measure the smallest distance between collection "bubbleRobCollection" (i.e. any measurable object in that collection) and any other measurable object in the scene. Rename the distance object to "bubbleRobDistance" (double-click the distance object). Close the distance dialog. When you now run the simulation, you won't see any difference, since the distance object will try to measure (and display) the smallest distance segment between "BubbleRob" and any other measurable object in the scene. The problem is that at this stage there is no other measurable object in the scene (the shape defining the floor has its measurable property turned off by default). At a later stage in this tutorial, we will add obstacles to our scene.

Next we are going to add a graph object to BubbleRob in order to display above smallest distance, but also BubbleRob's trajectory over time. Click [Menu bar --> Add --> Graph] and rename it to "bubbleRobGraph". Make the graph child of "bubbleRob", and set the graph's absolute coordinates to (0,0,0.005). Now open the graph dialog by double-clicking its icon in the scene hierarchy. Uncheck Display XYZ-planes. Click Add new data stream to record and select "Object: absolute x-position" for the Data stream type, and "bubbleRobGraph" for the Object / item to record. An item has appeared in the Data stream recording list. That item is a data stream of "bubbleRobGraph"'s absolute x-coordinate (i.e. the "bubbleRobGraph"'s object absolute x position will be recorded). Now we also want to record the y and z positions: add those data streams in a similar way as above. We now have 3 data streams that represent BubbleRob's x-, y- and z-trajectories. We are going to add one more data stream so that we are able to track the minimum distance between our robot and its environment. Click Add new data stream to record and select "Distance: segment length" for the Data stream type, and "bubbleRobDistance" for the Object / item to record. In the Data stream recording list, rename "Data" to "bubbleRob_x_pos", "Data0" to "bubbleRob_y_pos", "Data1" to "bubbleRob_z_pos", "Data2" to "bubbleRob_obstacle_dist".

Select "bubbleRob_x_pos" in the Data Stream recording list and in the Time graph properties section, uncheck Visible. Do the same for "bubbleRob_y_pos" and "bubbleRob_z_pos". By doing so, only the "bubbleRob_obstacle_dist" data stream will be visible in a time graph. Following is what you should have:


Next we will set-up a 3D curve that displays BubbleRob's trajectory: click Edit 3D curves to open the XY graph and 3D curve dialog. Click Add new curve. In the dialog that pops open, select "bubbleRob_x_pos" for the X-value item, "bubbleRob_y_pos" for the Y-value item and "bubbleRob_z_pos" for the Z-value item. Rename the newly added curve from "Curve" to "bubbleRobPath". Finally, check the Relative to world item and set Curve width to 4:


Close all dialogs related to graphs. Now set one motor target velocity to 50, run the simulation, and you will see BubbleRob's trajectory displayed in the scene. Stop the simulation and reset the motor target velocity to zero.

Next, let's add a visualization window for the minimum distance data stream. In the scene, click [Popup menu --> Add --> Floating view]. Select "bubbleRobGraph" then in the floating view right-click [Popup menu --> View --> Associate view with selected graph]. Running the simulation will not yet display anything in the graph window, since there are still no objects (i.e. obstacles) to measure against. Let's now add some obstacles!

Add a pure primitive cylinder with following dimensions: (0.1, 0.1, 0.2). For the cylinder also activate layer 9 (but keep layer 1 activated). We want this cylinder to be static (i.e. not influenced by gravity or collisions) but still exerting some collision responses on non-static respondable shapes. For this, in the shape dynamics dialog, keep Respondable checked but also check Static. In addition we want the cylinder to be detectable by proximity sensors, measurable, collidable and renderable too: check the appropriate items in the object common properties dialog. Now while the cylinder is still selected, click the object translation toolbar button (alternatively, a click on the mouse wheel alternates between the camera pan, the object translate and the object rotate mode):


Now click and drag any point in the scene: the cylinder will follow the movement while always being constrained to keep the same Z-coordinate. Then copy the cylinder and paste it a few times into the scene and move them to positions around BubbleRob (it is most convenient to perform that while looking at the scene as displayed on page 6: from the top). During object shifting, holding down the shift key allows to perform smaller shift steps. Holding down the ctrl key allows to move in an orthogonal direction to the "regular" direction(s). When done, select the camera pan toolbar button:


Set a target velocity of 50 to the left motor and run the simulation: the graph view now displays the distance to the closest obstacle and the distance segment is visible in the scene too. Stop the simulation and reset the target velocity to zero.

Let us now finish BubbleRob as a model definition. For the left and right joints, deactivate layer 2 and activate layer 10. Joints are now hidden in layer 10. Instead of making an object invisible, try simply moving it to a layer 8 levels above, so that at any time the whole scene content can easily be inspected by activating/deactivation scene layers. Select "bubbleRob", and then check the Object is model base item in the object common properties dialog. An object with that item enabled will have a stippled bounding box that encompasses all objects built on its hierarchy. Additionally, when such an object is copied, all its tree hierarchy is also automatically copied. This makes handling of BubbleRob very easy and similar to handling a single object (try to copy only the "bubbleRob" object, and to paste it into another scene: all attached objects are also pasted!).

Next, for each object in the "bubbleRob" tree, except for "bubbleRob", check the Select base of model instead item in the object common properties dialog. Now try selecting any BubbleRob object in the scene: whatever you click on BubbleRob, only its base gets selected! BubbleRob now appears to us as a single object (individual objects however still can be selected through the scene hierarchy, or by holding the shift and ctrl keys down while clicking the object). Finally, for both joints, check the Don't show as inside model selection checkbox. This is what you should have by now:


Next we will add a vision sensor, at the same position and orientation as BubbleRob's proximity sensor: click [Menu bar --> Add --> Vision sensor --> Perspective type]. Make the vision sensor child of "sensingNose", and set the local position and orientation of the vision sensor to (0,0,0). Select the vision sensor and open its properties dialog. Set the Far clipping plane item to 1, and the Resolution x and Resolution y items to 256 and 256. Open the vision sensor filter dialog by clicking Show filter dialog. Select the filter component "Edge detection on work image" and click Add filter. Position the newly added filter in second position (one position up, using the up button). Double-click the newly added filter component and adjust its Threshold item to 0.2, then click OK. Open the object common properties dialog and check the Select base of model instead and Don't show as inside model selection items. At this stage you can also check the Don't show as inside model selection item for the two joints so as to have the model bounding box only encompass visible objects. Move the vision sensor to layer 12. Add a floating view to the scene, and over the newly added floating view, right-click [Popup menu --> View --> Associate view with selected vision sensor]. To be able to see the vision sensor's image, start the simulation. Stop it again.

Next, we want to be able to modulate BubbleRob's velocity with a customized user interface. For that we will use a custom user interface: click [Menu bar --> Tools --> Custom user interfaces] to open the custom user interface dialog. Alternatively you can also access it by clicking the appropriate toolbar button:


Click Add new user interface. Enter 2 for the Client y-size item, keep the rest unchanged and then click OK. A new custom user interface was added to the scene (in the middle of the page). Switch to page 8, and click [Popup menu --> Remove page] in order to see the custom user interface more clearly. In the custom user interface dialog rename it to "bubbleCtrl":


In the page view, shift-select all free cells of the custom user interface, and click Insert merged button in the custom user interface dialog (the custom user interface dialog is context sensitive). The selected cells were replaced by a single button. Notice the Button handle item when the new button is selected: 3. This number can be used at a later stage to programmatically access that button. Select "Slider" as type. The button changed to a slider. Deselect all cells, then select "bubbleRob" under UI is associated with select "bubbleRob". This is important so that the custom user interface is also automatically copied if "bubbleRob" is copied, or also automatically saved if "bubbleRob" is saved as a model. Finally, check the UI is visible only during simulation item. Leave the custom user interface edit mode by closing its dialog. Switch back to page 1.

The last thing what we need for our scene is a small child script that will control BubbleRob's behavior. Select "bubbleRob" and click [Menu bar --> Add --> Associated child script --> Non threaded]. This just added a non-threaded child script to the scene, and associated it with "bubbleRob". You can also add, remove or modify child scripts (or the main script) through the script dialog which can be opened with [Menu bar --> Tools --> Scripts] or through the appropriate toolbar button:


Double-click the little script icon that appeared next to "bubbleRob"'s dummy icon in the scene hierarchy: this opens the child script that we just added. Copy and paste following code into the script editor, then close it:

if (simGetScriptExecutionCount()==0) then
    -- This is executed exactly once, the first time this script is executed
    bubbleRobBase=simGetObjectAssociatedWithScript(sim_handle_self) -- this is bubbleRob's handle
    -- following is the handle of bubbleRob's associated UI (user interface):
    ctrl=simGetUIHandle("bubbleCtrl")
    -- Set the title of the user interface: 
    simSetUIButtonLabel(ctrl,0,simGetObjectName(bubbleRobBase).." speed") 
    leftMotor=simGetObjectHandle("leftMotor") -- Handle of the left motor
    rightMotor=simGetObjectHandle("rightMotor") -- Handle of the right motor
    noseSensor=simGetObjectHandle("sensingNose") -- Handle of the proximity sensor
    minMaxSpeed={50*math.pi/180,300*math.pi/180} -- Min and max speeds for each motor
    backUntilTime=-1 -- Tells whether bubbleRob is in forward or backward mode
end

-- if any child script is attached to the bubbleRob tree, this command will execute it/them:
simHandleChildScript(sim_handle_all_except_explicit)
-- Retrieve the desired speed from the user interface: 
speed=minMaxSpeed[1]+(minMaxSpeed[2]-minMaxSpeed[1])*simGetUISlider(ctrl,3)/1000 

result=simReadProximitySensor(noseSensor) -- Read the proximity sensor
-- If we detected something, we set the backward mode:
if (result>0) then backUntilTime=simGetSimulationTime()+4 end 

if (backUntilTime<simGetSimulationTime()) then
    -- When in forward mode, we simply move forward at the desired speed
    simSetJointTargetVelocity(leftMotor,speed)
    simSetJointTargetVelocity(rightMotor,speed)
else
    -- When in backward mode, we simply backup in a curve at reduced speed
    simSetJointTargetVelocity(leftMotor,-speed/2)
    simSetJointTargetVelocity(rightMotor,-speed/8)
end

Run the simulation. BubbleRob now moves forward while trying to avoid obstacles (in a very basic fashion). While the simulation is still running, change BubbleRob's velocity, and copy/paste it a few times. Also try to scale a few of them while the simulation is still running. Be aware that the minimum distance calculation functionality might be heavily slowing down the simulation. You can turn that functionality on and off in the distance dialog, by checking / unchecking the Enable all distance calculations item.

Using a script to control a robot or model is only one way of doing. V-REP offers many different ways (also combined), have a look at the external controller tutorial, or the plugin tutorial.