Importing, Editing & Manipulating Configurations

  • This scripts loads a mini scene defined in ‘mini.g’.

  • The ‘watchFile’ command allows you to edit the file in some other editor, while it is being redisplayed whenever you save.

  • ‘set/getJointState’ shows how to access degrees-of-freedom of the scene

  • ‘eval’ shows how to access other features, including its Jacobian

Importing robot models

Let’s first have a look at robot models that are already installed with rai. (They are defined in the rai-robotModels github repo, which is partly copied into the .../site-packages/robotic/rai-robotModels install path.)

[1]:
from robotic import ry
[2]:
print('The path where model files are pre-installed:\n', ry.raiPath(''))
# you could overwrite this with:
# ry.setRaiPath('/home/mtoussai/git/rai-robotModels/')
The path where model files are pre-installed:
 /home/mtoussai/.local/lib/python3.8/site-packages/robotic/rai-robotModels/
[3]:
C = ry.Config()
C.addFile(ry.raiPath('panda/panda.g')) .setPosition([1.,.0,.0]) .setQuaternion([1,0,0,1])
C.addFile(ry.raiPath('panda/panda.g'), 'r_') .setPosition([1.5,.0,.0]) .setQuaternion([1,0,0,1])
C.addFile(ry.raiPath('pr2/pr2.g')) .setPosition([-1.,.0,.0])
C.addFile(ry.raiPath('baxter/baxter.g')) .setPosition([0,.0,1.]) .setQuaternion([1,0,0,1])
C.addFile(ry.raiPath('robotiq/robotiq.g')) .setParent(C.getFrame('panda_joint7')) .setRelativePosition([0., 0., .15])
C.view()
-- WARNING:graph.cpp:read:663(-1) nothing to delete with key 'visual'
-- WARNING:graph.cpp:edit:485(-1) no nodes edited! (from 'joint: { ctrl_H: 1 }')
[3]:
0

The addFile returns the first frame defined in the file, which typically is the base frame of the whole robot. Therefore, by setting it’s position, you can move the whole loaded robot.

Sometimes you want to add a model twice, but avoid duplicated frame names. With addFile you can specify a prefix string (here r_) which add that prefix to all frame names of the added robot. This is exemplified with the second panda added here.

The Robotiq example also shows that this base frame can be made a child of other frames – attaching the robotiq to the panda arm. (Here in addition to the existing gripper, which would first have to be removed using C.delFrame.)

The following is a simple helper to articulare all dofs in the display (press q to interrupt):

[4]:
C.animate()
[5]:
del C

Conversion from URDF

The python package should install a script urdf2rai.py in .local/bin. That converts a urdf-file to a .g-file. For instance, place a ur10.urdf and the ur_description folder into the same folder and run:

urdf2rai.py ur10.urdf > ur10_org.g

That should generate a ur10_org.g file that can be displayed with kinEdit ur10_org.g. Note, by default that excludes exporting collision shapes (as I typically replace them, e.g. by the convex hull of the visual shapes). The -coll 1 option of the script should include also the collision shapes. The rai-robotModels repository README provides more info on conversion from URDF, as well as specific examples for those models included in the package.

Configuration file (.g-file) syntax

The Appendix:Yaml-Graph Files provides a more detailed description of the underlying syntax of the .g-files. While the syntax should be yaml compatible, the fact that node names use (<parents>) to define the parent(s) of a node, filenames should be strings <filename>, and the additional Include, Edit, and Delete tags extend the semantics of what can be define in such a file.

In essence, to describe a robot/world configuration, every node describes a frame, and is described by three things:

   (  ) {  }

where <parent> needs to be a previously defined frame, or omitted, if the frame is a root frame. The attributes define properties of the frame, such as its pose (X), relative transformation (Q), shape, joint, and inertia properties. The main interpreted attributes are the following. Internally, a few more attributes are interpreted in experimental code (also your python code can retrieve attributes via f.info()).

Frame:

X Q

