Visualization
moro provides visualization tools for inspecting robot configurations and animations.
The main interface is RobotVisualizer, which takes a Robot model and evaluates its symbolic kinematics at numerical configurations before passing the resulting scene to one of the available rendering backends.
Two backends are currently available:
Matplotlib, for static 3D plots and Python-side animations;
Three.js, for interactive browser-based visualization.
This section focuses on the recommended high-level workflow. Lower-level visualization objects such as SceneData, FrameData, and the backend classes are documented in the API Reference.
Creating a visualizer
Start by defining a robot:
from moro import Robot
from moro.abc import q1, q2, l1, l2
robot = Robot(
(l1, 0, 0, q1, "r"),
(l2, 0, 0, q2, "r"),
)
Then create a visualizer:
from moro.visualization import RobotVisualizer
viz = RobotVisualizer(robot)
A RobotVisualizer is associated with a single Robot instance.
The same visualizer can then be reused for different configurations, backends, and animations.
Plotting a robot configuration
A single robot configuration is displayed with:
viz.plot(...)
For example:
values = {
l1: 1.0,
l2: 0.8,
q1: 0.5,
q2: 0.8,
}
viz.plot(values)
The first argument is a dictionary that maps symbolic robot parameters to numerical values.
Because Matplotlib is the default backend, the previous call is equivalent to:
viz.plot(
values,
backend="matplotlib",
)
Providing numerical values
Visualization requires numerical frame transformations.
For a fully numerical robot model, only the joint variables may need to be supplied:
robot = Robot(
(1.0, 0, 0, q1, "r"),
(0.8, 0, 0, q2, "r"),
)
values = {
q1: 0.5,
q2: 0.8,
}
For a symbolic robot model:
robot = Robot(
(l1, 0, 0, q1, "r"),
(l2, 0, 0, q2, "r"),
)
the geometric parameters must also be provided:
values = {
l1: 1.0,
l2: 0.8,
q1: 0.5,
q2: 0.8,
}
Internally, moro evaluates the transformation of each robot frame with respect to the base frame and converts the resulting matrices to numerical scene data.
Therefore, all symbolic quantities required by the robot transformations must have numerical values before rendering.
Choosing a backend
The backend is selected with:
backend=...
The available values are:
"matplotlib"
"threejs"
For example:
viz.plot(
values,
backend="matplotlib",
)
or:
viz.plot(
values,
backend="threejs",
)
Both backends use the same evaluated robot geometry and the same VisualizationStyle interface, but their rendering capabilities differ.
Matplotlib visualization
The Matplotlib backend creates a standard 3D Matplotlib figure.
Use:
fig, ax = viz.plot(
values,
backend="matplotlib",
)
The result contains:
fig → Matplotlib Figure
ax → 3D Matplotlib Axes
This makes it possible to continue customizing the plot using normal Matplotlib operations.
For example:
ax.set_title("Planar 2R robot")
The backend does not call plt.show() automatically, so display remains under user control.
In scripts, the figure can be shown with:
import matplotlib.pyplot as plt
plt.show()
Figure size
The default figure size is:
figsize=(10, 8)
and can be changed with:
fig, ax = viz.plot(
values,
backend="matplotlib",
figsize=(8, 6),
)
Camera orientation
The initial camera orientation can be configured with:
view_init=(elevation, azimuth)
For example:
fig, ax = viz.plot(
values,
backend="matplotlib",
view_init=(40, 25),
)
This follows the usual Matplotlib 3D view convention.
Interactive Three.js visualization
The Three.js backend produces an interactive view intended primarily for notebook environments.
Use:
viz.plot(
values,
backend="threejs",
)
The result is returned as an IPython.display.HTML object and can be displayed directly in environments such as Jupyter Notebook or JupyterLab.
The default rendering area is:
width=800
height=600
and can be changed with:
viz.plot(
values,
backend="threejs",
width=900,
height=650,
)
Preset views
The interactive viewer includes buttons for:
Front
Top
Isometric
These controls change the camera directly inside the viewer.
The initial view is isometric.
Camera type
The viewer supports both:
Orthographic
Perspective
projection.
The orthographic camera is selected initially.
The camera type can be changed interactively without recomputing the robot model.
Customizing the visualization
Visual appearance is configured through:
from moro.visualization import VisualizationStyle
For example:
style = VisualizationStyle(
show_frames=False,
show_grid=True,
link_linewidth=5,
)
Then pass the same style object to either backend:
viz.plot(
values,
backend="matplotlib",
style=style,
)
or:
viz.plot(
values,
backend="threejs",
style=style,
)
Visibility options
The main visibility controls are:
VisualizationStyle(
show_frames=True,
show_links=True,
show_joints=True,
show_base=True,
show_grid=True,
)
For example, to display only the robot links and joints:
style = VisualizationStyle(
show_frames=False,
show_grid=False,
)
Colors
The default colors can also be changed:
style = VisualizationStyle(
link_color="#444444",
joint_color="#d62728",
base_color="#000000",
)
Object sizes
The following properties control the size of the displayed elements:
frame_scale
joint_size
base_size
link_linewidth
When:
frame_scale=None
joint_size=None
base_size=None
their values are derived automatically from the dimensions of the robot scene.
This makes the default visualization scale appropriately for robots of different sizes.
Animating robot configurations
A sequence of robot configurations can be animated with:
viz.animate(...)
The input is a sequence of substitution dictionaries.
For example:
configurations = [
{
l1: 1.0,
l2: 0.8,
q1: 0.0,
q2: 0.0,
},
{
l1: 1.0,
l2: 0.8,
q1: 0.2,
q2: 0.1,
},
{
l1: 1.0,
l2: 0.8,
q1: 0.4,
q2: 0.2,
},
]
Then:
animation = viz.animate(
configurations,
)
Again, Matplotlib is the default backend.
An empty configuration sequence is not valid.
Matplotlib animations
Use:
animation = viz.animate(
configurations,
backend="matplotlib",
)
The result is a:
matplotlib.animation.FuncAnimation
Keep a reference to the returned animation object until it has been displayed or saved.
For example:
animation = viz.animate(
configurations,
backend="matplotlib",
)
rather than calling viz.animate(...) without assigning the result.
Frame interval
The default interval is:
interval=100
milliseconds.
It can be changed with:
animation = viz.animate(
configurations,
backend="matplotlib",
interval=50,
)
The spatial limits of the animation are computed from the complete sequence, so the camera scale remains fixed while the robot moves.
Three.js animations
Use:
viz.animate(
configurations,
backend="threejs",
)
The result is an interactive HTML animation.
The viewer includes controls for:
Play and Pause;
direct frame selection;
a frame slider;
Front, Top, and Isometric views;
orthographic and perspective cameras;
camera reset;
free orbit navigation.
The animation uses one global spatial scale for the complete sequence, so the apparent sizes of links, joints, and coordinate frames remain consistent throughout the motion.
Showing the end-effector trajectory
Animations can optionally display the Cartesian path followed by the end-effector.
Create a style with:
style = VisualizationStyle(
show_trajectory=True,
)
and use it in the animation:
viz.animate(
configurations,
backend="threejs",
style=style,
)
The same option is supported by the Matplotlib backend.
The displayed trajectory corresponds to the positions of the final robot frame throughout the animation sequence.
Trajectory display modes
Two display modes are available:
"full"
"trace"
The default is:
trajectory_mode="full"
With "full", the complete end-effector trajectory is displayed.
With:
style = VisualizationStyle(
show_trajectory=True,
trajectory_mode="trace",
)
the displayed path grows as the animation advances.
Trajectory appearance can also be customized with:
trajectory_color
trajectory_linewidth
For example:
style = VisualizationStyle(
show_trajectory=True,
trajectory_color="#222222",
trajectory_linewidth=3,
)
Animating an inverse-kinematics trajectory
RobotVisualizer.animate() expects substitution dictionaries, while solve_position_trajectory() returns joint vectors through:
trajectory.qs
The two APIs can be connected easily.
Suppose an IK trajectory has already been computed:
trajectory = solve_position_trajectory(
robot,
targets,
q0=[0.1, 0.1],
parameters={
l1: 1.0,
l2: 0.8,
},
)
First verify that the trajectory converged:
if trajectory.converged:
print("Trajectory solved.")
Then convert each joint vector into a substitution dictionary:
configurations = [
dict(zip(robot.qs, q))
for q in trajectory.qs
]
For a robot with symbolic geometric parameters, include them in each configuration:
parameters = {
l1: 1.0,
l2: 0.8,
}
configurations = [
{
**parameters,
**dict(zip(robot.qs, q)),
}
for q in trajectory.qs
]
Now animate the result:
style = VisualizationStyle(
show_trajectory=True,
trajectory_mode="trace",
)
viz.animate(
configurations,
backend="threejs",
style=style,
)
This provides a convenient workflow from Cartesian position targets to a visual representation of the corresponding robot motion.
A worked example
Consider a planar 2R robot:
import numpy as np
from moro import Robot
from moro.abc import q1, q2, l1, l2
from moro.visualization import (
RobotVisualizer,
VisualizationStyle,
)
robot = Robot(
(l1, 0, 0, q1, "r"),
(l2, 0, 0, q2, "r"),
)
viz = RobotVisualizer(robot)
Define the geometric parameters and one configuration:
values = {
l1: 1.0,
l2: 0.8,
q1: np.pi / 4,
q2: -np.pi / 6,
}
A static Matplotlib plot can be created with:
fig, ax = viz.plot(
values,
backend="matplotlib",
view_init=(35, 35),
)
The same configuration can be inspected interactively with Three.js:
viz.plot(
values,
backend="threejs",
)
Now create a sequence of configurations:
configurations = []
for theta in np.linspace(0, np.pi / 2, 40):
configurations.append({
l1: 1.0,
l2: 0.8,
q1: theta,
q2: -theta / 2,
})
Create a visualization style that shows the end-effector path:
style = VisualizationStyle(
show_trajectory=True,
trajectory_mode="trace",
)
Then animate the robot:
viz.animate(
configurations,
backend="threejs",
style=style,
)
The resulting viewer allows the animation to be played, paused, inspected frame by frame, and viewed using different camera projections.
The same data can be rendered with Matplotlib:
animation = viz.animate(
configurations,
backend="matplotlib",
interval=75,
style=style,
)
Notes and limitations
The visualization module is intended primarily for inspecting kinematic robot models and computed joint trajectories.
Keep the following points in mind:
visualization operates on the
Robotkinematic model;all symbolic quantities required by the robot transformations must be assigned numerical values;
RobotVisualizer.plot()renders one configuration;RobotVisualizer.animate()renders a sequence of configurations;Matplotlib is the default backend;
the Three.js backend returns interactive HTML and is especially convenient in notebook environments;
Three.js preset views and camera types are controlled from the viewer interface rather than through
RobotVisualizer.plot()arguments;visualization styles can be shared between both backends;
automatic scene scaling is used when explicit object sizes are not provided;
animation scaling remains fixed across the complete sequence;
the optional trajectory display corresponds to the Cartesian path of the final robot frame.
The current visualization module represents robots using kinematic links, joints, and coordinate frames. It is not intended to provide detailed CAD geometry or physically realistic rendering.
The visualization system does not currently provide:
collision detection;
contact visualization;
physics simulation;
URDF or mesh-based robot geometry;
automatic trajectory generation;
interactive joint sliders;
dynamic-force or torque visualization.
These capabilities may require additional modeling or visualization infrastructure beyond the current scope.
See also
Robot Modeling — define the serial robot to be visualized.
Forward Kinematics — compute the frame transformations used by the visualization system.
Inverse Kinematics — generate joint configurations for Cartesian targets and trajectories.
Jacobians — inspect differential kinematic quantities associated with the robot motion.
API Reference → Visualization — complete reference for
RobotVisualizer,VisualizationStyle, backend classes, and scene-data types.