simulo.OperationalSpaceController
Operational space controller for simultaneous motion and force control.
Computes joint effort/torque commands to achieve task-space motion and/or force objectives. Supports impedance control with configurable stiffness and damping, plus nullspace control for redundant manipulators.
This is useful for:
- Contact-rich manipulation (e.g., wiping a surface)
- Compliant motion control
- Force-controlled tasks
For common use cases, use the factory methods:
for_compliant_motion()- Pure motion control with compliancefor_force_control()- Force control on specified axis
Example:
import simulo
class OSCScenario(simulo.Scenario): def build(self, scene): self.robot = simulo.Robot(asset=simulo.Asset.usd("builtin://franka_panda_effort")) scene.add(self.robot, at="/World/Robot")
def on_start(self): arm_joints = self.robot.find_joints("panda_joint.*")
# Simple: use factory method self.osc = simulo.OperationalSpaceController.for_force_control( robot=self.robot, end_effector="panda_hand", joints=arm_joints, force_axis="z", )
self.target = simulo.Pose(position=[0.5, 0.3, 0.7], orientation=[1,0,0,0])
def on_step(self): # Single call - like IK's move_to() self.osc.move_to(self.target, force=10.0) # 10N in z-axisAttributes:
robot— The robot this controller is attached toend_effector— Name of the end-effector body/linkjoints— List of joint indices being controlledcontact_sensor— Optional contact sensor for force feedback
Full constructor (uncommon — see the factory methods above for the common cases):
simulo.OperationalSpaceController( robot: Robot, end_effector: str, joints: List[int], contact_sensor: Optional[Any] = None, stiffness: float = 400.0, damping_ratio: float = 1.0, force_axis: Optional[str] = None, nullspace_control: bool = True, config: Optional[OSCConfig] = None,)for_compliant_motion()
Section titled “for_compliant_motion()”OperationalSpaceController.for_compliant_motion( robot: Robot, end_effector: str, joints: List[int], stiffness: float = 400.0, damping_ratio: float = 1.0, nullspace_control: bool = True,) -> OperationalSpaceControllerclassmethod
Create OSC for compliant motion control (no force control).
Use this for tasks requiring soft, compliant end-effector motion.
Args:
robot— The robot to controlend_effector— Name of the end-effector body/linkjoints— List of joint indices to controlstiffness— Task-space stiffness in N/m (default: 400)damping_ratio— Damping ratio (1.0 = critically damped)nullspace_control— Keep joints centered when possible
Returns:
Configured OperationalSpaceController
Example:
osc = OperationalSpaceController.for_compliant_motion( … robot, “panda_hand”, arm_joints, stiffness=200.0 … ) osc.move_to(target_pose)
for_force_control()
Section titled “for_force_control()”OperationalSpaceController.for_force_control( robot: Robot, end_effector: str, joints: List[int], force_axis: str = 'z', contact_sensor: Optional[Any] = None, stiffness: float = 400.0, damping_ratio: float = 1.0, nullspace_control: bool = True,) -> OperationalSpaceControllerclassmethod
Create OSC for hybrid motion/force control.
Use this for contact tasks like pushing, wiping, or polishing. Motion is controlled on 5 axes while force is controlled on 1 axis.
Args:
robot— The robot to controlend_effector— Name of the end-effector body/linkjoints— List of joint indices to controlforce_axis— Axis for force control (“x”, “y”, or “z”)contact_sensor— Optional ContactSensor for closed-loop force controlstiffness— Task-space stiffness in N/m for motion axesdamping_ratio— Damping ratio (1.0 = critically damped)nullspace_control— Keep joints centered when possible
Returns:
Configured OperationalSpaceController
Example:
osc = OperationalSpaceController.for_force_control( … robot, “panda_hand”, arm_joints, … force_axis=“z”, contact_sensor=wall_sensor … ) osc.move_to(target_pose, force=10.0) # 10N in z
move_to()
Section titled “move_to()”OperationalSpaceController.move_to( target: Union[Pose, torch.Tensor, List[float]], force: Optional[float] = None, wrench: Optional[Union[torch.Tensor, List[float]]] = None, stiffness: Optional[Union[float, List[float]]] = None,) -> Optional[torch.Tensor]Move end-effector toward target pose, optionally applying force.
Call this every simulation step. The controller computes joint efforts and applies them to the robot automatically.
Args:
target— Target pose in robot base frame. Can be: - simulo.Pose object - Tensor of shape (7,) or (num_envs, 7) with [x, y, z, qw, qx, qy, qz] - List [x, y, z, qw, qx, qy, qz]force— Force to apply in the force-controlled axis (Newtons). Simple scalar for single-axis force control.wrench— Full 6-DOF wrench [fx, fy, fz, tx, ty, tz] for power users. Overridesforceif both provided.stiffness— Task-space stiffness. Can be: - float: Same stiffness for all axes (e.g., 400.0) - List of 6 floats: Per-axis stiffness If None, uses default stiffness from constructor.
Returns:
Joint effort tensor (num_envs, num_joints) or None if computation failed.
Example:
# Simple motion controlosc.move_to(target_pose)
# Motion with force in z-axisosc.move_to(target_pose, force=10.0)
# With custom stiffnessosc.move_to(target_pose, force=10.0, stiffness=300.0)reset()
Section titled “reset()”OperationalSpaceController.reset() -> NoneReset controller state.
Call this when resetting the robot to a new state.
ee_body_idx
Section titled “ee_body_idx”OperationalSpaceController.ee_body_idx: Optional[int]property
Get the end-effector body index.
action_dim
Section titled “action_dim”OperationalSpaceController.action_dim: intproperty
Get the action dimension (command size).