Package Architecture¶
curobo_ros is organized around one node, unified_planner, that composes three independent strategy families:
Planners — how a motion is computed: open-loop trajectory planning or closed-loop reactive control.
Robot control strategies — where joint commands go: an emulator, a velocity-streamed real robot, or a position-streamed real robot.
Camera strategies — what the collision world perceives: depth cameras feeding a GPU voxel map.
Each family can be switched independently, at runtime, without touching the others. Everything shares a single GPU context (robot model, scene, collision caches) so that all solvers see the same world.
System overview¶
graph TB
subgraph Clients
RVIZ[RViz panels<br/>curobo_rviz]
CLI[ros2 CLI / user nodes]
end
subgraph UnifiedPlannerNode["unified_planner node"]
SRV[Services & Action<br/>generate_trajectory, execute_trajectory, ...]
PM[PlannerManager<br/>+ PlannerFactory]
CW[ConfigWrapperMotion<br/>shared context: robot, Scene, caches]
RC[RobotContext<br/>control strategies]
CAM[CameraSystemManager<br/>depth cameras -> Mapper]
end
subgraph GPU["cuRobo v2 (GPU)"]
MP[MotionPlanner]
MPC[ModelPredictiveControl]
IK[InverseKinematics]
MAP[Mapper / ESDF]
end
ROBOT[Robot driver or emulator]
RVIZ --> SRV
CLI --> SRV
SRV --> PM
PM --> CW
CW --> MP
CW --> MPC
CW --> IK
CAM --> MAP
MAP --> CW
PM --> RC
RC --> ROBOT
Planner hierarchy¶
All planners implement the abstract TrajectoryPlanner interface (curobo_ros/planners/trajectory_planner.py) and declare an ExecutionMode: OPEN_LOOP (plan once, then stream) or CLOSED_LOOP (re-plan continuously while executing).
classDiagram
class TrajectoryPlanner {
<<abstract>>
+plan(request) PlannerResult
+execute(goal_handle)
+get_execution_mode() ExecutionMode
}
class SinglePlanner {
<<open-loop base>>
shared MotionPlanner instance
}
class ReactiveController {
<<closed-loop base>>
servo loop, live goal, perception refresh
+build_solver()
+setup(state, goal)
+step(state)
+apply_live_goal(pose)
+is_converged()
}
TrajectoryPlanner <|-- SinglePlanner
TrajectoryPlanner <|-- ReactiveController
SinglePlanner <|-- ClassicPlanner
SinglePlanner <|-- MultiPointPlanner
SinglePlanner <|-- JointSpacePlanner
ReactiveController <|-- MPCController
ReactiveController <|-- RetargetController
Open-loop (
SinglePlannerchildren) share one class-level cuRoboMotionPlanner:ClassicPlanner(single Cartesian goal),MultiPointPlanner(waypoint sequence),JointSpacePlanner(joint-space goal).Closed-loop (
ReactiveControllerchildren) each wrap a cuRobo reactive solver and inherit the whole servo loop from the base class:MPCController(Model Predictive Control) andRetargetController(IK-based teleoperation follower).
PlannerFactory (curobo_ros/planners/planner_factory.py) holds the catalog mapping SetPlanner enum IDs to classes; PlannerManager caches instances and performs runtime switching. Adding a planner = subclass one of the two bases + one catalog entry. See Unified Planner.
Robot control strategy hierarchy¶
The robot side has two orthogonal axes (see Tutorial 4):
Which robot — the
robotparameter selects a descriptorrobots/<name>.yaml(URDF, cuRobo config, topics).How to command it — the
control_strategyparameter selects aJointCommandStrategyimplementation.
classDiagram
class RobotContext {
strategy switching, lock order, buffer epochs
+set_command(trajectory)
+get_robot_state()
}
class JointCommandStrategy {
<<abstract>>
+send_trajectory()
+get_progression()
+stop_robot()
}
RobotContext o-- JointCommandStrategy
JointCommandStrategy <|-- EmulatorStrategy
JointCommandStrategy <|-- JointSpeedStrategy
JointCommandStrategy <|-- JointPoseStrategy
class GhostStrategy {
always-on RViz preview
publishes /trajectory
}
EmulatorStrategy— simulates the robot, publishes/emulator/joint_states.JointSpeedStrategy— streamsJointTrajectorywith velocities to a real robot bridge (used for the Doosan M1013 via theleelootopics), with a hard acceleration clamp and real velocity feedback.JointPoseStrategy— streams positions only.GhostStrategy— not switchable; always publishes the planned trajectory on/trajectoryfor the RViz preview robot (namespacepreview/).
RobotContext (curobo_ros/robot/robot_context.py) enforces a strict lock order (gpu_lock > strategy_lock > buffer_lock) and uses buffer epochs to refuse superseded trajectories.
Configuration and managers¶
ConfigWrapperMotion composes five managers that own startup and the shared state; the deep dive is in Manager Architecture:
ConfigManager— robot descriptor, cuRobo YAML resolution, world file.RobotModelManager— cuRoboKinematics, collision spheres, per-link collision toggling.ObstacleManager— the singleScene, obstacle add/remove, collision caches, voxel grid,Mapperperception.CameraSystemManager— camera strategies fromcameras.yaml.RosServiceManager— obstacle/introspection services and visualization publishers.
Key invariants¶
One GPU context. Every solver (planning, MPC, IK) is built from the same shared context — same robot model, same scene, same caches. There is no separate world for MPC.
One live CUDA graph. With
use_cuda_graph: true, the node guarantees at most one captured CUDA graph across solvers; switching planners releases and re-captures as needed.Push-based perception. Depth frames are integrated into the
Mapperas they arrive (under a non-blocking GPU lock); solvers read the resulting ESDF. See Tutorial 7.
Source layout¶
curobo_ros/
├── core/ # node, managers, IK/FK/attachment services, segmentation
├── planners/ # TrajectoryPlanner hierarchy + factory/manager
├── robot/ # RobotContext, control strategies, robot descriptors
├── cameras/ # camera strategies (depth_camera)
├── launch/ # gen_traj.launch.py and helpers
├── robots/ # robot descriptors (doosan_m1013.yaml, emulator.yaml)
└── config/ # cameras.yaml, floor_world.yml, test specs