Autore Topic: Acquisizione dati da accelerometri (in generale da sensori)  (Letto 6182 volte)

Offline ScarfaceIII

  • Utente junior
  • **
  • Post: 112
  • Respect: +13
    • raferalston12
    • Mostra profilo
  • Dispositivo Android:
    Nexus One
  • Sistema operativo:
    GNU/Linux, Ubuntu 10.04 / Windows 7
Acquisizione dati da accelerometri (in generale da sensori)
« il: 15 Maggio 2010, 13:16:57 CEST »
+3
Buongiorno a tutti, quale miglior modo di segnalare il proprio consenso a questa sezione, se non...usarla?!
Colgo l'occasione per condividere con voi, nel caso ce ne fosse bisogno, una porzione di codice che avevo richiesto proprio io agli inizi della mia esperienza Android, proprio su questo forum. Sono stato aiutato, e ora vorrei ricambiare condividendo con voi la versione "rivista", migliorata, aggiornata, bla bla bla...passiamo al codice che è meglio va:
Comincio con il cuore dell'acquisizione: una classe che estende Service (per poter acquisire anche in background) e che implementa SensorEventLister, necessario per poter registrarsi come listener dei sensori e riceve i dati.

Codice (Java): [Seleziona]
public class AccelerometerService extends Service implements SensorEventListener {
        private float [] mRawData;
        private long [] mTimestamp;
        private DataModel mDataModel = DataModel.getInstance();

        @Override
        public void onCreate() {
                super.onCreate();
                if(D) Log.v(TAG, "+++ ON CREATE +++");
                mRawData = new float[6];
                mTimestamp = new long[2];
                sm = (SensorManager) getApplicationContext().getSystemService(SENSOR_SERVICE);
                //create Sensors
                Sensor Accel = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
                Sensor Orient = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
                //register this class as a listener for the orientation and accelerometer sensors
                sm.registerListener((SensorEventListener) this, Accel, SensorManager.SENSOR_DELAY_FASTEST);
                sm.registerListener((SensorEventListener) this, Orient, SensorManager.SENSOR_DELAY_FASTEST);
                //creates a new log file
                new_file();
                Toast.makeText(this,"Service started ...", Toast.LENGTH_SHORT).show();
        }
       
        public void onSensorChanged(SensorEvent event) {
                Sensor sensor = event.sensor;
                float [] values = event.values;
                synchronized (this) {
                        Log.d(tag, "onSensorChanged: " + sensor.getName() + ", x: " +
                                        values[0] + ", y: " + values[1] + ", z: " + values[2]);
                        if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                                mRawData [0] = values[0];
                                mRawData [1] = values[1];
                                mRawData [2] = values[2];
                                mTimestamp [0] = event.timestamp;
                                if ((mRawData[3] != 0)||(mRawData[4] != 0)||(mRawData[5] != 0))
                                {
                                        write (mRawData, mTimestamp);
                                }
                        }
                        else if (sensor.getType() == Sensor.TYPE_ORIENTATION ) {
                                mRawData [3] = values[0];
                                mRawData [4] = values[1];
                                mRawData [5] = values[2];
                                mTimestamp [1] = event.timestamp;
                                if ((mRawData[0] != 0)||(mRawData[1] != 0)||(mRawData[2] != 0))
                                {
                                        write (mRawData, mTimestamp);
                                }
                        }
                }
        }
        private void write(float [] data, long [] timestamp){
                mDataModel.setRawData(data);
                mDataModel.write(data[0] + ", " + data[1] + ", " + data[2] + ", " + timestamp[0] + ", "
                                + data[3] + "," + data[4] + ", " + data[5] + ", " + timestamp[1] + " \n");
                //if(D) Log.v(TAG, "*** DATA MODEL WROTE ***");
        }
        public void new_file(){
                String logname = Integer.toString(mCalendar.get(Calendar.YEAR)) + "_"
                        + Integer.toString(mCalendar.get(Calendar.MONTH) + 1) + "_"
                        + Integer.toString(mCalendar.get(Calendar.DAY_OF_MONTH)) + "_"
                        + Integer.toString(mCalendar.get(Calendar.HOUR_OF_DAY)) + "_"
                        + Integer.toString(mCalendar.get(Calendar.MINUTE)) + "_"
                        + Integer.toString(mCalendar.get(Calendar.SECOND)) + "_"
                        + "log.csv";

                mDataModel.newFile(logname);
                mDataModel.write("Sway, Surge, Heave, TimestampAcc, Yaw, Pitch, Roll, TimestampOr \n");
                //if(D) Log.v(TAG, "*** NEW FILE CREATED ***");
        }

        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
        }

        @Override
        public IBinder onBind(Intent arg0) {
        return null;
        }
       
        @Override
        public void onDestroy() {
        super.onDestroy();
        // unregister listener
        sm.unregisterListener(this);
        Toast.makeText(this, "Service destroyed ...", Toast.LENGTH_SHORT).show();
        }
}

