Funzione PID con pLtouch 101g utilizzando Android

Il progetto d’esempio che vi vogliamo presentare in questo post riguarda un esempio di controllo Pid con Android.

In particolare verrà spiegato come utilizzare la funzione PID per un controllo di temperatura utilizzazando il pLtouch 101g  .

Di default ci sono due funzioni pid utilizzabili:
La prima funzione PID ha l’ingresso analogico 1 associato all’uscita analogica 1
La seconda funzione PID ha l’ingresso analogico 2 associato all’uscita analogica 2

In questo caso la modalità PID che andremo ad utilizzare è per il riscaldamento e utilizzeremo la funzione PID1; perciò al diminuire della temperatura rilevata aumenterà l’apertura del servocomando in base ad un set point impostabile da touch screen.

Per far questo avremo bisogno di :

- pLtouch  101g
- sensore di temperatura mod. pt100
- convertitore pt100 to 0..20 mA
- servocomando modulante 0..10 V
- alimentatore 24 VDC

Passiamo all’esempio pratico:


Per acquisire la temperatura utilizziamo una normalissima sonda mod. PT100.

Poichè il pLtouch 101g ha 4 ingressi in corrente, utilizziamo un convertitore capace appunto di convertire la temperatura in un segnale di corrente con range 0 .. 20 mA. L’uscita del convertitore la colleghiamo al primo ingresso analogico del pltouch 101g nel rispettivo morsetto VA0 ( maggiori dettagli ).
La funzione PID comanderà in uscita, in questo caso, un servocomando modulante con ingresso 0..10 V perciò colleghiamo la prima uscita in tensione del pLtouch 101g all’attuatore come da schema. Nel caso aveste attuatori o servocomandi con comando in corrente collegate come da descrizione nella nostra pagina wiki.
Alimentiamo il pLtocuch 101g a 24Vdc tramite i morsetti estraibili nel retro del case.

Fatto questo apriamo l’IDE di eclipse.
Supponiamo abbiate già creato un progetto Android, se così non fosse prego di leggere il blog, importato e inizializzata la libreria Pltouch (leggete il nostro articolo in merito). Ciò serve a far implementare alla vostra Activity l’interfaccia PltouchListener. Grazie a questo potete mettere il vostro codice all’interno della callback onLogicExecution, come per esempio:

public class MainActivity extends Activity implements PlTouchListener, OnClickListener {

	private PltouchManager pManager;
	private TextView analog1,analog1_mA;
	private TextView analog2,analog2_mA;
        private Button btnonPID1;
	public EditText SetPid1,SetOut;
	private int index;
        public double z = 0;
	public double m =0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		pManager = new PltouchManager(this);
		pManager.setDebug(true);
		pManager.initialize();
		pManager.addListener(this);

         };

Il codice inserito all’interno della callback onLogicExecution sarà eseguito in maniera sequenziale, un numero infinito di volte indipendentemente da tutto il resto. Una cosa importante da sapere è che tutte le chiamate inserite all’interno del metodo saranno eseguite in un thread diverso da quello della UI: Ciò implica che se volete aggiornare un qualche widget grafico dall’interno della onLogicExecution, dovete utilizzare una chiamata a runOnUiThread, come nell’esempio che segue:

