5. Adding A Controller

5.1. Learning Objectives

This tutorial shows how to create and use a custom controller to move a mobile robot. It then shows how to use the controllers available in Omniverse Isaac Sim. After this tutorial, you should be more confident adding and controlling robots in Omniverse Isaac Sim.

10 Minute Tutorial

5.2. Getting Started

Prerequisites

  • Please review Hello Robot prior to beginning this tutorial.

Begin with the source code open from the previous tutorial, Hello Robot.

5.3. Creating a Custom Controller

Let’s code an openloop controller that uses the unicycle model for differential drive. Controllers in Omniverse Isaac Sim inherits from the BaseController interface. A forward method needs to be implemented and it has to return an ArticulationAction type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.jetbot import Jetbot
from omni.isaac.core.utils.types import ArticulationAction
from omni.isaac.core.controllers import BaseController
import numpy as np

class CoolController(BaseController):
    def __init__(self):
        super().__init__(name="my_cool_controller")
        # An open loop controller that uses a unicycle model
        self._wheel_radius = 3
        self._wheel_base = 11.25
        return

    def forward(self, command):
        # command will have two elements, first element is the forward velocity
        # second element is the angular velocity (yaw only).
        joint_velocities = [0.0, 0.0]
        joint_velocities[0] = ((2 * command[0]) - (command[1] * self._wheel_base)) / (2 * self._wheel_radius)
        joint_velocities[1] = ((2 * command[0]) + (command[1] * self._wheel_base)) / (2 * self._wheel_radius)
        # A controller has to return an ArticulationAction
        return ArticulationAction(joint_velocities=joint_velocities)

class HelloWorld(BaseSample):
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.scene.add_default_ground_plane()
        jetbot_robot = world.scene.add(Jetbot(prim_path="/World/Fancy_Robot", name="fancy_robot"))
        return

    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        # Initialize our controller after load and the first reset
        self._my_controller = CoolController()
        return

    def send_robot_actions(self, step_size):
        #apply the actions calculated by the controller
        self._jetbot.apply_wheel_actions(self._my_controller.forward(command=[20.0, np.pi/ 4]))
        return
../_images/isaac_sim_control_robot_1.gif

5.4. Using the Available Controllers

Omniverse Isaac Sim also provides different controllers under the many robot extensions. Let’s re-write the previous code using the DifferentialController class and add a WheelBasePoseController.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.jetbot import Jetbot
# This extension includes several generic controllers that could be used with multiple robots
from omni.isaac.motion_generation import WheelBasePoseController
# Robot specific controller
from omni.isaac.jetbot.controllers import DifferentialController
import numpy as np


class HelloWorld(BaseSample):
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.scene.add_default_ground_plane()
        jetbot_robot = world.scene.add(Jetbot(prim_path="/World/Fancy_Robot", name="fancy_robot"))
        return

    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        # Initialize our controller after load and the first reset
        self._my_controller = WheelBasePoseController(name="cool_controller",
                                                    open_loop_wheel_controller=DifferentialController(name="open_loop_controller"),
                                                    is_holonomic=False)
        return

    def send_robot_actions(self, step_size):
        position, orientation = self._jetbot.get_world_pose()
        self._jetbot.apply_wheel_actions(self._my_controller.forward(start_position=position,
                                                                     start_orientation=orientation,
                                                                     goal_position=np.array([80, 80])))
        return

Press Ctrl+S to save and hot reload the example. Then press the LOAD button to reload the scene.

../_images/isaac_sim_control_robot_2.gif

5.5. Summary

This tutorial covered the following topics:

  1. Creating a custom controller to move a mobile robot

  2. Using Controller Classes from Omniverse Isaac Sim

5.5.1. Next Steps

Continue on to the next tutorial in our Essential Tutorials series, Adding a Manipulator Robot, to learn how to add a manipulator robot to the simulation.