Volevo far notare un paio di cose, riguardo all'acquisizione dati dai sensori: innanzitutto il fatto che questa classe è un listener, a cui viene notificato l'evento "SensorChange", ogni qual volta i sensori subiscono un cambiamento di valore. Avendo sotto osservazione due sensori ed essendo l'evento generato uno solo e quindi non univoco, si deve effettuare una distinzione di quale sensore ha generato l'evento, come si vede nella sezione "if (Sensor.getType()...)".
Altra cosa è la sezione "if ((mRawData[3] != 0)||(mRawData[4] != 0)||(mRawData[5] != 0))" che mi serve per effettuare una singola scrittura del file di log, quando ho raccolto i valori di entrambi i sensori, generando quindi un'unica riga nel mio file csv.
Raccogliendo dati da sensori molto diversi, l'acquisizione dai due potrebbe avere tempistiche molto molto diverse e converrebbe quindi generare due file di log separati, ma nel caso di Accelerometro e Orientazione, i dati sono generati dallo stesso sensore fisico, l'accelerometro, appunto. Con la differenza che nel caso dell'orientation, i dati vengono trattati per calcolare la distrubuzione dell'accelerazione di gravità terrestre sui vari assi, quindi l'evento è "doppio", ma generato dallo stesso sensore e quindi i due eventi saranno praticamente contemporanei.
Infine, per esperienza personale, vorrei avvisare che i dati arrivano ad una frequenza non costante, hanno frequenza media molto alta e sono piuttosto rumorosi, conviene quindi applicare un filtro (ad esempio un filtro a media mobile).

Per fare una cosa un po' figa, io mi appoggio ad una classe DataModel, che contiene tutti i miei dati, con dei getter/setter, molto comoda (thanks Qlimax per il tutorial MVC :-P). Siccome qui sopra sono nominati dei metodi che la utilizzano, ne riporto uno stralcio per la comprensione...
Codice (Java): [Seleziona]
public class DataModel extends Observable {

        public static DataModel singleInstance;
        public float[] mRawData = new float [6];
        private File sd = Environment.getExternalStorageDirectory();
        private File f;

        public static DataModel getInstance() {
                if (singleInstance == null) {
                        singleInstance = new DataModel();
                }
                return singleInstance;
        }

        public float[] getRawData() {
                return mRawData;
        }

        public void setRawData(float[] Data) {
                this.mRawData = Data;
                setChanged();
                notifyObservers(this.mRawData);
        if(D) Log.v(TAG, "* DATA MODIFIED BY SENSOR SERVICE *");
        }

        public void newFile(String logname) {
                f = new File(sd, logname);
        }

        public void write(String Data) {
                FileWriter fw = null;
                BufferedWriter bw = null;
                try {
                        fw = new FileWriter(f, true);
                        bw = new BufferedWriter(fw);
                        bw.write(Data);
                        bw.close();
                        fw.close();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
}

Ovviamente non è strettamente necessario usare una classe che funga da datamodel, specie per un esempio così semplice, ma nel mio caso, facendo anche altre cose (ad esempio mantiene lo stato di una connessione bluetooth tra diverse activity), mi è molto utile.
Come si può vedere nella classe AccelerometerService, l'esempio è incentrato sugli accelerometri, ma in fase di impostazione dei listener, si può scegliere quale sensore "ascoltare". Perdonate se ci sono errori nel codice ma ho tentato di fare un taglia-incolla intelligente da porzioni di codice molto più grandi e spero di non aver perso pezzi, nel caso, fatemi sapere che sistemo. Buon divertimento!
« Ultima modifica: 18 Agosto 2010, 11:21:10 CEST da JD »

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:acquisizione dati da accelerometri (in generale da sensori)
« Risposta #1 il: 15 Maggio 2010, 18:21:36 CEST »
0
Ciao Scarface,
grazie per il contributo :)

Visto però l'ammontare del codice, non sarebbe forse il caso di creare un tutorial anziché uno snippet? La classe AccelerometerService è veramente grossa e gli snippets dovrebbero essere poche righe di codice.

Non dovrebbe essere troppo complicato per te convertirlo in tutorial, dovresti solo allegare uno zip con un progettino che utilizza questa classe, anche solo per stampare a video i valori letti dai sensori.

Fammi sapere :)
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline ScarfaceIII

  • Utente junior
  • **
  • Post: 112
  • Respect: +13
    • raferalston12
    • Mostra profilo
  • Dispositivo Android:
    Nexus One
  • Sistema operativo:
    GNU/Linux, Ubuntu 10.04 / Windows 7
