In order to be allowed to do this laboratory, you must be able to show a working solution to the The Ball and Beam Control exercise from Exercise 3.
Your task is to write a Java program that controls the physical ball and beam process. The Java program should consist of two parts:
The main difference between the new Regul class and the one used in Exercise 3 is that the GUIs for the inner PI controller and the outer PID controller are now implemented by OpCom rather than by internal GUI classes.
A predefined ReferenceGenerator class is used to provide the reference signal. This class has its own GUI. From the GUI you can decide to either use a squarewave signal as the reference or to manually set the reference using a JSlider.
The public interface of OpCom is the following:
// Constructor. Note: Different from Exercise 4. public OpCom(int plotterPriority); // Passes in a reference to Regul. Called from Main. public void setRegul(Regul r); // Build up the GUI. Called from Main. public void initializeGUI(); // Starts the plotting within OpCom. Called from Main. public void start(); // Insert a new DoublePoint containing the current time (x) and // control signal (y) into a PlotterPanel in OpCom. // The documentation for DoublePoint can be found here. public void putControlDataPoint(DoublePoint dp); // Inserts a new PlotData containing the current time, reference and // measured value into a PlotterPanel in OpCom. public void putMeasurementDataPoint(PlotData pd);
The control modes are represented as
public static final int OFF = 0; public static final int BEAM = 1; public static final int BALL = 2;
The PlotData class looks as follows:
public class PlotData implements Cloneable { double ref, y; double x; // holds the current time public Object clone() { try { return super.clone(); } catch (Exception e) { return null; } } }
The Regul class should contain one PI controller for the inner loop and one PID controller for the outer loop. Use a single thread for the execution of both the controllers in the same way as in the exercise. Regul receives its reference signal by calling the getRef() method of the ReferenceGenerator.
The controller should use the cascade structure according to the figure below. The code should be written so that the delay between the sampling of the measurement signals and the generation of the control signal is minimized.
Regul should run in one out of three different modes: BEAM, BALL, and OFF. In the mode OFF the control signal (u) = 0 should be sent out. The regul thread should still be executing. In each loop, the regul thread should call putControlDataPoint() and putMeasurementDataPoint() in OpCom. If the mode is OFF the signals sent to OpCom, (y, yref and u) should all have the value 0.
A major difference compared with the exercise is that you now use real analog IO classes rather than the classes that communicate with the virtual process. The following code example shows how to use these classes:
import se.lth.control.realtime.*; class IODemo { public static void main(String[] args) { AnalogIn yChan; AnalogOut uChan; double y; try { uChan = new AnalogOut(1); yChan = new AnalogIn(1); } catch (Exception e) { System.out.println(e); return; } try { uChan.set(0.0); } catch (Exception e) { System.out.println(e); } try { y = yChan.get(); } catch (Exception e) { System.out.println(e); } } }
A difference compared to Exercise 3 is that the reference signal now is plotted by the GUI rather than by the virtual simulator. Hence, the reference signal should no longer be output using an analog output. Instead, the reference signal should be sent to OpCom by calling putMeasurementDataPoint().
The Regul class should have the following public methods:
// Constructor public Regul(int priority); // Passes in a reference to OpCom. Called from Main. public void setOpCom(OpCom o); // Passes in a reference to ReferenceGenerator. Called from Main. public void setRefGen(ReferenceGenerator r); // Starts the regul thread. Called from Main. public void start(); // Set the parameters of the inner PI controller (inner controller). // The PIParameters class is the same as in Exercise 3. // Not synchronized, the synchronization is handled by PI. public void setInnerParameters(PIParameters p); // Returns the current parameters in the inner loop. // Only called during OpCom GUI initialization. // Not synchronized, the synchronization is handled by PI. public PIParameters getInnerParameters(); // Set the parameters of the outer PID controller (outer controller). // The PIDParameters class is the same as in Exercise 3. // Not synchronized, the synchronization is handled by PID. public void setOuterParameters(PIDParameters p); // Returns the current parameters in the outer loop. // Only called during OpCom GUI initialization. // Not synchronized, the synchronization is handled by PID. public PIDParameters getOuterParameters(); // Set the controller in OFF mode. // Use an internal monitor class to store the current control mode. public void setOFFMode(); // Set the controller in BEAM control mode. // Use an internal monitor class to store the current control mode. public void setBEAMMode(); // Set the controller in BALL control mode. // Use an internal monitor class to store the current control mode. public void setBALLMode(); // Returns the initial control mode. public int getMode(); // Called from OpCom when the Stop button has been pressed, // before the system is shut down. public synchronized void shutDown();
Use your PI and PID classes from Exercise 3 with the following extensions:
// PI // Sets the I-part of the controller to 0. // For example needed when changing controller mode. public synchronized void reset(); // Returns the current PIParameters. public synchronized PIParameters getParameters(); // PID // Sets the I-part and D-part of the controller to 0. // For example needed when changing controller mode. public synchronized void reset(); // Returns the current PIDParameters. public synchronized PIDParameters getParameters();
The following Main class should be used.
import javax.swing.*; public class Main { public static void main(String[] argv) { final int regulPriority = 8; final int refGenPriority = 6; final int plotterPriority = 7; ReferenceGenerator refgen = new ReferenceGenerator(refGenPriority); Regul regul = new Regul(regulPriority); final OpCom opcom = new OpCom(plotterPriority); regul.setOpCom(opcom); regul.setRefGen(refgen); opcom.setRegul(regul); Runnable initializeGUI = new Runnable(){ public void run(){ opcom.initializeGUI(); opcom.start(); } }; try{ SwingUtilities.invokeAndWait(initializeGUI); }catch(Exception e){ return; } refgen.start(); regul.start(); } }
The design can be summarized in the figure below. The notation introduced in the Buttons exercise is used.
NOTE: In the diagram, the PI and PID classes are drawn as if they are inner classes of Regul but it is probably easier to keep them as ordinary, "top-level", classes.
The output signals from the process (angle and position) are voltages in the interval [-10, 10]. The angle measurement should be connected to AI 0 on and the position signal to AI 1.
The input signal to the process (the control signal) is a voltage in the interval [-10, 10]. The control signal should be connected to AO 0.
Since we only have four physical processes you will have to share these among the groups.
To be able to build you need the following classes:
Test your program against the real ball and beam process.
In the next part you will extend the previous control-structure with
a time-optimal feedforward generator. You have already been provided
all the necessary classes for this, and will only have to do some
minor changes in order to make it work. Before moving on, you should
recall the lecture slides
from
Lecture 10.
Below you can see a block diagram for the new control
structure. To change your current control-structure into this, you
will need to call two new methods from the reference generator in
order to get the feedforward
terms: referenceGenerator.getPhiff()
and referenceGenerator.getUff().
You should also recall from the lecture that when using the
feedforward generator you will need to use gamma=1 when computing
the D-term in the PID-controller. Therefore, in PID.java
you will have to change D = ad*D - bd*(y - yOld) to D
= ad*D + bd*(e - eOld) (NOTE the plus-sign!)