Up Next

2.1  The Inverted Pendulum

Consider the problem of balancing an inverted pendulum with the hand (through a mouse). The inverted pendulum has a length l, its bottom has coordinates x0 and y0 which are continuously controlled by the user and it forms an angle θ with the vertical. This pendulum is submitted to the following law:

  l × 
  =   (sin(θ) × (
 + g)) −  (cos(θ) × 
x = x0 + l × sin(θ)
y = y0 + l × cos(θ)

We suppose that some auxiliary scalar functions have been defined in a Objective Caml module Draw with implementation draw.ml and interface draw.mli. A pendulum is characterized by its bottom and top coordinates. The exported values are defined below:

  (* file draw.mli *)
  type pendulum
  val make_pend : float -> float -> float -> float -> pendulum
  val clear_pend : pendulum -> unit
  val draw_pend : pendulum -> unit
  val mouse_pos : unit -> float * float

We start by defining a synchronous module for integrating and deriving a signal.

  (* file misc.ls *)
  (* rectangle integration *)
  let node integr t dx = let rec x = 0.0 -> t *. dx +. pre x in x

  (* derivative *)
  let node deriv t x = 0.0 -> (x -.(pre x))/. t

Now, the main module defines global constants and the pendulum law.

  (* file pendulum.ls *)
  open Draw
  open Misc

  let static t = 0.05 (* sampling frequency *)
  let static l = 10.0 (* length of the pendulum *)
  let static g = 9.81 (* acceleration *)

  let node integr dx = Misc.integr t dx
  let node deriv x = Misc.deriv t x

  (* the equation of the pendulum *)
  let node equation d2x0 d2y0 = theta
    rec theta =
        let thetap = 0.0 fby theta
        in integr (integr ((sin thetap) *. (d2y0 +. g)
                          -.(cos thetap) *. d2x0) /. l)

  let node position x0 y0 = 
    let d2x0 = deriv (deriv x0)  in
    let d2y0 = deriv (deriv y0)  in

    let theta = equation d2x0 d2y0 in

    let x = x0 +. l *. (sin theta)  in
    let y = y0 +. l *. (cos theta)  in
    make_pend x0 y0 x y

As in Objective Caml, an open Module directive makes the names exported by the module Module visible in the current module 2. Imported values may be either used with the dot notation (e.g, Draw.mouse_pos) or directly (e.g, make_pend) once the module is opened.

Finally the main function continuously reads informations from the mouse, computes the position of pendulum, clear its previous position and draw its current position. We get:

  let node play () =
    let x0,y0 = mouse_pos () in
    let p = position x0 y0 in
    clear_pendulum (p fby p);
    draw_pendulum p;;

Now, all the files must be compiled. The compiler of Lucid Synchrone acts as a preprocessor: the compilation of the implementation misc.ls produces a file misc.ml and a compiled interface misc.lci containing informations about types and clocks of the implementation. Similarly, the compilation of the scalar interface draw.mli produces a compiled interface draw.lci. Files are compiled by typing:

  % lucyc draw.mli          => draw.lci
  % lucyc misc.ls           => misc.ml, misc.lci
  % lucyc pendulum.ls       => pendulum.ml
  % lucyc -s play -sampling 0.05 pendulum.lci
  % ocamlc draw.mli
  % ocamlc draw.ml
  % ocamlc pendulum.ml
  % ocamlc play.ml
  % ocamlc -o play draw.cmo pendulum.cmo play.cmo ...

The whole system is obtained by linking all the modules (synchronous and scalars ones) and by sampling the main transition function play on a timer (here, 0.05 seconds) giving the base clock (the real-time clock) of the system.

The implemented module system of Lucid Synchrone is borrowed from Caml Light, giving the minimal tools for importing names from Objective Caml files or from Lucid Synchrone files.

Up Next