Shape:

shape size contact mesh meshscale core sdf color texture

Joint:

joint joint_scale q Q limits ctrl_limits ctrl_H sampleUniform joint_active mimic

Inertia:

mass inertia

Interactive Editing

When developing your own model (robot or scene), you could of course just use the python commands addFrame etc. But that might be cumbersome and not interactive enough. The .g-file format is fairly easy to edit. To help doing this more interactively, there is the watchFile method:

Open the file you want to edit (here mini.g) in any editor. At the same time, open it from within python and display it using watchFile. The key benefit is that watchFile reloads and re-displays the file whenever it is externally modified (it watches the file’s inode). That enables interactive editing. It also allows you to return information on objects you point on (their name and attributes), which helps a lot to inspect models. It also reports on collisions in the configuration.

A ry-view command line script is shipped with the python package, which simply calls watchFile. Editing from command line with this script makes it easier to see the info and error messages, e.g. when you get the syntax wrong. Jupyter is problematic, as it holds info messages back – still, here an example directly from within python:

[6]:
from robotic import ry
[7]:
C = ry.Config()
C.watchFile('mini.g')
-- kin.cpp:watchFile:3510(0) reloading `mini.g' ...
Configuration: q.N=1 #frames=3 #dofs=1 #shapes=3 #ucertainties=0 #proxies=0 #forces=0 #evals=107
-- kin.cpp:watchFile:3544(0) watching...
The file z.log.global was modified.
EXITING
[8]:
del C

How to attach frames - faking grasps

Note, this is not real grasping. Just editing the kinematic tree in your configuration

[9]:
from robotic import ry
import numpy as np
import time
[10]:
C = ry.Config()
C.addFile(ry.raiPath('scenarios/pandasTable.g'))
C.view()
[10]:
0
[11]:
C.attach("l_gripper", "r_gripper")
[12]:
#move a bit around

q = C.getJointState()

for t in range(30):
    q[0] = np.sin(t/10)

    C.setJointState(q)
    C.view()
    time.sleep(0.1)
[13]:
del C

Advanced: YAML and dict representations

The following shows that configuration files are essentially YAML files with some special semantics. We can load the configuration description as a dict using yaml as follows:

[14]:
import yaml

with open(ry.raiPath('panda/panda_clean.g'), 'r', encoding='utf-8') as fil:
    model = yaml.safe_load(fil)

print(model)
{'panda_link0': {}, 'panda_link0_0(panda_link0)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link0.ply>', 'visual': True}, 'panda_joint1_origin(panda_link0)': {'Q': [0, 0, 0.333, 1, 0, 0, 0]}, 'panda_joint1(panda_joint1_origin)': {'joint': 'hingeZ', 'limits': [-2.8973, 2.8973, 2.175, -1, 87], 'ctrl_limits': [2.175, -1, 87]}, 'panda_link1(panda_joint1)': {}, 'panda_link1_0(panda_link1)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link1.ply>', 'visual': True}, 'panda_joint2_origin(panda_link1)': {'Q': [0, 0, 0, 0.707107, -0.707107, 0, 0]}, 'panda_joint2(panda_joint2_origin)': {'joint': 'hingeZ', 'limits': [-1.7628, 1.7628, 2.175, -1, 87], 'ctrl_limits': [2.175, -1, 87]}, 'panda_link2(panda_joint2)': {}, 'panda_link2_0(panda_link2)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link2.ply>', 'visual': True}, 'panda_joint3_origin(panda_link2)': {'Q': [0, -0.316, 0, 0.707107, 0.707107, 0, 0]}, 'panda_joint3(panda_joint3_origin)': {'joint': 'hingeZ', 'limits': [-2.8973, 2.8973, 2.175, -1, 87], 'ctrl_limits': [2.175, -1, 87]}, 'panda_link3(panda_joint3)': {}, 'panda_link3_0(panda_link3)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link3.ply>', 'visual': True}, 'panda_joint4_origin(panda_link3)': {'Q': [0.0825, 0, 0, 0.707107, 0.707107, 0, 0]}, 'panda_joint4(panda_joint4_origin)': {'joint': 'hingeZ', 'limits': [-3.0718, -0.0698, 2.175, -1, 87], 'ctrl_limits': [2.175, -1, 87]}, 'panda_link4(panda_joint4)': {}, 'panda_link4_0(panda_link4)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link4.ply>', 'visual': True}, 'panda_joint5_origin(panda_link4)': {'Q': [-0.0825, 0.384, 0, 0.707107, -0.707107, 0, 0]}, 'panda_joint5(panda_joint5_origin)': {'joint': 'hingeZ', 'limits': [-2.8973, 2.8973, 2.61, -1, 12], 'ctrl_limits': [2.61, -1, 12]}, 'panda_link5(panda_joint5)': {}, 'panda_link5_0(panda_link5)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link5.ply>', 'visual': True}, 'panda_joint6_origin(panda_link5)': {'Q': [0, 0, 0, 0.707107, 0.707107, 0, 0]}, 'panda_joint6(panda_joint6_origin)': {'joint': 'hingeZ', 'limits': [-0.0175, 3.7525, 2.61, -1, 12], 'ctrl_limits': [2.61, -1, 12]}, 'panda_link6(panda_joint6)': {}, 'panda_link6_0(panda_link6)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link6.ply>', 'visual': True}, 'panda_joint7_origin(panda_link6)': {'Q': [0.088, 0, 0, 0.707107, 0.707107, 0, 0]}, 'panda_joint7(panda_joint7_origin)': {'joint': 'hingeZ', 'limits': [-2.8973, 2.8973, 2.61, -1, 12], 'ctrl_limits': [2.61, -1, 12]}, 'panda_link7(panda_joint7)': {}, 'panda_link7_0(panda_link7)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/link7.ply>', 'visual': True}, 'panda_joint8_origin(panda_link7)': {'Q': [0, 0, 0.107, 1, 0, 0, 0]}, 'panda_joint8(panda_joint8_origin)': {'joint': 'rigid'}, 'panda_link8(panda_joint8)': {}, 'panda_hand_joint_origin(panda_link8)': {'Q': [0, 0, 0, 0.92388, 0, 0, -0.382683]}, 'panda_hand_joint(panda_hand_joint_origin)': {'joint': 'rigid'}, 'panda_hand(panda_hand_joint)': {}, 'panda_hand_0(panda_hand)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/hand.ply>', 'visual': True}, 'panda_finger_joint1_origin(panda_hand)': {'Q': [0, 0, 0.0584, 1, 0, 0, 0]}, 'panda_finger_joint2_origin(panda_hand)': {'Q': [0, 0, 0.0584, 1, 0, 0, 0]}, 'panda_finger_joint1(panda_finger_joint1_origin)': {'joint': 'transY', 'limits': [0, 0.04, 0.2, -1, 20], 'ctrl_limits': [0.2, -1, 20]}, 'panda_finger_joint2(panda_finger_joint2_origin)': {'joint': 'transY', 'joint_scale': -1, 'limits': [0, 0.04, 0.2, -1, 20], 'mimic': 'panda_finger_joint1', 'ctrl_limits': [0.2, -1, 20]}, 'panda_leftfinger(panda_finger_joint1)': {}, 'panda_rightfinger(panda_finger_joint2)': {}, 'panda_leftfinger_0(panda_leftfinger)': {'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/finger.ply>', 'visual': True}, 'panda_rightfinger_0(panda_rightfinger)': {'Q': [0, 0, 0, -1.03412e-13, 0, 0, 1], 'shape': 'mesh', 'color': [0.9, 0.9, 0.9], 'mesh': '<meshes/visual/finger.ply>', 'visual': True}}

This dict contains all information about the configuration.

[15]:
del C
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[15], line 1
----> 1 del C

NameError: name 'C' is not defined
[ ]: