Defining a Controller

Here, we explain how to write a controller and simulate its effect on a dynamical system, i.e., a Mechanism. We focus on the stabilization of the cartpole, which has two joints but only a single input on the cart. The controller is a method that always takes 2 input arguments:

  • a Mechanism,
  • an integer k indicating the current simulation step.

For the cartpole, the controller computes the control input based on the current state x, the goal state x_goal and an LQR controller. The simulation step is not used in this example.

There are three ways to apply inputs to the system

  • set an input directly to a joint
  • set a set of inputs to all joints of the mechanism
  • set an external force on bodies
# ### Setup
using Dojo
using DojoEnvironments

# ### Mechanism
mechanism = get_mechanism(:cartpole)

# ### Controller
K = [-0.948838; -2.54837; 48.6627; 10.871]

function controller!(mechanism, k)
    ## Target state
    x_goal = [0;0; 0.0;0]

    ## Current state
    x = get_minimal_state(mechanism)

    ## Control inputs
    u = -K' * (x - x_goal)

    ## 3 ways to set input:

    ## 1: get joint and set input
    cart_joint = get_joint(mechanism, :cart_joint)
    set_input!(cart_joint, [u])

    ## 2: set input for all joints at once
    ## set_input!(mechanism, [u;0]) # need to know joint order

    ## 3: direct external force on body
    ## cart = get_body(mechanism, :cart)
    ## set_external_force!(cart; force=[0;u;0])

end

# ### Simulate 
initialize!(mechanism, :cartpole; position=0, orientation=pi/4)
            
storage = simulate!(mechanism, 10.0, controller!, record=true)

    
# ### Visualize
vis = visualize(mechanism, storage)
render(vis)