Visuals and collisions
A geometry is a simple shape, such as a box, sphere, or capsule, that represents an object’s physical extent. Viam uses one set of geometry types for two jobs: drawing a custom visual in the 3D scene, and telling the motion planner about an obstacle.
To create a custom visual, you attach a geometry to a transform’s physical_object
field and provide
that transform to a world state store service.
This service holds the transforms you publish and streams them to the 3D scene. This
page covers what a transform contains, the difference between planner geometry and
visualization geometry, and how to build each geometry type.
Anatomy of a transform
A Transform carries four things that together place and style a visual:
- Reference frame and pose: the visual’s origin.
- Geometry (the
physical_objectfield): the shape to draw (a box, sphere, capsule, mesh, or point cloud). - Metadata: styling such as color and opacity.
- UUID: a stable identifier for this specific visual.
The reference frame and pose decide where, the geometry decides what shape, and the metadata decides how it looks.
The UUID gives a visual a stable identity
Each transform has a UUID. That identifier is what lets a module change one visual without disturbing the rest of the scene: to update a visual, the module re-sends a transform with the same UUID; to remove it, it references that UUID; to add a new one, it uses a fresh UUID. Without stable identifiers the client would have to re-render everything on every change. With them, the scene applies incremental add, update, and remove operations to individual visuals.
Metadata styles the visual
The metadata is a set of rendering attributes the scene reads when it draws the geometry:
colors: the fill color, or one color per point for point cloud geometryopacities: how transparent the shape isshow_axes_helper: draws a coordinate triad at the visual’s origininvisible: hides the visual by default
The scene reads only the keys it recognizes and ignores the rest. These are all
visualization attributes: they control how the visual looks.
The planner reads its solid geometry from the frame
system and the WorldState you pass to Move, so
metadata changes what you see without changing what the planner plans around.
The scene draws, the planner collision-checks
The geometry on a world state store transform renders in the 3D scene: publishing a box draws a box. The motion planner collision-checks a separate geometry, which it reads from two places:
- The frame system: each component’s
frame.geometry. - The
WorldStateyou pass to aMovecall: obstacles and transforms supplied for that single planning request.
Despite the similar names, these are different things: the
world state store service holds transforms
for the scene to draw, and the WorldState you pass to
Move carries obstacles for the planner to avoid. A world state store transform and a
WorldState obstacle travel two paths, each with its own job: one is drawn in the scene,
the other is planned around. The same shape can take both paths.
Making a geometry both visible and collision-checked
If you want a geometry to appear in the scene and be avoided by the planner, you do both, separately:
- For the scene: publish it as a transform through the world state store service (see Publish visuals from a module).
- For planning: add it to the frame system, or include it in the
WorldStateyou pass toMove.
Today these are two separate outputs you produce from the same source data: one transform for the scene, one geometry for the planner.
Geometry types
The supported types are:
- box: dimensions in millimeters
- sphere: a radius
- capsule: a radius and length
- mesh: an arbitrary triangle mesh
- point cloud: a set of points
Choose the type that matches what you are representing: a box or capsule to approximate a physical object, a mesh for a precise model, a point cloud for sensor data. For guidance on matching a geometry type to a physical object, see the geometry types table.
You build a geometry as a Geometry proto, the same type a world state store
transform and a WorldState obstacle both carry. The Python SDK and the Go SDK
construct that proto directly, with no helper library. The box, sphere, and
capsule primitives also have a machine config (JSON) form. The transform you
attach the geometry to supplies its reference frame and pose.
For the constructor for each type, in JSON, Python, and Go, see Geometry constructors.
Assemble a transform
A geometry is only the shape. To render it as a visual, wrap it in a transform
that carries the other anatomy fields: a reference frame and pose, metadata, and a UUID.
The draw library assembles them for you, so you do not build the Transform proto by
hand:
import (
"github.com/golang/geo/r3"
"github.com/viam-labs/motion-tools/draw"
"go.viam.com/rdk/spatialmath"
)
// A red box at (400, 0, 200) in the world frame.
box, err := spatialmath.NewBox(
spatialmath.NewZeroPose(), r3.Vector{X: 100, Y: 100, Z: 100}, "target-box",
)
if err != nil {
return nil, err
}
drawn, err := draw.NewDrawnGeometry(
box,
draw.WithGeometryColor(draw.NewColor(draw.WithName("red"))), // metadata: color
)
if err != nil {
return nil, err
}
transform, err := drawn.Draw(
"target-box", // the name of this visual's frame
draw.WithID("target-box"), // UUID: a stable identity for later updates
draw.WithParent("world"), // reference frame the pose is expressed in
draw.WithPose(spatialmath.NewPoseFromPoint(r3.Vector{X: 400, Y: 0, Z: 200})), // pose
)
Publish transform through a world state store service to draw it. For the full module
that serves transforms this way, see
Publish visuals from a module.
What’s next
- Publish visuals from a module: implement a world state store service that publishes transforms.
- Define obstacles: the geometry the planner collision-checks.
- Frame system: how the planner gets the geometry and frames it plans around.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!