Re:acquisizione dati da accelerometri (in generale da sensori)
« Risposta #2 il: 16 Maggio 2010, 00:41:06 CEST »
0
Ciao Scarface,
grazie per il contributo :)

Visto però l'ammontare del codice, non sarebbe forse il caso di creare un tutorial anziché uno snippet? La classe AccelerometerService è veramente grossa e gli snippets dovrebbero essere poche righe di codice.

Non dovrebbe essere troppo complicato per te convertirlo in tutorial, dovresti solo allegare uno zip con un progettino che utilizza questa classe, anche solo per stampare a video i valori letti dai sensori.

Fammi sapere :)

perdona la negligenza, però, pur per grossa che sia, questa porzione di codice è solo una "piccola parte" di un qualcosa di più grosso e per creare un tutorial dovrei, come ben dici, allegare un progetto, creandolo da zero però. In ogni caso, anche se grossa, il compito che svolge questa porzione di codice è limitato, ho solo voluto inserirlo in un contesto comprensibile, aggiungendo ad esempio alcune funzionalità di logging come ad esempio scrivere su file, ecc. Erano metodi che già avevo, erano inerenti all'argomento e ho voluto "regalarli", per così dire...anzichè mettere la sola classe SensorEventListener col metodo OnSensorChange...

Ripeto: perdona la negligenza, ma mi sono complimentato con l'aggiunta di questa sezione appunto perché mi permette di condividere pezzi di codice e non sentirmi solo uno sfruttatore, evitando però lo "sbattimento" di creare un tutorial, con tutto quello che ne concerne, non me ne volere, spero non sia un problema.

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:acquisizione dati da accelerometri (in generale da sensori)
« Risposta #3 il: 16 Maggio 2010, 11:49:39 CEST »
0
Nessun problema ;)
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline TelespallaZ

  • Nuovo arrivato
  • *
  • Post: 3
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei Ideos
  • Sistema operativo:
    Windows 7
Re:Acquisizione dati da accelerometri (in generale da sensori)
« Risposta #4 il: 13 Marzo 2011, 19:39:19 CET »
0
Ciao Scarface, sono alle prime armi con la programmazione in android e volevo avere una precisazione su quel ciclo if(D); la variablie D di che tipo deve essere? :-P

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Acquisizione dati da accelerometri (in generale da sensori)
« Risposta #5 il: 13 Marzo 2011, 20:16:32 CET »
0
Ciao Scarface, sono alle prime armi con la programmazione in android e volevo avere una precisazione su quel ciclo if(D); la variablie D di che tipo deve essere? :-P

Mi sa che D è una variabile booleana che viene usata per abilitara il logging in fase di debug.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline TelespallaZ

  • Nuovo arrivato
  • *
  • Post: 3
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei Ideos
  • Sistema operativo:
    Windows 7
Re:Acquisizione dati da accelerometri (in generale da sensori)
« Risposta #6 il: 13 Marzo 2011, 20:57:57 CET »
0
Ok grazie! Ora provo

Offline ScarfaceIII

  • Utente junior
  • **
  • Post: 112
  • Respect: +13
    • raferalston12
    • Mostra profilo
  • Dispositivo Android:
    Nexus One
  • Sistema operativo:
    GNU/Linux, Ubuntu 10.04 / Windows 7
Re:Acquisizione dati da accelerometri (in generale da sensori)
« Risposta #7 il: 13 Marzo 2011, 21:52:26 CET »
0
confermo bradipao. è una sottigliezza abbastanza utile per debuggare

Offline TelespallaZ

  • Nuovo arrivato
  • *
  • Post: 3
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Huawei Ideos
  • Sistema operativo:
    Windows 7
Re:Acquisizione dati da accelerometri (in generale da sensori)
« Risposta #8 il: 14 Marzo 2011, 19:25:04 CET »
0
Ciao a tutti nuovamente, sto provando a creare un nuovo progetto sfruttando il codice postato sopra, ho importato tutte le librerie e i package necessari e sono riuscito a compilare correttamente, però quando vado a debuggare l'applicazione sul telefono riesco a visualizzare solamente il nome dell'applicazione, ma dei sensori nessuna traccia e non capisco il perchè, qualcuno mi può aiutare?
Codice (Java): [Seleziona]
package sensors.data;

import java.util.Calendar;
//import android.app.Activity;
import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
//import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;



public class AccelerometerService extends Service implements SensorEventListener {
    private float [] mRawData;
    private long [] mTimestamp;
    private static String TAG,tag;
    private DataModel mDataModel = DataModel.getInstance();
    private SensorManager sm;
    private Calendar mCalendar;

