.g-files to describe robot configurations
We use .g-files to represent robot/world configurations. .g-files describe a general graph data structure as explained in Graph and .g-files. But for robot configurations it is rather simple: Everly node describes a frame, and is described by three things:
<frame-name> ( <parent> ) { <attributes> }
where <parent>
needs to be a previously defined frame, or omitted, if
the frame is a root frame. The attributes defined properties of the
frame, such as its pose, shape, joint properties, etc.
Here is an example taken from the test/Kin/kin
:
stem { X:<t(0 0 .5)>, shape:capsule, mass:1, size:[1 .05] }
joint1_pre (stem) { Q:<t(0 0 .5) d(90 1 0 0)> }
joint1 (arm1) { joint:hingeX, Q:<d(-30 1 0 0)> }
arm1 (joint1) { Q:<t(0 0 .15)>, shape:capsule, mass:1, size:[.3 .05] }
arm2 { shape:capsule, mass:1, size:[.3 .05] }
eff { shape:capsule, mass:1, size:[.3 .05] }
joint2 (arm1 arm2) { joint:hingeX, A:<t(0 0 .15) d(0 0 0 1)>, Q:<d(-10 1 0 0)>, B:<t(0 0 .15) > }
joint3 (arm2 eff ) { joint:hingeX, A:<t(0 0 .15) d(0 0 0 1)>, Q:<d(-10 1 0 0)>, B:<t(0 0 .15) > }
target { X:<t(.0 .2 1.7)>, shape:sphere, mass:.001, size:[0 0 0 .02], color:[0 0 0] }
The first line defines a frame stem
, which has absolute pose
<t(0 0 .5)>
(pose specifications are described below). It also has
a shape attached, namely a capsule of length 1 and radius .05. And it
has inertial mass attached, namely with mass 1.
The 2nd to 4th line form a block of 3 new frames: the joint1_pre
frame is child of step, which fixed relative transformation <t(0 0
.5) d(90 1 0 0)>
(.5 meter up, 90 degress rotation about x). The
joint1
frame is a child of joint1_pre
. But this frame is
special! It is a joint, which means that its relative transformation
to the parent is not fixed, but varies with joint dofs. Here, it is 1
joint dofs describing a hinge joint about the parent’s x-axis. This
joint is here initialized to non-zero, namely to a relative transform
<d(-30 1 0 0)>
. The arm1
frame is then a child of joint1
,
with a relative transform <t(0 0 .15)>
, a capsule shape attached,
and mass.
This is a typical example for a chain of frames from one link, via a joint, to the next. All robot configurations are just trees; and the configuration file simply defines frames one-by-one, where each frame may have 1 parent frame.
The next two lines define two more frames arm2
and eff
mass
and capsule shapes; but they’re yet all located at zero absolute
pose. The following two lines are actually a short hand notation to
introduce a joint frames between arm1 and arm2 (arm2 and arm3) in
retrospect. The joint2
declaration implicitly first defines a
joint2_pre
child of arm1 with fixed relative transformation A;
then the joint2
chile of joint2_pre with hinge joint and initial
transformation Q; then attaches the arm2 frame as its child with fixed
relative transformation B. So this is a typical short hand to specify
a joint (more similarly to how its done in URDF). But the generated
underlying data structure is just a tree of frames.
Editing using kinEdit
Whenever working with .g-files, you should try to display them using
the $RAI/bin/kinEdit
command line tool. CMake automatically
compiles it; otherwise call ‘make bin’ in $RAI to compile this. Then
you can call kinEdit someFile.g
on any model file. (In
python, the equivalence is to reload the configuration from file
repeatedly.) Whenever kinEdit
reads a file, it also outputs a
file z.g
and z.urdf
of what it read. Sometimes it is useful to
look into this. It can also be used to clean and prune kinematic
structures.
But more than that, you can keep the display open when editing the file in any text editor. Whenever you save the file the display will notice it, reload the file, and display the updated model. This allows some degree of continuous editing. You might sometimes have to hit enter in the window to enforce reloading. The little tool tries to catch and report on syntax errors and be robust, but it crashes on some syntax errors and then needs to be restarted manually.
Import from URDF
You can convert URDF files to .g-files using the rai/bin/urdf2rai.py
script. However, the overall conversion is only partially automatic. The
resulting g-file encodes the full kinematic structure, but the mesh files usually require manual fiddling. First, in the
g-file, you have to change the path to their location in the file
system (removing the ‘package’ part). Potentially that’s all you
need. However, the rai code calls various collision libraries that need clean
and correct (orientation, holes, etc) mesh files. Those that come with
URDF files are typically not clean and correct. I typically use
meshlab (the command line tool) to automatically clean and compress
meshes into ply files.
The best guide for the whole conversion pipeline is hubo/HOWTO.sh in the rai-robotModels repository, which also describes mesh cleaning scripts. We also managed to import a [full kitchen](https://github.com/MarcToussaint/rai-robotModels/tree/master/bremenKitchen) from unreal, where we first exported the description to JSON. There are also some working examples to import gltf.
Finally, the collada file format can represent trees of frames and objects, which can be loaded. This can be augmented with just a little extra information on joints to make this a properly articulated robot world.
Notation to specify transformations
Transformation can always be specified as 7-vectors Q:[p1 p2 p3 q0
q1 q2 q3]
(position, quaternion), or also 3- or 4-vectors if you
only want to set position or orientation. But this is not always intuitive
for human editing. Therefore, the bracket notation <...>
allows
for another notation, namely as a chaining of little interpretable
transformations, as in the old turtle language.
Specifically, you specify a transform by chaining:
t(x y z) # translation by (x,y,z)
q(q0 q1 q2 q3) # rotation by a quaternion
r(r x y z) # rotation by `r` _radians_ around the axis (x,y,z)
d(d x y z) # rotation by `d` _degrees_ around the axis (x,y,z)
E(r p y) # rotation by roll-pitch-yaw Euler angles
Joint types
The libry.JT
enum (in python; or rai::JointType in C++) lists all available joint type. Currently these are:
hingeX, hingeY, hingeZ, transX, transY, transZ, transXY, trans3, transXYPhi, universal, rigid, quatBall, phiTransXY, XBall, free, tau
A quatBall is a quaternion ball joint with 4 dofs (that supports all differentiability and optimization); a free joint is a full 7 dof joint; a rigid joint might seem redundant, but internally it sometimes markes a break between separate objects (like an object sitting rigidly on a table) rather than having multiple shapes attached to the same object.
The joint’s dofs can be initialized equivalently either with a q
attribute (defining the dofs values), or a Q
attribute (defining
the resulting relative transformation generated by the joint).