mercoledì 10 gennaio 2018

Rotazioni con quaternioni

I quaternioni sono oggetti matematici 4 dimensionali (aw+bx+cy+dz) costituiti da una parte scalare (detta anche reale) ed una parte vettoriale a 3 dimensioni (detta immaginaria). Il loro uso in informatica e' legata al calcolo della rotazione di oggetti in 3 dimensioni e la rappresentazione geometrica del quaternione e' dato da un vettore da (0,0,0) (b,c,d) attorno al quale si sviluppa una rotazione di valore a. (il vettore rotazione nel caso non lo sia deve essere normalizzato). Un documento di lettura abbastanza semplice si puo' trovare a questo link



i vantaggi sono:

0) i quaternioni sono ottimali per calcolare le rotazioni nello spazio. Per invece individuare la posizione di un punto dello spazio e' piu' semplice la definizione cartesiana o con gli angoli di Eulero

1) rispetto ad una rotazione con gli angoli di Eulero (dove sono necessari 9 parametri) con i quaternioni sono sufficienti 4 parametri

2) la rotazione tramite gli angoli di Eulero e' data dalla successione combinata di rotazioni attorno ai tre assi cartesiani con un ordine non preordinato (per esempio si puo' ruotare prima x che su y ma lo stesso risultato puo' essere ottenuto anche invertendo l'ordine). La rotazione tramite quaternioni e' univoca in quanto quella ottimale

3) legato al punto 2 la rotazione con i quaternioni non e' soggetta a gimbal lock

Per effettuare una rotazione si utilizza una estensione del metodo per i numeri complessi in 2D.
Si crea un quaterione (R) che descriva la rotazione e poi si applica al vettore desiderato(P) con la regola RPR-1

la moltiplicazione dei tre quaternioni segue una regola un po' particolare che si riassume come



Questa codice e' stato ripreso da questo link

---------------------------------------
        //coordinate del punto da ruotare
        float x_old = 0;
        float y_old = 1;
        float z_old = 1;
        
        //angolo di rotazione
        float angle = (float) Math.toRadians(45.0);
        
        // vettore di rotazione
        float xi = 1;
        float yi = 1;
        float zi = 1;
        
        float norma = (float) Math.sqrt((xi*xi)+(yi*yi)+(zi*zi));
        
        xi = xi/norma;
        yi = yi/norma;
        zi = zi/norma;

        double w = Math.cos(angle/2.0);
        double x = xi*Math.sin(angle/2.0);
        double y = yi*Math.sin(angle/2.0);
        double z = zi*Math.sin(angle/2.0);

        float x_new = (float) ((1 - 2*y*y -2*z*z)*x_old + (2*x*y + 2*w*z)*y_old + (2*x*z-2*w*y)*z_old);
        float y_new = (float) ((2*x*y - 2*w*z)*x_old + (1 - 2*x*x - 2*z*z)*y_old + (2*y*z + 2*w*x)*z_old);
        float z_new = (float) ((2*x*z + 2*w*y)*x_old + (2*y*z - 2*w*x)*y_old + (1 - 2*x*x - 2*y*y)*z_old);
                
        System.out.println(Double.toString(x_new));
        System.out.println(Double.toString(y_new));
        System.out.println(Double.toString(z_new));
---------------------------------------


Rotazione di un cubo di spigolo 1 orientato come gli asse e con spigolo in (0,0,0) di 45° attorno ad un vettore (0,0,0)(1,1,1). Dati rappresentati con Geogebra (nella tabella a fianco i valori dei punti ruotati)


lunedì 8 gennaio 2018

Estimote Eddystone telemetry bridge in Android

Un esempio per ricevere i dati di telemetria dei pacchetti Eddystone (il protocollo di Google per i beacons) e trasmetterli poi ad un server remoto (ho scoperto solo oggi che il nome Eddystone deriva dal faro di Eddystone)



compile 'com.estimote:sdk:1.0.12'

---------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="innocenti.luca.com.eddyacc">

    <uses-permission android:name="android.permisssion.ACCESS_COARSE_LOCATION">
