Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/source/features/interaction/window.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,52 @@ Recording hotkey registration is controlled by `SimConfig.window_record.enable_h

The camera-pose hotkey is controlled by `SimulationManagerCfg.window_camera_pose.enable_hotkey` and prints look-at form by default. Set `SimulationManagerCfg.window_camera_pose.convert_to_look_at=False` to print the raw 4x4 pose matrix instead. The same output can be requested programmatically with `SimulationManager.print_window_camera_pose()`.

### Entity Gizmo Control

Opening a non-headless `SimulationManager` window enables dexsim's world-owned
`EntityGizmoManipulator` by default:

```python
import dexsim

gizmo_config = dexsim.interaction.EntityGizmoConfig()
gizmo_config.max_gizmos = 0 # Unlimited simultaneous bindings.
sim.open_window(entity_gizmo_config=gizmo_config)
```

While enabled, left-click a render mesh, dynamic/kinematic rigid body, or
articulation link and press **G** to attach or detach its root gizmo. The
controller supports multiple simultaneous bindings and owns selection,
temporary physics-state changes, and cleanup. No `sim.update_gizmos()` call is
needed for this world-level controller.

EmbodiChain's built-in `default_plane` is registered as an immovable target and
cannot receive an entity gizmo. Other supported scene entities remain
selectable normally.

For a view-only window, opt out explicitly:

```python
sim.open_window(enable_entity_gizmo=False)
```

Set `SimulationManagerCfg.enable_entity_gizmo_on_window_open=False` to change
the default for constructor-opened and subsequently opened windows. Headless
simulations do not create or enable the controller.

`sim.enable_entity_gizmo(config)` can reconfigure or reactivate the controller
at any time, and `sim.disable_entity_gizmo()` cancels it without closing the
window. The last explicit configuration is restored if the window is closed
and reopened.

Use `sim.get_entity_gizmo()` to access the native controller and
`sim.has_entity_gizmo()` to query its lifecycle state. Closing the window or
destroying the `SimulationManager` disables it automatically.

This controller is distinct from the target-specific Robot TCP IK gizmo. When
both are active, **G** controls entity roots and **I** shows or hides the Robot
TCP IK gizmo.

## Customizing Window Events

Users can create their own custom window interaction controls by subclassing the `ObjectManipulator` class (provided by `dexsim`). This allows for the implementation of specific behaviors and responses to user inputs.
Expand Down
104 changes: 82 additions & 22 deletions docs/source/tutorial/gizmo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Interactive Robot Control with Gizmo

.. currentmodule:: embodichain.lab.sim

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.
This tutorial demonstrates how to use the Gizmo class for interactive robot manipulation in SimulationManager. Robot gizmos delegate interactive inverse kinematics (IK) to dexsim's Newton IK controller while all joint-state reads and drive-target writes continue to pass through the EmbodiChain ``Robot`` abstraction.

The Code
~~~~~~~~
Expand All @@ -28,7 +28,8 @@ Similar to the previous tutorial on robot simulation, we use the :class:`Simulat



**Important:** Gizmo only supports single environment mode (`num_envs=1`). Using multiple environments will raise an exception.
**Important:** The target-specific Robot TCP, rigid-object, and Camera
``Gizmo`` wrapper supports only single-environment mode (``num_envs=1``).

All gizmo creation, visibility, and destruction operations must be managed via the SimulationManager API:

Expand Down Expand Up @@ -57,21 +58,34 @@ The :class:`objects.Gizmo` class provides a unified interface for interactive co
Setting up Robot Configuration
------------------------------

First, we configure a UR10 robot with an IK solver for end-effector control:
First, configure a UR10 robot and its controllable arm joints:

.. literalinclude:: ../../../scripts/tutorials/sim/gizmo_robot.py
:language: python
:start-at: # Create UR10 robot configuration
:start-at: # Create UR10 robot
:end-at: robot = sim.add_robot(cfg=robot_cfg)

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**: :class:`solvers.PinkSolverCfg` provides inverse kinematics capabilities
- **Drive 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.
An EmbodiChain kinematics solver is not required by the gizmo. The IK chain is declared when enabling it:

.. code-block:: python

gizmo_cfg = GizmoCfg(
ik_root_link_name="base_link",
ik_end_link_name="ee_link",
)
sim.enable_gizmo(
uid="ur10_gizmo_test",
control_part="arm",
gizmo_cfg=gizmo_cfg,
)

For existing robot configurations, these link names and the TCP can instead be inherited from the selected control part's configured EmbodiChain solver. The solver supplies metadata only; interactive IK is still performed by dexsim's ``NewtonChainIK``.

Creating and Attaching a Gizmo
-------------------------------
Expand All @@ -83,7 +97,14 @@ After configuring the robot, enable the gizmo for interactive control using the
.. code-block:: python

# Enable gizmo for the robot's arm
sim.enable_gizmo(uid="ur10_gizmo_test", control_part="arm")
sim.enable_gizmo(
uid="ur10_gizmo_test",
control_part="arm",
gizmo_cfg=GizmoCfg(
ik_root_link_name="base_link",
ik_end_link_name="ee_link",
),
)
if not sim.has_gizmo("ur10_gizmo_test", control_part="arm"):
logger.log_error("Failed to enable gizmo!")
return
Expand All @@ -102,22 +123,23 @@ The Gizmo system will automatically:

1. **Detect Target Type**: Identify that the target is a robot (vs. rigid object or camera)
2. **Find End-Effector**: Locate the robot's end-effector link (``ee_link`` for UR10)
3. **Create Proxy Object**: Generate a small invisible cube at the end-effector position
4. **Set Up IK Callback**: Configure the gizmo to trigger IK solving when moved
3. **Build Newton IK Chain**: Build a reduced start-link-to-end-link model from the robot URDF
4. **Bind dexsim Controller**: Attach ``IKGizmoController`` directly to the articulation adapter

How Gizmo-Robot Interaction Works
----------------------------------



The gizmo-robot interaction follows this efficient workflow:
The gizmo-robot interaction follows this workflow:

1. **Gizmo Callback**: When the user drags the gizmo, a callback function updates the proxy object's transform
2. **Deferred IK Solving**: Instead of solving IK immediately in the callback (which causes UI lag), the target transform is stored
3. **Update Loop**: During each simulation step, ``gizmo.update()`` solves IK and applies joint commands
4. **Robot Motion**: The robot smoothly moves to follow the gizmo position
1. **Target Update**: Dragging the dexsim target gizmo updates the Newton IK target state
2. **Deferred Solve**: ``sim.update_gizmos()`` asks ``IKGizmoController`` to solve only when the target changed
3. **State Bridge**: The adapter reads the selected EmbodiChain control-part joints as the solve seed
4. **Drive Target**: The solved positions are written through ``Robot.set_qpos(..., target=True)`` so CPU and CUDA state paths stay synchronized
5. **Robot Motion**: Joint drives move the robot toward the target without teleporting its current state

This design separates UI responsiveness from computational IK solving, ensuring smooth interaction even with complex robots.
Robot gizmos no longer create or maintain an EmbodiChain proxy cube. Camera gizmos continue to use their existing proxy path, and rigid-object gizmos continue to follow the selected object directly.

The Simulation Loop
-------------------
Expand Down Expand Up @@ -163,19 +185,57 @@ Gizmo Lifecycle Management

Gizmo lifecycle is managed by SimulationManager:

- Enable: `sim.enable_gizmo(...)`
- Enable a target-specific gizmo: `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.

World-Level Entity Gizmo
------------------------

For selection-based root manipulation, opening a non-headless window enables
dexsim's world-level entity gizmo by default:

.. code-block:: python

import dexsim

config = dexsim.interaction.EntityGizmoConfig()
config.max_gizmos = 0
sim.open_window(entity_gizmo_config=config)

# Left-click an entity and press G to attach or detach a gizmo.
# Multiple entities may remain attached.

sim.disable_entity_gizmo()

This path supports render meshes, eligible rigid bodies, and articulation
roots. dexsim owns raycast selection, temporary body-state changes, multiple
bindings, and cleanup. It requires no ``sim.update_gizmos()`` call.

EmbodiChain's built-in ``default_plane`` is excluded from manipulation.
Selecting it and pressing **G** does not create a gizmo.

Use ``sim.open_window(enable_entity_gizmo=False)`` for a view-only window, or
set ``SimulationManagerCfg.enable_entity_gizmo_on_window_open=False`` to
change the default. Headless simulations do not create the controller.

``sim.get_entity_gizmo()`` returns the native
``EntityGizmoManipulator`` and ``sim.has_entity_gizmo()`` reports whether it is
enabled. Closing the window or destroying the simulation also disables it.

The Robot end-effector controller remains target-specific because it solves a
TCP pose rather than editing the articulation root. By default, **G** controls
the entity gizmo and **I** toggles Robot TCP IK gizmo visibility.

Available Gizmo Methods
-----------------------




If you need to access the underlying Gizmo instance (via `sim.get_gizmo`), you can use the following methods:
If you need to access the underlying Gizmo instance (via `sim.get_gizmo`), you can use the following methods. For robot targets these methods operate on dexsim's IK target gizmo:

**Transform Control:**

Expand Down Expand Up @@ -245,16 +305,16 @@ Tips and Best Practices

**Robot compatibility:**

- Ensure your robot is configured with a correct IK solver
- Check the end-effector (EE) link name
- Set valid ``ik_root_link_name`` and ``ik_end_link_name`` values, or configure an EmbodiChain solver whose chain metadata can be inherited
- Set ``ik_tcp_pose`` when the desired tool center point differs from the end-link frame
- 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
- Adjust robot target size with ``GizmoCfg.ik_gizmo_scale``
- Enable collision for debugging if needed

Next Steps
Expand All @@ -265,6 +325,6 @@ 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
- **Advanced IK Configuration**: Tune ``GizmoCfg.ik_iterations``, ``ik_device``, and the TCP pose

For more advanced robot control and simulation features, refer to the complete :doc:`robot` tutorial and the API documentation for :class:`objects.Gizmo` and :class:`solvers.PinkSolverCfg`.
For more advanced robot control and simulation features, refer to the complete :doc:`robot` tutorial and the API documentation for :class:`objects.Gizmo`.
4 changes: 2 additions & 2 deletions embodichain/lab/sim/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,8 +1735,8 @@ class RobotCfg(ArticulationCfg):
If no control part is specified, the robot will use all joints as a single control part.

Note:
- if `control_parts` is specified, `solver_cfg` must be a dict with part names as
keys corresponding to the control parts name.
- `control_parts` can be used without `solver_cfg`. If `solver_cfg` is a
dictionary, its keys must correspond to control-part names.
- The joint names in the control parts support regular expressions, e.g., 'joint[1-6]'.
After initialization of robot, the names will be expanded to a list of full joint names.
- `Robot` is a derived class of `Articulation`, with control parts support. So the `drive_pros`
Expand Down
4 changes: 3 additions & 1 deletion embodichain/lab/sim/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# limitations under the License.
# ----------------------------------------------------------------------------

from __future__ import annotations

from ..common import BatchEntity
from .rigid_object import RigidObject, RigidBodyData, RigidObjectCfg
from .rigid_object_group import (
Expand All @@ -26,7 +28,7 @@
from .articulation import Articulation, ArticulationData, ArticulationCfg
from .robot import Robot, RobotCfg
from .light import Light, LightCfg
from .gizmo import Gizmo
from .gizmo import Gizmo, GizmoCfg
from .constraint import RigidConstraint


Expand Down
Loading
Loading