Motion Generator#
The MotionGenerator class in EmbodiChain provides a unified and extensible interface for robot trajectory planning. It supports time-optimal trajectory generation (currently via TOPPRA), joint/Cartesian interpolation, and is designed for easy integration with RL, imitation learning, and classical control scenarios.
Key Features#
Unified API: One interface for multiple planning strategies (time-optimal, interpolation, etc.)
Constraint Support: Velocity/acceleration constraints configurable per joint
Flexible Input: Supports both joint space and Cartesian space waypoints
Extensible: Easy to add new planners (RRT, PRM, etc.)
Integration Ready: Can be used in RL, imitation learning, or classical pipelines
The Code#
The tutorial corresponds to the motion_generator.py script in the scripts/tutorials/sim directory.
Code for motion_generator.py
1# ----------------------------------------------------------------------------
2# Copyright (c) 2021-2025 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
17import time
18import torch
19import numpy as np
20from copy import deepcopy
21from embodichain.lab.sim import SimulationManager, SimulationManagerCfg
22from embodichain.lab.sim.objects import Robot
23from embodichain.lab.sim.robots import CobotMagicCfg
24from embodichain.lab.sim.planners.utils import TrajectorySampleMethod
25from embodichain.lab.sim.planners.motion_generator import MotionGenerator
26
27
28def move_robot_along_trajectory(
29 robot: Robot, arm_name: str, qpos_list: list[torch.Tensor], delay: float = 0.1
30):
31 """
32 Set the robot joint positions sequentially along the given joint trajectory.
33 Args:
34 robot: Robot instance.
35 arm_name: Name of the robot arm.
36 qpos_list: List of joint positions (torch.Tensor).
37 delay: Time delay between each step (seconds).
38 """
39 for q in qpos_list:
40 robot.set_qpos(qpos=q.unsqueeze(0), joint_ids=robot.get_joint_ids(arm_name))
41 time.sleep(delay)
42
43
44def create_demo_trajectory(
45 robot: Robot, arm_name: str
46) -> tuple[list[torch.Tensor], list[np.ndarray]]:
47 """
48 Generate a three-point trajectory (start, middle, end) for demonstration.
49 Args:
50 robot: Robot instance.
51 arm_name: Name of the robot arm.
52 Returns:
53 qpos_list: List of joint positions (torch.Tensor).
54 xpos_list: List of end-effector poses (numpy arrays).
55 """
56 qpos_fk = torch.tensor(
57 [[0.0, np.pi / 4, -np.pi / 4, 0.0, np.pi / 4, 0.0]], dtype=torch.float32
58 )
59 xpos_begin = robot.compute_fk(name=arm_name, qpos=qpos_fk, to_matrix=True)
60 xpos_mid = deepcopy(xpos_begin)
61 xpos_mid[0, 2, 3] -= 0.1 # Move down by 0.1m in Z direction
62 xpos_final = deepcopy(xpos_mid)
63 xpos_final[0, 0, 3] += 0.2 # Move forward by 0.2m in X direction
64
65 qpos_begin = robot.compute_ik(pose=xpos_begin, name=arm_name)[1][0]
66 qpos_mid = robot.compute_ik(pose=xpos_mid, name=arm_name)[1][0]
67 qpos_final = robot.compute_ik(pose=xpos_final, name=arm_name)[1][0]
68 return [qpos_begin, qpos_mid, qpos_final], [
69 xpos_begin[0],
70 xpos_mid[0],
71 xpos_final[0],
72 ]
73
74
75def main():
76 np.set_printoptions(precision=5, suppress=True)
77 torch.set_printoptions(precision=5, sci_mode=False)
78
79 # Initialize simulation
80 sim = SimulationManager(SimulationManagerCfg(headless=False, sim_device="cpu"))
81 sim.set_manual_update(False)
82
83 # Robot configuration
84 cfg_dict = {"uid": "CobotMagic"}
85 robot: Robot = sim.add_robot(cfg=CobotMagicCfg.from_dict(cfg_dict))
86 arm_name = "left_arm"
87
88 # # Generate trajectory points
89 qpos_list, xpos_list = create_demo_trajectory(robot, arm_name)
90
91 # Initialize motion generator
92 motion_generator = MotionGenerator(
93 robot=robot,
94 uid=arm_name,
95 planner_type="toppra",
96 default_velocity=0.2,
97 default_acceleration=0.5,
98 )
99
100 # Joint space trajectory
101 out_qpos_list, _ = motion_generator.create_discrete_trajectory(
102 qpos_list=[q.numpy() for q in qpos_list],
103 is_linear=False,
104 sample_method=TrajectorySampleMethod.QUANTITY,
105 sample_num=20,
106 )
107 move_robot_along_trajectory(robot, arm_name, out_qpos_list)
108
109 # Cartesian space trajectory
110 out_qpos_list, _ = motion_generator.create_discrete_trajectory(
111 xpos_list=[x.numpy() for x in xpos_list],
112 is_linear=True,
113 sample_method=TrajectorySampleMethod.QUANTITY,
114 sample_num=20,
115 )
116 move_robot_along_trajectory(robot, arm_name, out_qpos_list)
117
118
119if __name__ == "__main__":
120 main()
Typical Usage#
from embodichain.lab.sim.planners.motion_generator import MotionGenerator
# Assume you have a robot instance and uid
motion_gen = MotionGenerator(
robot=robot,
uid="arm",
default_velocity=0.2,
default_acceleration=0.5
)
# Plan a joint-space trajectory
current_state = {"position": [0, 0, 0, 0, 0, 0]}
target_states = [{"position": [0.5, 0.2, 0, 0, 0, 0]}]
success, positions, velocities, accelerations, times, duration = motion_gen.plan(
current_state=current_state,
target_states=target_states
)
# Generate a discrete trajectory (joint or Cartesian)
qpos_list, xpos_list = motion_gen.create_discrete_trajectory(
qpos_list=[[0,0,0,0,0,0],[0.5,0.2,0,0,0,0]],
sample_num=20
)
API Reference#
Initialization
MotionGenerator(
robot: Robot,
uid: str,
sim=None,
planner_type="toppra",
default_velocity=0.2,
default_acceleration=0.5,
collision_margin=0.01,
**kwargs
)
robot: Robot instance, must support get_joint_ids, compute_fk, compute_ikuid: Unique robot identifier (e.g., “arm”)planner_type: Planner type (default: “toppra”)default_velocity,default_acceleration: Default joint constraints
plan
plan(
current_state: Dict,
target_states: List[Dict],
sample_method=TrajectorySampleMethod.TIME,
sample_interval=0.01,
**kwargs
) -> Tuple[bool, positions, velocities, accelerations, times, duration]
Plans a time-optimal trajectory (joint space), returns trajectory arrays and duration.
create_discrete_trajectory
create_discrete_trajectory(
xpos_list=None,
qpos_list=None,
is_use_current_qpos=True,
is_linear=False,
sample_method=TrajectorySampleMethod.QUANTITY,
sample_num=20,
qpos_seed=None,
**kwargs
) -> Tuple[List[np.ndarray], List[np.ndarray]]
Generates a discrete trajectory between waypoints (joint or Cartesian), auto-handles FK/IK.
estimate_trajectory_sample_count
estimate_trajectory_sample_count(
xpos_list=None,
qpos_list=None,
step_size=0.01,
angle_step=np.pi/90,
**kwargs
) -> int
Estimates the number of samples needed for a trajectory.
plan_with_collision
plan_with_collision(...)
(Reserved) Plan trajectory with collision checking (not yet implemented).
Notes & Best Practices#
Only collision-free planning is currently supported; collision checking is a placeholder.
Input/outputs are numpy arrays or torch tensors; ensure type consistency.
Robot instance must implement get_joint_ids, compute_fk, compute_ik, get_proprioception, etc.
For custom planners, extend the PlannerType Enum and _create_planner methods.