</uses-permission>
    <uses-permission android:name="android.permisssion.INTERNET">
</uses-permission>


    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

---------------------------------------------


package innocenti.luca.com.eddyacc;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.estimote.coresdk.common.config.EstimoteSDK;
import com.estimote.coresdk.recognition.packets.Eddystone;
import com.estimote.coresdk.recognition.packets.EstimoteTelemetry;
import com.estimote.coresdk.service.BeaconManager;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;





public class MainActivity extends AppCompatActivity {

    private BeaconManager beaconManager;
    private String scanId;




    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EstimoteSDK.initialize(getApplicationContext(),"eddyacc-ev0","f73efb85963579672c048fc92de64039");

        beaconManager = new BeaconManager(this);

        beaconManager.setEddystoneListener(new BeaconManager.EddystoneListener() {
            @Override public void onEddystonesFound(List<Eddystone> eddystones) {
                for (Eddystone e : eddystones){
                    if (e.telemetry != null)
                    {
                        String aa = "http://150.217.73.108/luca/test.php?temperatura="+Double.toString(e.telemetry.temperature);
                        Double myTaskParameters = e.telemetry.temperature;
                        new invia().execute(myTaskParameters);

                        Log.d("Eddy", e.macAddress + " temperature :" + e.telemetry.temperature + " ");
                    }
                }

            }
        });


        beaconManager.setTelemetryListener(new BeaconManager.TelemetryListener() {
            @Override            public void onTelemetriesFound(List<EstimoteTelemetry> telemetries) {
                for (EstimoteTelemetry tlm : telemetries) {
                    Log.d("TELEMETRY", "beaconID: " + tlm.deviceId +
                            ", temperature: " + tlm.temperature + " °C" + tlm.accelerometer.x);
                }
            }
        });
    }




    @Override protected void onStart() {
        super.onStart();
        beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
            @Override            public void onServiceReady() {
                beaconManager.startEddystoneDiscovery();
                beaconManager.startTelemetryDiscovery();
            }
        });
    }

    @Override protected void onStop() {
        super.onStop();
        beaconManager.stopEddystoneDiscovery();

        beaconManager.stopTelemetryDiscovery();
    }

    @Override protected void onDestroy(){
        super.onDestroy();
        beaconManager.disconnect();
    }

    private class invia extends AsyncTask<Double, Void, String> {

        @Override        protected String doInBackground(Double... params) {
            // These two need to be declared outside the try/catch            // so that they can be closed in the finally block.            HttpURLConnection urlConnection = null;
            Double t = params[0];

            try {

                URL url = new URL("http://150.217.73.108/luca/test.php?temperatura="+Double.toString(t));
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();
                urlConnection.getInputStream();


            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }

            }
            return null;
        }

            @Override            protected void onPostExecute (String s)
            {
                super.onPostExecute(s);
            }

        }
}

Homebrew Launcher su Nintendo 3DS


Cosa fare quando a Natale il bambino ti chiede un esotico giochino Nintendo che e' uscito solo in Giappone??

1) Gli spieghi che le consolle ed in giochi sono distribuiti a seconda di alcune regioni del mondo e che se hai la consolle italiana non puoi giocare con le schede giapponesi??

2) Provi a scaricare la Rom del gioco e la monti sull'emulatore Citra ? (per poi scoprire che anche un I5 funziona al 25% di velocita' del Nintendo originale??

no, ti ricordi che su un mercatino avevi comprato ad un prezzo stracciato una 3DS per poi scoprire a casa che era una consolle americana, non compatibile con le schede in vendita in Italia e metterla in un cassetto per un paio di anni. Avere una consolle vecchia non aggiornata  (nel mio caso 10.6.0-31U) permette di far girare un firmware su cui sono conosciuti degli exploit per mandare in esecuzione Homebrew Launcher.



