Interactive Robot Control with Gizmo#
This tutorial demonstrates how to use the Gizmo class for interactive robot manipulation in SimulationManager. You’ll learn how to create a gizmo attached to a robot’s end-effector and use it for real-time inverse kinematics (IK) control, allowing intuitive manipulation of robot poses through visual interaction.
The Code#
The tutorial corresponds to the gizmo_robot.py script in the scripts/tutorials/sim directory.
Code for gizmo_robot.py
1# ----------------------------------------------------------------------------
2# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15# ----------------------------------------------------------------------------
16"""
17Gizmo-Robot Example: Test Gizmo class on a robot (UR10)
18"""
19
20import time
21import torch
22import numpy as np
23import argparse
24
25from embodichain.lab.sim import SimulationManager, SimulationManagerCfg
26from embodichain.lab.gym.utils.gym_utils import add_env_launcher_args_to_parser
27from embodichain.lab.sim.cfg import (
28 RenderCfg,
29 RobotCfg,
30 URDFCfg,
31 JointDrivePropertiesCfg,
32)
33
34from embodichain.lab.sim.solvers import PinkSolverCfg
35from embodichain.data import get_data_path
36from embodichain.utils import logger
37
38
39def main():
40 """Main function to create and run the simulation scene."""
41
42 # Parse command line arguments
43 parser = argparse.ArgumentParser(
44 description="Create a simulation scene with SimulationManager"
45 )
46 add_env_launcher_args_to_parser(parser)
47 args = parser.parse_args()
48
49 # Configure the simulation
50 sim_cfg = SimulationManagerCfg(
51 width=1920,
52 height=1080,
53 physics_dt=1.0 / 100.0,
54 sim_device=args.device,
55 render_cfg=RenderCfg(renderer=args.renderer),
56 )
57
58 sim = SimulationManager(sim_cfg)
59 sim.set_manual_update(False)
60
61 # Get UR10 URDF path
62 urdf_path = get_data_path("UniversalRobots/UR10/UR10.urdf")
63
64 # Create UR10 robot
65 robot_cfg = RobotCfg(
66 uid="ur10_gizmo_test",
67 urdf_cfg=URDFCfg(
68 components=[{"component_type": "arm", "urdf_path": urdf_path}]
69 ),
70 control_parts={"arm": ["Joint[1-6]"]},
71 solver_cfg={
72 "arm": PinkSolverCfg(
73 urdf_path=urdf_path,
74 end_link_name="ee_link",
75 root_link_name="base_link",
76 pos_eps=1e-2,
77 rot_eps=5e-2,
78 max_iterations=300,
79 dt=0.1,
80 )
81 },
82 drive_pros=JointDrivePropertiesCfg(
83 stiffness={"Joint[1-6]": 1e4},
84 damping={"Joint[1-6]": 1e3},
85 ),
86 )
87 robot = sim.add_robot(cfg=robot_cfg)
88
89 # Set initial joint positions
90 initial_qpos = torch.tensor(
91 [[0, -np.pi / 2, np.pi / 2, 0.0, np.pi / 2, 0.0]],
92 dtype=torch.float32,
93 device="cpu",
94 )
95 joint_ids = robot.get_joint_ids("arm")
96 robot.set_qpos(qpos=initial_qpos, joint_ids=joint_ids)
97
98 time.sleep(0.2) # Wait for a moment to ensure everything is set up
99
100 # Enable gizmo using the new API
101 sim.enable_gizmo(uid="ur10_gizmo_test", control_part="arm")
102 if not sim.has_gizmo("ur10_gizmo_test", control_part="arm"):
103 logger.log_error("Failed to enable gizmo!")
104 return
105
106 sim.open_window()
107
108 logger.log_info("Gizmo-Robot example started!")
109 logger.log_info("Use the gizmo to drag the robot end-effector (EE)")
110 logger.log_info("Press Ctrl+C to stop the simulation")
111
112 run_simulation(sim)
113
114
115def run_simulation(sim: SimulationManager):
116 step_count = 0
117 try:
118 last_time = time.time()
119 last_step = 0
120 while True:
121 time.sleep(0.033) # 30Hz
122 # Update all gizmos managed by sim
123 sim.update_gizmos()
124 step_count += 1
125
126 if step_count % 100 == 0:
127 current_time = time.time()
128 elapsed = current_time - last_time
129 fps = (
130 sim.num_envs * (step_count - last_step) / elapsed
131 if elapsed > 0
132 else 0
133 )
134 logger.log_info(f"Simulation step: {step_count}, FPS: {fps:.2f}")
135 last_time = current_time
136 last_step = step_count
137 except KeyboardInterrupt:
138 logger.log_info("\nStopping simulation...")
139 finally:
140 sim.destroy()
141 logger.log_info("Simulation terminated successfully")
142
143
144if __name__ == "__main__":
145 main()
The Code Explained#
Similar to the previous tutorial on robot simulation, we use the SimulationManager class to set up the simulation environment. If you haven’t read that tutorial yet, please refer to Simulating a Robot first.
Important: Gizmo only supports single environment mode (num_envs=1). Using multiple environments will raise an exception.
All gizmo creation, visibility, and destruction operations must be managed via the SimulationManager API:
# Toggle visibility for a gizmo
sim.toggle_gizmo_visibility("ur10_gizmo_test", control_part="arm")
# Set visibility explicitly
sim.set_gizmo_visibility("ur10_gizmo_test", visible=False, control_part="arm")
Always use the SimulationManager API to control gizmo visibility and lifecycle. Do not operate on the Gizmo instance directly.
What is a Gizmo?#
A Gizmo is an interactive visual tool that allows users to manipulate simulation objects in real-time through mouse interactions. In robotics applications, gizmos are particularly useful for:
Interactive Robot Control: Drag the robot’s end-effector to desired positions
Inverse Kinematics: Automatically solve joint angles to reach target poses
Real-time Manipulation: Provide immediate visual feedback during robot motion planning
Debugging and Visualization: Test robot reachability and workspace limits
The objects.Gizmo class provides a unified interface for interactive control of different simulation elements including robots, rigid objects, and cameras.
Setting up Robot Configuration#
First, we configure a UR10 robot with an IK solver for end-effector control:
Key components of the robot configuration:
URDF Configuration: Loads the robot’s kinematic and visual model
Control Parts: Defines which joints can be controlled (
"Joint[1-6]"for UR10)IK Solver:
solvers.PinkSolverCfgprovides inverse kinematics capabilitiesDrive Properties: Sets stiffness and damping for joint control
The IK solver is crucial for gizmo functionality, as it enables the robot to automatically calculate joint angles needed to reach gizmo target positions.
Creating and Attaching a Gizmo#
After configuring the robot, enable the gizmo for interactive control using the SimulationManager API (supports robot, rigid object, camera; key is uid:control_part):
# Enable gizmo for the robot's arm
sim.enable_gizmo(uid="ur10_gizmo_test", control_part="arm")
if not sim.has_gizmo("ur10_gizmo_test", control_part="arm"):
logger.log_error("Failed to enable gizmo!")
return
The Gizmo instance is managed internally by SimulationManager. If you need to access it:
gizmo = sim.get_gizmo("ur10_gizmo_test", control_part="arm")
The Gizmo system will automatically:
Detect Target Type: Identify that the target is a robot (vs. rigid object or camera)
Find End-Effector: Locate the robot’s end-effector link (
ee_linkfor UR10)Create Proxy Object: Generate a small invisible cube at the end-effector position
Set Up IK Callback: Configure the gizmo to trigger IK solving when moved
How Gizmo-Robot Interaction Works#
The gizmo-robot interaction follows this efficient workflow:
Gizmo Callback: When the user drags the gizmo, a callback function updates the proxy object’s transform
Deferred IK Solving: Instead of solving IK immediately in the callback (which causes UI lag), the target transform is stored
Update Loop: During each simulation step,
gizmo.update()solves IK and applies joint commandsRobot Motion: The robot smoothly moves to follow the gizmo position
This design separates UI responsiveness from computational IK solving, ensuring smooth interaction even with complex robots.
The Simulation Loop#
In the main loop, simply call sim.update_gizmos(). There is no need to manually update any Gizmo instance.
def run_simulation(sim: SimulationManager):
step_count = 0
try:
last_time = time.time()
last_step = 0
while True:
time.sleep(0.033) # 30Hz
sim.update_gizmos() # Update all gizmos
step_count += 1
# ...performance statistics, etc...
except KeyboardInterrupt:
logger.log_info("\nStopping simulation...")
finally:
sim.destroy() # Release all resources
logger.log_info("Simulation terminated successfully")
Main loop highlights:
Gizmo update: Only sim.update_gizmos() is needed, no gizmo.update()
Performance monitoring: Optional FPS statistics
Resource cleanup: Only sim.destroy() is needed, no manual Gizmo destruction
Graceful shutdown: Supports Ctrl+C interruption
Gizmo Lifecycle Management#
Gizmo lifecycle is managed by SimulationManager:
Enable: sim.enable_gizmo(…)
Update: Main loop automatically calls sim.update_gizmos()
Destroy/disable: sim.disable_gizmo(…) or sim.destroy() (recommended)
There is no need to manually create or destroy Gizmo instances. All resources are managed by SimulationManager.
Available Gizmo Methods#
If you need to access the underlying Gizmo instance (via sim.get_gizmo), you can use the following methods:
Transform Control:
set_world_pose(pose): Set gizmo world position and orientationget_world_pose(): Get current gizmo world transformset_local_pose(pose): Set gizmo local transform relative to parentget_local_pose(): Get gizmo local transform
Visual properties (strongly recommend using SimulationManager API):
sim.toggle_gizmo_visibility(uid, control_part=None): Toggle gizmo visibilitysim.set_gizmo_visibility(uid, visible, control_part=None): Set gizmo visibility
Hierarchy Management:
get_parent(): Get gizmo’s parent node in scene hierarchyget_name(): Get gizmo node name for debuggingdetach(): Disconnect gizmo from current targetattach(target): Attach gizmo to a new simulation object
Running the Tutorial#
To run the gizmo robot tutorial:
cd scripts/tutorials/sim
python gizmo_robot.py --device cpu
Command-line options:
--device cpu|cuda: Choose simulation device--num_envs N: Number of parallel environments--headless: Run without GUI for automated testing--renderer: Enable ray tracing for better visuals
Once running:
Mouse Interaction: Click and drag the gizmo (colorful axes) to move the robot
Real-time IK: Watch the robot joints automatically adjust to follow the gizmo
Workspace Limits: Observe how the robot behaves at workspace boundaries
Performance: Monitor FPS in the console output
Tips and Best Practices#
Performance optimization:
Only call
sim.update_gizmos()in the main loop, no need forgizmo.update()Reduce IK solver iterations for better real-time performance if needed
Use
set_manual_update(False)for smoother interaction
Debugging tips:
Check console output for IK solver success/failure messages
Use
get_world_pose()to check gizmo position (if needed)Monitor FPS to identify performance bottlenecks
Robot compatibility:
Ensure your robot is configured with a correct IK solver
Check the end-effector (EE) link name
Test joint limits and workspace boundaries
Visualization customization:
Adjust gizmo appearance via Gizmo config (e.g.,
set_line_width(); requires access to the instance via sim.get_gizmo)Adjust gizmo scale according to robot size
Enable collision for debugging if needed
Next Steps#
After mastering basic gizmo usage, you can explore:
Multi-robot Gizmos: Attach gizmos to multiple robots simultaneously
Custom Gizmo Callbacks: Implement application-specific interaction logic
Gizmo with Rigid Objects: Use gizmos for interactive object manipulation
Advanced IK Configuration: Fine-tune solver parameters for specific robots
For more advanced robot control and simulation features, refer to the complete Simulating a Robot tutorial and the API documentation for objects.Gizmo and solvers.PinkSolverCfg.