@Override
	public void onLogicExecution() {
		Log.d("Pltouch", "on logic execution");

		runOnUiThread(new Runnable() {

			@Override
			public void run() {
			TextView ValueResult = (TextView)findViewById(R.id.textView1);
			ProgressBar progressBar = (ProgressBar)findViewById (R.id.progressBar1);
			final EditText SetPointPID1 = (EditText) findViewById(R.id.editTxtPID1);

			Switch sw1,sw2;
			sw1 = (Switch) findViewById(R.id.switch00);

			sw1.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					boolean isChecked = ((Switch) v).isChecked();
					boolean value = (isChecked) ? true : false;

					try {
						pManager.setPidStatus(1, value); // start PID
						} catch (ConnectException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IndexOutOfBounds e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				});

				try {
					z = pManager.analogRead(1);  //read analog input 1
					double x = Math.floor(z);
					m = x/100;                   //transform into temperature

				} catch (IndexOutOfBounds e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ConnectionError e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				ValueResult.setText(String.valueOf(m)); //I represent on screen

	SetPointPID1.setOnEditorActionListener(new OnEditorActionListener() {
	              public boolean onEditorAction1(TextView arg0,
	                      int actionId, KeyEvent arg2) {
	                    // TODO Auto-generated method stub
	                  if (actionId == EditorInfo.IME_ACTION_NEXT) {
	                  }
	                  return false;
	              }
		          @Override
			public boolean onEditorAction(TextView v, int actionId,
					KeyEvent event) {
				if (SetPointPID1.getText().toString().equals("")) {

				}else{
					int setpointPid1 =Integer.parseInt( SetPointPID1.getText().toString());
					try {
						pManager.setPidSetPoint(1, setpointPid1); // write set point
					} catch (ConnectException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IndexOutOfBounds e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				return false;
			}

	          });
	}

Ho volutamente scelto di non concentrarmi su aspetti di design delle interfacce, ma bensì sugli aspetti più tecnici del progetto. Potreste essere voi, lettori, che attraverso la vostra fantasia create la miglior interfaccia che risponda alle vostre necessità.
Andiamo ad analizzare il codice nel dettaglio:


- Impostiamo la direzione della funzione, le costanti Kp-Ki-Kd e la velocità di campionamento
- Leggiamo l’ingresso analogico e rappresentiamolo a video
- Impostiamo il set point
- Abilitiamo la funzione PID

Impostiamo le costanti Kp-Ki-Kd e la velocità di campionamento:

Utilizziamo la funzionde della libreria: pManager.setPidParameters(function,direction,kp,ki,kd,ts);

Questi parametri si possono istanziarli all’avvio dell’app oppure richiamarli in qualsiasi momento. Nel nostro caso abbiamo un bottone che richiama la funzione della libreria.

Attraverso questa istruzione impostiamo :
function: corrisponde a quale funzione utilizziamo ( in questo caso funzione 1)
direction: corrisponde alla direzione del movimento dell’uscita
ci sono due possibilità :
-DigitalValues.HIGH
-DigitalValues.LOW
(nel nostro esempio vogliamo riscaldare perciò utilizziamo DigitalValues.Low; al diminuire della temperatura aumenta l’uscita analogica)
kp : costante proporzionale ( in questo caso 5)
ki : costante integrale ( in questo caso 14)
kd: costante derivata ( in questo caso 4)
ts: tempo campionamento in ms ( in questo caso 100)

Leggiamo l’ingresso analogico e rappresentiamo a video:

Andiamo a leggere il valore dell’ ingresso analogico 1 appoggiandolo alla variabile z che poi ci torna utile per convertirlo in temperatura e rappresentarlo a video.

z= pManager.analogRead(1);

Come si vede basta semplicemente richiamare la libreria pManager.analogRead e assegnargli il riferimento dell’ingresso analogico.

Come da figura :
1 = primo ingresso analogico morsetto VA0

Ora rappresentiamo a video il valore della temperatura in questo modo :

double x = Math.floor(z);
m = x/100;

ValueResult.setText(String.valueOf(m));

Impostiamo il set point per la funzione PID:

Inseriamo un editText dal nome editTxtPID1 e con un semplice controllo  se il campo è vuoto o meno richiamiamo la libreria pManager e scriviamo il valore del set point così :
( attenzione : il valore che passiamo è 12 bit con scala 0-4096 )

if (SetPointPID1.getText().toString().equals("")) {
  // campo vuoto non faccio niente
}else{
  int setpointPid1 =Integer.parseInt( SetPointPID1.getText().toString());
try {
  pManager.setPidSetPoint(1, setpointPid1);
    } catch (ConnectException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IndexOutOfBounds e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

Ora non ci resta altro che abilitare la funzione PID ed otterremo la modulazione del servocomando in funzione della temperatura.
Utilizziamo un semplice switch, che al cambiamento dello stato varia da 0 ad 1.
Anche in questo caso basta richiamare la libreria in questo modo:

pManager.setPidStatus(function, value);

dove il primo campo corrisponde alla relativa funzione ( nel nostro esempio funzione 1) ed il secondo allo start / stop della funzione stessa.