Il modo piu' semplice per ottenere l'exploit e' quello di andare su Homebrew Github e preparare la scheda SD con l'hack per Nintendo 3DS seguendo le istruzioni (molto semplici peraltro).
Con Homebrew launcher non si possono eseguire giochi piratati ma si possono eseguire giochi di qualsiasi regione e quindi alla fine sono riuscito a far funzionare il gioco giapponese


ps : visto che c'ero una partina a DOOM per DOS...

Filtro Kalman su dati accelerometro Android

Una implementazione del filtro kalman su dati sui dati dell'accelerometro in Android (in realta' i dati sono pitch e roll ma derivano dall'accelerometro)

Per il calcolo e' stata utilizzata la libreria JKalman.  Dal pacchetto zip si estrae il file .jar e dalla cartella dist e lo si copia nella cartella /libs del progetto Android. A questo punto si clicca destro sul file .jar in Android Studio e si clicca "Add as Library"




Si aggiunge la classe ripresa da qui
------------------------------------------------------------------
package innocenti.luca.com.camerakit;

import jkalman.JKalman;
import jama.Matrix;

public class KalmanFilter {

    private int variables;
    private JKalman kalman;
    private Matrix s; // state [x, y, dx, dy, dxy]    private Matrix c; // corrected state [x, y, dx, dy, dxy]    private Matrix m; // measurement [x]
    /*     * Inicializa el filtro kalman con 2 variables     */    public void initialize2() throws Exception{
        double dx, dy;

        if(variables != 0){
            throw new RuntimeException();
        }
        variables = 2;
        kalman = new JKalman(4, 2);

        // constant velocity        dx = 0.2;
        dy = 0.2;

        s = new Matrix(4, 1); // state [x, y, dx, dy, dxy]        c = new Matrix(4, 1); // corrected state [x, y, dx, dy, dxy]
        m = new Matrix(2, 1); // measurement [x]        m.set(0, 0, 0);
        m.set(1, 0, 0);

        // transitions for x, y, dx, dy        double[][] tr = { {1, 0, dx, 0},
                {0, 1, 0, dy},
                {0, 0, 1, 0},
                {0, 0, 0, 1} };
        kalman.setTransition_matrix(new Matrix(tr));

        // 1s somewhere?        kalman.setError_cov_post(kalman.getError_cov_post().identity());

    }

    /*     * Aplica Filtro a variables     */    public void push(double x,double y) throws Exception{
        m.set(0, 0, x);
        m.set(1, 0, y);

        c = kalman.Correct(m);
        s = kalman.Predict();
    }

    /*     * obtiene arreglo con datos filtrados.     */    public double[] getKalmanPoint2() throws Exception{
        double[] point = new double[2];
        point[0] = c.get(0,0);
        point[1] = c.get(1,0);
        return point;
    }

    /*     * obtiene arreglo con prediccion de punto.     */    public double[] getPredict2() throws Exception{
        double[] point = new double[2];
        point[0] = s.get(0,0);
        point[1] = s.get(1,0);
        return point;
    }

    /*     * obtiene cantidad de variables del objeto     */    public int getNVariables() throws Exception{
        return this.variables;
    }

}

------------------------------------------------------------------


a questo punto e' sufficiente creare il filtro in OnCreate

kf = new KalmanFilter();

try {
    kf.initialize2();
} catch (Exception e) {
    e.printStackTrace();
}


si popola poi l'array dei dati e si ottiene il valore filtrato

try {
    kf.push(pi,ro);
} catch (Exception e) {
    e.printStackTrace();
}

try {
    double[] test = kf.getKalmanPoint2();
    Log.d("Kalman", Double.toString(Math.toDegrees(test[0]))+ " "+ 
 Double.toString(Math.toDegrees(test[1])) + " " +Math.toDegrees(pi)+ " "+
 Math.toDegrees(ro));
} catch (Exception e) {
    e.printStackTrace();
}







venerdì 5 gennaio 2018

16 anni di onorato servizio

Non sempre sono i programmi migliori quelli che durano nel tempo...e questo e' un esempio.
Un  mio programma fatto in Visual Basic 6, scritto con i piedi e con gli occhi bendati, e' ancora in funzione e gestisce la fatturazione di un micro impresa dopo 16 anni


