-
Notifications
You must be signed in to change notification settings - Fork 8
Add gizmo helper tool for robot control #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,8 +21,12 @@ | |||||||||||
| """ | ||||||||||||
|
|
||||||||||||
| from typing import Callable | ||||||||||||
| from typing import TYPE_CHECKING | ||||||||||||
| from dexsim.types import TransformMask | ||||||||||||
|
|
||||||||||||
| if TYPE_CHECKING: | ||||||||||||
| from embodichain.lab.sim.objects import Robot | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def create_gizmo_callback() -> Callable: | ||||||||||||
| """Create a standard gizmo transform callback function. | ||||||||||||
|
|
@@ -44,3 +48,176 @@ def gizmo_transform_callback(node, translation, rotation, flag): | |||||||||||
| node.set_rotation_rpy(rotation) | ||||||||||||
|
|
||||||||||||
| return gizmo_transform_callback | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def run_gizmo_robot_control_loop( | ||||||||||||
| robot: "Robot", control_part: str = "arm", end_link_name: str | None = None | ||||||||||||
| ): | ||||||||||||
| """Run a control loop for testing gizmo controls on a robot. | ||||||||||||
|
|
||||||||||||
| This function implements a control loop that allows users to manipulate a robot | ||||||||||||
| using gizmo controls with keyboard input for additional commands. | ||||||||||||
|
|
||||||||||||
| Args: | ||||||||||||
| robot (Robot): The robot to control with the gizmo. | ||||||||||||
| control_part (str, optional): The part of the robot to control. Defaults to "arm". | ||||||||||||
| end_link_name (str | None, optional): The name of the end link for FK calculations. Defaults to None. | ||||||||||||
|
|
||||||||||||
| Keyboard Controls: | ||||||||||||
| Q/ESC: Exit the control loop | ||||||||||||
| P: Print current robot state (joint positions, end-effector pose) | ||||||||||||
| G: Toggle gizmo visibility | ||||||||||||
| R: Reset robot to initial pose | ||||||||||||
| I: Print control information | ||||||||||||
| """ | ||||||||||||
| import select | ||||||||||||
| import sys | ||||||||||||
| import tty | ||||||||||||
| import termios | ||||||||||||
| import time | ||||||||||||
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| import numpy as np | ||||||||||||
|
|
||||||||||||
| np.set_printoptions(precision=5, suppress=True) | ||||||||||||
|
|
||||||||||||
| from embodichain.lab.sim import SimulationManager | ||||||||||||
| from embodichain.lab.sim.objects import Robot | ||||||||||||
| from embodichain.lab.sim.solvers import PinkSolverCfg | ||||||||||||
|
|
||||||||||||
| from embodichain.utils.logger import log_info, log_warning, log_error | ||||||||||||
|
|
||||||||||||
| sim = SimulationManager.get_instance() | ||||||||||||
|
|
||||||||||||
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| # Enter auto-update mode. | ||||||||||||
| sim.set_manual_update(False) | ||||||||||||
|
|
||||||||||||
|
Comment on lines
+90
to
+92
|
||||||||||||
| # Replace robot's default solver with PinkSolver for gizmo control. | ||||||||||||
| robot_solver = robot.get_solver(name=control_part) | ||||||||||||
| control_part_link_names = robot.get_control_part_link_names(name=control_part) | ||||||||||||
| end_link_name = ( | ||||||||||||
| control_part_link_names[-1] if end_link_name is None else end_link_name | ||||||||||||
| ) | ||||||||||||
| pink_solver_cfg = PinkSolverCfg( | ||||||||||||
| urdf_path=robot.cfg.fpath, | ||||||||||||
| end_link_name=end_link_name, | ||||||||||||
| root_link_name=robot_solver.root_link_name, | ||||||||||||
| pos_eps=1e-2, | ||||||||||||
|
Comment on lines
+94
to
+103
|
||||||||||||
| rot_eps=5e-2, | ||||||||||||
| max_iterations=300, | ||||||||||||
| dt=0.1, | ||||||||||||
| ) | ||||||||||||
| robot.init_solver(cfg={control_part: pink_solver_cfg}) | ||||||||||||
|
|
||||||||||||
| # Enable gizmo for the robot | ||||||||||||
| gizmo = sim.enable_gizmo(uid=robot.uid, control_part=control_part) | ||||||||||||
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| gizmo = sim.enable_gizmo(uid=robot.uid, control_part=control_part) | |
| sim.enable_gizmo(uid=robot.uid, control_part=control_part) |
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several imports/variables in this helper are unused (Robot imported inside the function, log_warning, log_error, and the gizmo local). Please remove unused imports/assignments to keep this utility lean and avoid implying side effects that don’t exist.
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
termios.tcgetattr expects a file descriptor (int) but this passes sys.stdin (a file object). This will raise TypeError on most platforms. Use sys.stdin.fileno() (and consider handling non-TTY stdin cases) when saving terminal settings.
| old_settings = termios.tcgetattr(sys.stdin) | |
| tty.setcbreak(sys.stdin.fileno()) | |
| stdin_fd = sys.stdin.fileno() | |
| old_settings = termios.tcgetattr(stdin_fd) | |
| tty.setcbreak(stdin_fd) |
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eef_pose is logged via eef_pose.squeeze().numpy() before checking whether compute_fk returned None, and .numpy() is not valid on a torch tensor without moving to CPU/detaching. This will throw when eef_pose is None or a CUDA tensor. Only log the pose after the None check, and convert with eef_pose.detach().cpu().numpy() (or similar).
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
yuecideng marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| except: | |
| # Intentionally ignore errors while restoring terminal settings during shutdown |
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except block directly handles BaseException.
| except: | |
| except Exception: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type was changed to
Gizmo, but this method still has code paths that return without a value (e.g., when the gizmo already exists or the UID isn't found). To keep the API consistent, either return the existing gizmo / raise, or change the annotation toGizmo | Noneand returnNoneexplicitly on those paths.