Remote Tree Viewer Interface

Most of this documentation focuses on developing visualizations from within Director itself, but it is also possible to use Director as a standalone remote-controlled 3D viewer. By sending messages from your application over LCM, you can create and move geometric primitives in the viewer.

To launch the viewer, run the drake-visualizer binary which is created by Director. For an example of a Python client, see director/viewerclient.py. The remote tree viewer protocol is described below. The current implementation uses LCM for the message transport and JSON to encode the viewer commands. Future versions may add new encoding methods for better performance.

Viewer Tree

The remote viewer is built around the concept of a tree of objects (in other words, an acyclic scene graph). We refer to nodes in the tree by their path, that is, by the list of nodes traversed from the root of the tree. We can create and delete 3D objects anywhere in the tree by specifying their paths, and we can set the homogeneous transform of any path. The pose of an object in the viewer is determined by the composition of all transforms in its path.

For example, if we have a viewer with two objects, a box at path robot/box1 and a box at path robot/box2, our tree will look like:

-robot
   |-box1
   |-box2

If we apply a transform to the path robot/box1, then only box1 will be affected, but if we apply a transform to the path robot, then both boxes will move together.

Protocol

Communication with the viewer happens over two LCM channels:

DIRECTOR_TREE_VIEWER_REQUEST_<client_id>:
 for communication from the client to the viewer
DIRECTOR_TREE_VIEWER_RESPONSE_<client_id>:
 for communication from the viewer back to the client

The client_id can consist of any characters except < and >, and it should be unique for each client. Note that the ID needs to be enclosed within literal < and > characters.

Two-way communication is not mandatory: a simple client may just publish on _REQUEST and ignore all responses. But handling responses from the viewer enables better synchronization of the client and viewer state, as discussed in responses.

All communication on both channels uses a single general-purpose LCM type: viewer2_comms_t. The viewer2_comms_t message contains a byte array of encoded command data along with information about the format of that data. Currently, only one format is defined: treeviewer_json version 1.0. This format consists of a string of JSON-encoded commands.

Sample LCM message:

msg.format = "treeviewer_json"
msg.format_version_major = 1
msg.format_version_minor = 0
msg.data = data_payload
msg.num_bytes = len(data_payload)

The data_payload is a JSON-encoded dictionary with four fields:

timestamp:Microseconds since the Unix epoch and expressed as an integer
setgeometry:A list of setgeometry commands. May be empty.
settransform:A list of settransform commands. May be empty.
delete:A list of delete commands. May be empty.

Setgeometry Command

The setgeometry command sets geometric object at a given path, creating nodes in the viewer tree as necessary. It will overwrite any geometry that was already present at that path in the viewer. A setgeometry command is a dictionary with the following keys:

path:the path for the geometry in the tree as a list of strings
geometries:a list of geometry descriptions

Each geometry description, in turn, is a dictionary with the key type that indicates the kind of geometric primitive. Various geometry descriptions have other mandatory or optional fields:

Fields for all geometry types:

type:(required) a keyword indicating the kind of geometry. Currently supports: box, sphere, cylinder, capsule, ellipsoid, mesh_file, mesh_data, pointcloud, planar_lidar, triad, line
color:(optional) the geometry’s color, in RGBA order, as a list of 4 floating point values between 0 and 1. Defaults to [1, 1, 1, 1]
transform:(optional) a transform to apply to this geometry, in the same format as used in settransform. Defaults to identity.

Box fields:

lengths:the lengths of the box along each dimension, in XYZ order as a list of 3 floating point values

Sphere fields:

radius:radius of the sphere

Cylinder fields:

radius:radius
length:length along the Z axis

Capsule fields:

radius:radius of the capsule and the hemispheres at each end
length:length of the capsule, not including the end caps

Ellipsoid Fields:

radii:the radii of the ellipsoid along the XYZ axes

Mesh File Fields:

A mesh_file geometry type contains a path to a mesh file on disk.

filename:the absolute path to the mesh file
scale:(optional) a scaling factor to apply to the mesh. Defaults to 1.

Mesh Data Fields:

A mesh_data geometry encodes the entire mesh in the message itself..

vertices:the mesh vertices as a list of triplets in XYZ order.
faces:a list of triplets describing the faces of the mesh. Each element in the list gives the indices (0-based) of three vertices which are joined as a triangular face.

Pointcloud Fields:

points:the point coordinates as a list of triplets in XYZ order
channels:(optional) a dictionary of channel data to attach to the points. Each key in the dict should be the name of the channel, and the corresponding value should be a list of length (number of points). The channel rgb is special: its value (a list of triplets in RGB order) will be used to color the points by default.

Planar Lidar Fields:

The planar_lidar type is simply a shortcut for generating a pointcloud without specifying the coordinates of every point. Instead, we only need to specify a list of distances and the angles over which those ranges were measured.

ranges:a list of distance measurements
angle_start:the angle about the Z axis (in rad) corresponding to ranges[0]
angle_step:the angular spacing between adjacent range measurements (in rad) about the Z axis

Line Fields:

The line type generates a line through a sequence of two or more points.

points:the point coordinates as a list of 2 or more triplets in XYZ order.
radius:(optional) the radius of the line. A radius of 0 creates a hairline. A radius > 0 creates a tube. Defaults to 0.01
closed:(optional) a boolean indicating whether to add a segment from the last point back to the first. Defaults to False.
start_head:(optional) a boolean indicating whether to draw an arrowhead at the beginning of the first line segment. Defaults to False.
end_head:(optional) a boolean indicating whether to draw an arrowhead at the end of the last line segment. Defaults to False.
head_radius:(optional) radius of the arrowheads. Defaults to 0.05.
head_length:(optional) length of the arrowheads. Defaults to head_radius

Settransform Command

The settransform command sets the transform of a given path. A command has two fields:

path:the path, as a list of strings
transform:the transform, as a dictionary

The format of the transform field is a dictionary with two keys:

translation:(optional) the translational component, in [x, y, z] order. Defaults to [0, 0, 0].
quaternion:(optional) the rotational component, in [w, x, y, z] order. Defaults to [1, 0, 0, 0]

Delete Command

A delete command has just one field:

path:the path of the geometry to delete. Any geometries and transforms at this path and its descendants will be deleted.

Examples

Create a box at path “robot1/link1”:

{
        "timestamp": 1486691399249288,
        "setgeometry": [
                {
                        "path": ["robot1", "link1"],
                        "geometry": {
                                "type": "box",
                                "color": [1, 0, 0, 0.5],
                                "lengths": [1, 0.5, 2]
                        }
                }
        ],
        "settransform": [],
        "delete": []
}

Create a sphere at path “robot1/link2” and a pointcloud at “perception/cloud1”:

{
        "timestamp": 1486691399249288,
        "setgeometry": [
                {
                        "path": ["robot1", "link2"],
                        "geometry": {
                                "type": "sphere",
                                "radius": 0.5
                        }
                },
                {
                        "path": ["perception", "cloud1"],
                        "geometry": {
                                "type": "pointcloud",
                                "points": [[0.0, 0, 0],
                                           [0.1, 0, 0],
                                           [0.2, 0, 0],
                                           [0.3, 0, 0]]
                                "channels": {
                                        "rgb": [[1, 1, 1],  # white
                                                [1, 0, 0],  # red
                                                [0, 1, 0],  # green
                                                [0, 0, 1]]  # blue
                                }
                        }
                },
        ],
        "settransform": [],
        "delete": []
}

Translate the entire robot group 1 meter in Z:

{
        "setgeometry": [],
        "settransform":
                {
                        "path": ["robot1"],
                        "transform": {
                                "translation": [0, 0, 1],
                                "quaternion": [1, 0, 0, 0]
                        }
                }
        ],
        "delete": []
}

Delete the sphere at robot1/link2:

{
        "setgeometry": [],
        "settransform": []
        "delete": [
                {
                        "path": ["robot1", "link2"]
                }
        ]
}

Handling Responses

When the viewer receives a request, it will respond with a message on the DIRECTOR_TREE_VIEWER_RESPONSE channel. This message will be in the same format as the request: an LCM message containing JSON-encoded data.

The viewer response data is a dictionary with at least one field. It will always contain the field status with an integer value. The following status values are defined:

status == 0:OK
status == 1:MISSING_PATHS
status == -1:ERROR_UNKNOWN_FORMAT
status == -2:ERROR_UNKNOWN_FORMAT_VERSION

Status MISSING_PATHS means that the viewer received a settransform command for a path which has no geometry at that path or any of its descendants. This can happen if, for example, the client sends a settransform before sending a setgeometry, or if the viewer is restarted and loses its state. A MISSING_PATHS status will be accompanied by a field missing_paths in the viewer response data, listing all of the paths which were not found. The client should send the appropriate setgeometry commands for those paths.