martedì 2 gennaio 2018

Misurare distanze con realta' aumentata

Un piccolo esperimento sulla possibilita' di misurare distanze utilizzando la realta' aumentata e l'utilizzo di un semplice marker.

Per questa applicazione e' stato modificato l'esempio ARSimple di ARToolKit per Android.

Il codice puo' essere scaricato da GitHub


La cosa piu' difficile e' stato mostrare in tempo reale la distanza sulla interfaccia perche' con OpenGL su Android non esiste un modo semplice di renderizzare testo. La soluzione e' un po' contorta ma funziona. Dalla classe ARSimplerender viene scritta una sharedpreference con all'interno il valore della distanza in mm, nella classe ARSimple (che gestisce la UI) c'e' un timer che legge ogni 0.1 secondi il valore della sharedpreference e la mostra a video nella textview

Per avere un'idea della precisione di tale metodo si puo' consultare il seguente poster o l'articolo
Measuring ARTootKit accuracy in long distance tracking experiments (P. Malbezin,W. Piekarski,B.H. Thomas) 2002 Augmented Reality Toolkit, The First IEEE International Workshop

La massima distanza che sono riuscito a misurare e' stata di circa 2.6 m

ps : la applicazione e' stata provata su Nexus 5 e Nexus 5x, Sul 5x la applicazione NON funziona perche' il sensore della camera e' ruotata di 90° rispetto alla norma (questa cosa mi ha ovviamente fatto diventare pazzo per un paio di giorni)

ps: una cosa curiosa. Mentre stavo facendo le prove ho inquadrato senza volerlo la tastiera del Mac e ....e' comparso il cubetto ....in pratica ARToolkit ha confuso il marker Hiro con i simboli sulla tastiera





venerdì 29 dicembre 2017

Compilare ARToolKit su Android Studio 3

Le istruzioni per installare ARToolKit su Android Studio sono piuttosto vecchie e di fatto non funzionano nella configurazione attuale (sono state scritte nel 2016)

Per fare funzionare gli esempi, dopo avere compilato la libreria nell'NDK (come da istruzioni), si devono modificare i file di progetto come segue (vedi linee evidenziate in giallo)

---------------------------
buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath 'com.android.tools.build:gradle-experimental:0.11.1'

        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }
}

allprojects {
    repositories {
        jcenter()
    }
}
---------------------------


gradle.wrapper.properties
---------------------------
#Fri Dec 29 14:39:59 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
---------------------------


build.gradle (Module ArSimple)
---------------------------

apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23        buildToolsVersion = "23.0.2"
        defaultConfig.with {
            applicationId = "org.artoolkit.ar.samples.ARSimple"            minSdkVersion.apiLevel = 15            targetSdkVersion.apiLevel = 22            versionCode = 1            //Integer type incremented by 1 for every release, major or minor, to Google store            versionName = "1.0" //Real fully qualified major and minor release description
            buildConfigFields.with {
                //Defines fields in the generated Java BuildConfig class, in this case, for                create() {           //default config, that can be accessed by Java code                    type = "int"     //e.g. "if (1 == BuildConfig.VALUE) { /*do something*/}".                    name = "VALUE"                    //See: [app or lib]/build/generated/source/buildConfig/[package path]/                    value = "1"      //     BuildConfig.java                }
            }
        }
    }

    android.buildTypes {
        release {
            minifyEnabled = false            proguardFiles.add(file('proguard-rules.pro'))        }
    }

    android.productFlavors {
    }
    android.sources {
        main {
            jni {
                source {
                    srcDirs = ['src/main/nop']
                }
            }
        }
        main {
            jniLibs {
                source {
                    srcDirs = ['src/main/libs']
                }
            }
        }
    }
    
}

dependencies {
    //compile 'com.android.support:support-v4:23.0.1'    //compile 'com.android.support:appcompat-v7:23.0.1' //Only required when the target device API level is greater than    compile project(':aRBaseLib')
}     

------------------------------------------------------

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...