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:
|
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 where 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.