    @Override
    public void onCreate() {
            super.onCreate();
            boolean D=true;
                        if(D) Log.v(TAG, "+++ ON CREATE +++");
            mRawData = new float[6];
            mTimestamp = new long[2];
            sm = (SensorManager) getApplicationContext().getSystemService(SENSOR_SERVICE);
            //create Sensors
            Sensor Accel = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            Sensor Orient = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
            //register this class as a listener for the orientation and accelerometer sensors
            sm.registerListener((SensorEventListener) this, Accel, SensorManager.SENSOR_DELAY_FASTEST);
            sm.registerListener((SensorEventListener) this, Orient, SensorManager.SENSOR_DELAY_FASTEST);
            //creates a new log file
            new_file();
            Toast.makeText(this,"Service started ...", Toast.LENGTH_SHORT).show();
    }
   
    public void onSensorChanged(SensorEvent event) {
            Sensor sensor = event.sensor;
            float [] values = event.values;
            synchronized (this) {
                    Log.d(tag, "onSensorChanged: " + sensor.getName() + ", x: " +
                                    values[0] + ", y: " + values[1] + ", z: " + values[2]);
                    if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                            mRawData [0] = values[0];
                            mRawData [1] = values[1];
                            mRawData [2] = values[2];
                            mTimestamp [0] = event.timestamp;
                            if ((mRawData[3] != 0)||(mRawData[4] != 0)||(mRawData[5] != 0))
                            {
                                    write (mRawData, mTimestamp);
                            }
                    }
                    else if (sensor.getType() == Sensor.TYPE_ORIENTATION ) {
                            mRawData [3] = values[0];
                            mRawData [4] = values[1];
                            mRawData [5] = values[2];
                            mTimestamp [1] = event.timestamp;
                            if ((mRawData[0] != 0)||(mRawData[1] != 0)||(mRawData[2] != 0))
                            {
                                    write (mRawData, mTimestamp);
                            }
                    }
            }
    }
    private void write(float [] data, long [] timestamp){
            mDataModel.setRawData(data);
            mDataModel.write(data[0] + ", " + data[1] + ", " + data[2] + ", " + timestamp[0] + ", "
                            + data[3] + "," + data[4] + ", " + data[5] + ", " + timestamp[1] + " \n");
            //if(D) Log.v(TAG, "*** DATA MODEL WROTE ***");
    }
    public void new_file(){
            String logname = Integer.toString(mCalendar.get(Calendar.YEAR)) + "_"
                    + Integer.toString(mCalendar.get(Calendar.MONTH) + 1) + "_"
                    + Integer.toString(mCalendar.get(Calendar.DAY_OF_MONTH)) + "_"
                    + Integer.toString(mCalendar.get(Calendar.HOUR_OF_DAY)) + "_"
                    + Integer.toString(mCalendar.get(Calendar.MINUTE)) + "_"
                    + Integer.toString(mCalendar.get(Calendar.SECOND)) + "_"
                    + "log.csv";

            mDataModel.newFile(logname);
            mDataModel.write("Sway, Surge, Heave, TimestampAcc, Yaw, Pitch, Roll, TimestampOr \n");
            //if(D) Log.v(TAG, "*** NEW FILE CREATED ***");
    }

    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
    }

    @Override
    public IBinder onBind(Intent arg0) {
    return null;
    }
   
    @Override
    public void onDestroy() {
    super.onDestroy();
    // unregister listener
    sm.unregisterListener(this);
    Toast.makeText(this, "Service destroyed ...", Toast.LENGTH_SHORT).show();
    }
}

e questa è la classe DataModel:
Codice (Java): [Seleziona]
package sensors.data;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import java.util.Observable;
import android.os.Environment;
import android.util.Log;

public class DataModel extends Observable {

    public static DataModel singleInstance;
    public float[] mRawData = new float [6];
    private File sd = Environment.getExternalStorageDirectory();
    private File f;
    private static String TAG;

    public static DataModel getInstance() {
            if (singleInstance == null) {
                    singleInstance = new DataModel();
            }
            return singleInstance;
    }

    public float[] getRawData() {
            return mRawData;
    }

    public void setRawData(float[] Data) {
            this.mRawData = Data;
            setChanged();
            notifyObservers(this.mRawData);
            boolean D=true;
                        if(D) Log.v(TAG, "* DATA MODIFIED BY SENSOR SERVICE *");
    }

    public void newFile(String logname) {
            f = new File(sd, logname);
    }

    public void write(String Data) {
            FileWriter fw = null;
            BufferedWriter bw = null;
            try {
                    fw = new FileWriter(f, true);
                    bw = new BufferedWriter(fw);
                    bw.write(Data);
                    bw.close();
                    fw.close();
            } catch (IOException e) {
                    e.printStackTrace();
            }
    }
}

Grazie anticipatamente!  ;-)