sabato 7 novembre 2020

PCL e Tango tablet

Ho ritirato fuori il mio tablet Google Yellowstone Tango per provare il trattamento dati delle nuvole dei punti con PCL

I dati reali sono stati presi presso la cava di Maiano (Fiesole) che e' la palestra di geologia per numerose generazioni di geologi fiorentini 

Ho ripreso con lo scanner del tablet questo dettaglio 


in particolare volevo vedere se riuscivo a misurare l'angolo tra i due piani indicati nella foto sottostante


(quello in blu e' il piano del fronte di scavo, quello in rosso e' relativo ad una frattura)

Usando la app Clino Fieldmove e Innstereo 


a posteriori la scelta delle superfici non e' stata felicissima perche' sono tutte ad alto angolo e si distinguono poco sullo stereoplot

Con il tablet la distanza massima a cui era possibile avere risposta dallo scanner era di circa 3 m. Per prova la superficie e' stata bagnata per vedere se il laser infrarosso era influenzato dall'umidita' della parete ma non si verifiche significative tra superficie asciutta e bagnata

I dati sono stati salvati in PLY e sono stati elaborati con PCL in QtCreator




Il programma dopo aver caricato il file PLY fa un sottocampionamento dei dati con VoxelGrid (passando da oltre 200.000 superfici a poco oltre 50 superfici) e calcola le  normali. I dati sono visualizzati con il visualizzatore interno a PLC

leggermente modificato da https://github.com/jeffdelmerico/pointcloud_tutorial


========================================

cmake_minimum_required(VERSION 3.5)

project(maiano LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features visualization)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable(maiano main.cpp)

target_link_libraries(maiano ${PCL_LIBRARIES})

========================================

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/ply_io.h>

#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>


using namespace std;

void
downsample (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &points, float leaf_size,
            pcl::PointCloud<pcl::PointXYZRGB>::Ptr &downsampled_out)
{
    cout << "PointCloud before filtering: " << points->width * points->height
           << " data points (" << pcl::getFieldsList (*points) << ")." << std::endl;

  pcl::VoxelGrid<pcl::PointXYZRGB> vox_grid;
  vox_grid.setLeafSize (leaf_size, leaf_size, leaf_size);
  vox_grid.setInputCloud (points);
  vox_grid.filter (*downsampled_out);
  cout << "PointCloud after filtering: " << downsampled_out->width * downsampled_out->height
         << " data points (" << pcl::getFieldsList (*downsampled_out) << ")." << std::endl;

}

void compute_surface_normals (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &points, float normal_radius,
                                    pcl::PointCloud<pcl::Normal>::Ptr &normals_out)
{
  pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> norm_est;

  // Use a FLANN-based KdTree to perform neighborhood searches
  norm_est.setSearchMethod (pcl::search::KdTree<pcl::PointXYZRGB>::Ptr
                            (new pcl::search::KdTree<pcl::PointXYZRGB>));

  // Specify the size of the local neighborhood to use when computing the surface normals
  norm_est.setRadiusSearch (normal_radius);

  // Set the input points
  norm_est.setInputCloud (points);

  // Estimate the surface normals and store the result in "normals_out"
  norm_est.compute (*normals_out);
}

void visualize_normals (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr points,
                        const pcl::PointCloud<pcl::PointXYZRGB>::Ptr normal_points,
                        const pcl::PointCloud<pcl::Normal>::Ptr normals)
{
  // Add the points and normals to the vizualizer
  pcl::visualization::PCLVisualizer viz;
  viz.addPointCloud (points, "points");
  viz.addPointCloud (normal_points, "normal_points");

  viz.addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal> (normal_points, normals, 1, 0.01, "normals");

  // Give control over to the visualizer
  viz.spin ();
}


int main (int argc, char** argv)
{
    // Load data from pcd
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
    //if (pcl::io::loadPCDFile<pcl::PointXYZRGB> ("../data/robot1.pcd", *cloud) == -1) //* load the file
    if (pcl::io::loadPLYFile<pcl::PointXYZRGB> ("maiano.ply", *cloud) == -1) //* load the file

    {
        PCL_ERROR ("Couldn't read file robot1.pcd \n");
        return (-1);
    }

    // Point Clouds to hold output
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr downsampled (new pcl::PointCloud<pcl::PointXYZRGB>);
    pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);

    // Downsample the cloud
    const float voxel_grid_leaf_size = 1.0;
    downsample (cloud, voxel_grid_leaf_size, downsampled);

    // Compute surface normals
    const float normal_radius = 1.0;
    compute_surface_normals (downsampled, normal_radius, normals);

    visualize_normals(cloud, downsampled, normals);

    int i = 0;
    for (pcl::Normal n : *normals) {
        std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z <<"\n";
        i++;
    }

    return(0);
}

========================================

Plottando le normali si ha questo risultato



Al di la' dell'orientamento verso il Nord (che lo scanner non registra in quanto non calibra i dati con la bussola) il programma estra sia le suprfici ad alto angolo che alcun suborizzontali.. non so quanto i dati alla fine siano corretti....i dati delle normali vengono forniti come componenti x,y,z di un versore con origine 0,0,0....non so se ho fatto qualche sbaglio nella conversione in strike e dip



mercoledì 4 novembre 2020

GPS differenziale con RTL-SDR

Un progetto molto interessante che sto provando e' GNSS-SDR , un sistema per usare un modulo RTL-SDR v3 da circa 20 ed una antenna GPS da 5 euro per poter ricevere i dati GPS ed utilizzarli per la correzione differenziale (ovviamente il dispositivo agisce come se fosse in banda L1.. per avere la banda L2 bisognerebbe avere un'altra RTL-SDR sintonizzata sulla frequenza L2 ....ed in ogni caso il software non prevede questa decodifica)


Il computer puo' essere un comune portatile...l'unico problema e' che il programma apre un thread per ogni satellite osservato quindi la memoria e' sempre benvenuta

Oltre alla libreria si devono scaricare anche 

http://www.rtklib.com/rtklib.htm

https://github.com/rtlsdrblog/rtl-sdr/releases/tag/v1.1

Per far funzionare il software prima si devono configurare i driver della scheda, lanciare il file bat bias_tee_on e successivamente gnss-sdrgui

(il bias tee e' un circuito inserito nell'elettronica dell'RTL-SDR per alimentare l'antenna attiva esterna patch)

Istruzioni nel video 


Questa e' la configurazione che ha funzionato per me


Per semplicita' io ho abilitato come output la configurazione di rete e l'output su file Rinex
Il file Rinex sembra registrare solo la costellazione GPS anche se nella finestra di monitor vengono mostrati anche i dati di Galileo e Glonass

ATTENZIONE: deve essere impostata la coordinata il piu' precisa possibile del punto di misura

Per monitorare la applicazione si puo' premere il pulsante M a fianco del pulsante Start 

Attenzione : se si usa Windows in italiano il file Rinex in uscita avra' i punti decimali sostituiti da virgole e cio' rende impossibile fare il postprocessing con RTKLib. (con editor di testo si puo' fare Find/Replace per la modifica da virgola a punto)

Questo il risultato finale del post processing

% program   : RTKPOST ver.2.4.3 b33

% inp file  : C:\Users\lucai\Desktop\gpssdr\sdr_20201015113400.obs

% inp file  : C:\Users\lucai\Desktop\gpssdr\cal\cala289h00.rnx\cala289h00.20o

% inp file  : C:\Users\lucai\Desktop\gpssdr\cal\cala289h00.rnx\cala289h00.20n

% obs start : 2020/10/15 11:34:36.1 GPST (week2127 387276.1s)

% obs end   : 2020/10/15 11:44:20.1 GPST (week2127 387860.1s)

% pos mode  : static

% freqs     : L1+L2

% solution  : forward

% elev mask : 15.0 deg

% dynamics  : off

% tidecorr  : off

% ionos opt : broadcast

% tropo opt : saastamoinen

% ephemeris : broadcast

% amb res   : continuous

% val thres : 3.0

% antenna1  :                       ( 0.0000  0.0000  0.0000)

% antenna2  :                       ( 0.0000  0.0000  0.0000)

% ref pos   : 43.854470000   11.166080000   -50.0000

%

% (lat/lon/height=WGS84/ellipsoidal,Q=1:fix,2:float,3:sbas,4:dgps,5:single,6:ppp,ns=# of satellites)

%  GPST                  latitude(deg) longitude(deg)  height(m)   Q  ns   sdn(m)   sde(m)   sdu(m)  sdne(m)  sdeu(m)  sdun(m) age(s)  ratio

2020/10/15 11:36:42.068   43.745316827   11.292427977   -63.6904   2   6   1.0450   0.1566   0.2377  -0.3671   0.0280   0.0864   0.07    1.8

2020/10/15 11:36:48.068   43.745347458   11.292425620   -64.6534   2   6   0.4222   0.1009   0.2274  -0.1555   0.0559  -0.1237   0.07    1.5

2020/10/15 11:36:57.068   43.745340452   11.292424419   -65.2242   2   6   0.3162   0.0907   0.2193  -0.1200   0.0539  -0.1264   0.07    1.1

2020/10/15 11:37:03.068   43.745306097   11.292430701   -65.7996   1   6   0.0136   0.0063   0.0206  -0.0056   0.0036  -0.0125   0.07    3.6

2020/10/15 11:37:09.068   43.745293252   11.292432728   -66.0801   2   6   0.2346   0.0797   0.2056  -0.0921   0.0490  -0.1221   0.07    1.1

2020/10/15 11:37:15.068   43.745286998   11.292432314   -66.6728   2   6   0.2129   0.0759   0.1997  -0.0845   0.0468  -0.1193   0.07    1.2

2020/10/15 11:37:21.068   43.745275726   11.292432226   -66.5496   2   6   0.1968   0.0726   0.1943  -0.0787   0.0449  -0.1166   0.07    1.3

2020/10/15 11:37:27.068   43.745263392   11.292434190   -66.5540   2   6   0.1841   0.0698   0.1893  -0.0741   0.0432  -0.1139   0.07    2.3

2020/10/15 11:37:33.068   43.745263939   11.292432314   -65.8761   2   6   0.1737   0.0673   0.1847  -0.0703   0.0417  -0.1114   0.07    1.9

2020/10/15 11:37:39.068   43.745263809   11.292432072   -65.3947   2   6   0.1651   0.0651   0.1804  -0.0670   0.0403  -0.1090   0.07    1.1

2020/10/15 11:37:45.068   43.745260600   11.292432193   -64.7574   2   6   0.1577   0.0631   0.1764  -0.0642   0.0390  -0.1068   0.07    1.2

2020/10/15 11:37:51.068   43.745257360   11.292431499   -64.6951   2   6   0.1513   0.0613   0.1727  -0.0618   0.0379  -0.1047   0.07    2.2

2020/10/15 11:37:57.068   43.745257344   11.292430153   -64.7047   2   6   0.1457   0.0596   0.1691  -0.0596   0.0369  -0.1027   0.07    1.0

2020/10/15 11:38:03.068   43.745249007   11.292431368   -64.0611   2   6   0.1408   0.0580   0.1658  -0.0577   0.0359  -0.1008   0.07    1.9

2020/10/15 11:38:09.068   43.745238433   11.292434035   -64.0050   2   6   0.1363   0.0566   0.1627  -0.0560   0.0350  -0.0990   0.07    1.1

2020/10/15 11:38:15.068   43.745230377   11.292436913   -63.8980   2   6   0.1323   0.0553   0.1598  -0.0544   0.0342  -0.0973   0.07    1.2

2020/10/15 11:38:21.068   43.745228652   11.292439258   -63.8676   2   6   0.1286   0.0541   0.1570  -0.0529   0.0335  -0.0957   0.07    1.3

2020/10/15 11:38:27.068   43.745224795   11.292441520   -63.8227   2   6   0.1253   0.0529   0.1543  -0.0516   0.0328  -0.0942   0.07    1.1

2020/10/15 11:38:34.068   43.745218911   11.292444739   -63.9953   2   6   0.1222   0.0518   0.1518  -0.0504   0.0322  -0.0928   0.07    2.3

2020/10/15 11:38:40.068   43.745218438   11.292447546   -64.5331   2   6   0.1194   0.0508   0.1494  -0.0492   0.0316  -0.0914   0.07    1.0

2020/10/15 11:38:46.068   43.745214867   11.292448732   -64.9767   2   6   0.1170   0.0499   0.1471  -0.0484   0.0310  -0.0900   0.07    2.1

2020/10/15 11:38:52.068   43.745207694   11.292451080   -65.1364   2   6   0.1145   0.0490   0.1450  -0.0474   0.0305  -0.0888   0.07    1.6

2020/10/15 11:38:58.068   43.745204558   11.292451150   -65.2057   2   6   0.1122   0.0482   0.1429  -0.0464   0.0300  -0.0876   0.07    1.2

2020/10/15 11:39:04.068   43.745206265   11.292447041   -65.3471   2   6   0.1100   0.0474   0.1409  -0.0456   0.0295  -0.0864   0.07    2.6

2020/10/15 11:39:10.068   43.745204921   11.292445771   -65.5508   2   6   0.1080   0.0466   0.1390  -0.0448   0.0291  -0.0853   0.07    2.3

2020/10/15 11:39:17.068   43.745200520   11.292445390   -65.8156   2   6   0.1064   0.0459   0.1371  -0.0442   0.0286  -0.0842   0.07    1.0

2020/10/15 11:39:25.068   43.745196791   11.292446870   -65.6732   1   6   0.0141   0.0063   0.0205  -0.0059   0.0042  -0.0129   0.07    7.5

2020/10/15 11:39:31.068   43.745192898   11.292447003   -65.8577   2   6   0.1029   0.0446   0.1337  -0.0428   0.0279  -0.0822   0.07    2.2

2020/10/15 11:39:37.068   43.745192144   11.292447887   -65.9236   2   6   0.1012   0.0439   0.1320  -0.0421   0.0276  -0.0813   0.07    1.9

2020/10/15 11:39:43.068   43.745191371   11.292448821   -66.2033   1   6   0.0141   0.0063   0.0205  -0.0059   0.0043  -0.0130   0.07   29.7

2020/10/15 11:39:49.068   43.745193313   11.292449696   -66.5907   2   6   0.0982   0.0427   0.1289  -0.0409   0.0269  -0.0795   0.07    1.1

2020/10/15 11:39:55.068   43.745198138   11.292449984   -67.2350   1   6   0.0142   0.0063   0.0205  -0.0059   0.0043  -0.0130   0.07    3.3

2020/10/15 11:40:01.068   43.745195988   11.292452579   -67.6868   2   6   0.0955   0.0416   0.1260  -0.0398   0.0263  -0.0779   0.07    1.1

2020/10/15 11:40:07.068   43.745194796   11.292453940   -68.0674   2   6   0.0942   0.0411   0.1247  -0.0392   0.0261  -0.0771   0.07    1.5

2020/10/15 11:40:13.068   43.745194622   11.292454531   -68.3254   2   6   0.0930   0.0406   0.1233  -0.0387   0.0258  -0.0763   0.07    1.8

2020/10/15 11:40:19.068   43.745194780   11.292455399   -68.2927   1   6   0.0142   0.0063   0.0205  -0.0060   0.0044  -0.0131   0.07    7.1

2020/10/15 11:40:25.068   43.745198122   11.292453471   -67.9572   2   6   0.0907   0.0396   0.1208  -0.0378   0.0253  -0.0749   0.07    2.3

2020/10/15 11:40:31.068   43.745200144   11.292452410   -67.7198   1   6   0.0143   0.0063   0.0205  -0.0060   0.0045  -0.0131   0.07    6.3

martedì 3 novembre 2020

Matplot++ e QtCreator

Vista la necessita' di plottare in uno scatterplot 3D qualche decina di migliaia di punti ho cercato una libreria adeguata e l'ho trovata in Matplot++



Per integrarla con QtCreator (ma funziona cosi' anche con Visual Studio su Windows) dopo aver creato un progetto CMake nella root del progetto si digita

git clone https://github.com/alandefreitas/matplotplusplus/

successivamente si modifica il file CMakeLists.txt aggiungendo le righe evidenziate

add_subdirectory(matplotplusplus)

add_executable(grafico
  main.cpp
)
target_link_libraries(grafico PUBLIC matplot Qt${QT_VERSION_MAJOR}::Core)
#target_link_libraries(grafico PUBLIC matplot)

per la visualizzazione e' necessario che sia installato gnuplot



lunedì 2 novembre 2020

Coreboot + Trisquel

 Per provare l'ebbrezza di un calcolatore libero sia dal punto di vista hardware che software da materiale proprietario mi sono comprato un Lenovo T400 con il Bios modificato e sostituito con Coreboot e con Trisquel come sistema operativo


pensavo che avrei avuto un sacco di limitazioni nell'uso normale ma devo ammettere che non ho limitazioni sostanziali nell'uso del calcolatore




Primi passi con Point Cloud Library

Con l'arrivo del sensore lidar sugli IPhone (rip Project Tango) forse e' il caso di ritirare fuori le librerie per trattare le nuvole di punti come PCL 

Per installare la liberia su Debian e' sufficiente

apt-get install libpcl-dev

che si porta dietro un po' di dipendenze come boost

I primi passi sono ovviamente quelli di inserire la libreria in un progetto. Per questo sono partito da QTCreator con le varie opzioni di Make

Compilazione con CMake

per la compilazione si devono aggiungere le righe in giallo al file CMakeLists.txt. Attenzione che in PCL le funzioni sono distribuite in vari moduli che devono essere specificati in REQUIRED COMPONENTS

cmake_minimum_required(VERSION 3.5)

project(nuvola LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable(nuvola main.cpp)

target_link_libraries(nuvola ${PCL_LIBRARIES})

Compilazione con QMake

Usando qmake, come prima, si devono modificare le righe in giallo.  Si deve anche specificare  c++14 

TEMPLATE = app
CONFIG += console c++14
CONFIG -= app_bundle
CONFIG -= qt
CONFIG += link_pkgconfig
PKGCONFIG += eigen3
INCLUDEPATH += /usr/include/pcl-1.11
LIBS += -L/usr/lib/x86_64-linux-gnu -lpcl_common -lpcl_io -lpcl_features -lpcl_search
QT += widgets

SOURCES += \
        main.cpp

Compilazione con Make

Per verificare quali sono gli switch di compilazione necessari per usare make si puo'usare

pkg-config --cflags --libs pcl_commons-1.7

anche in questo caso si deve ripetere l'operazione per ogni modulo utilizzato

Di seguito un semplice esempio di come leggere un file PCD, calcolare le normali e stampare i dati a schermo

===========================================

#include <iostream>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

#include <pcl/features/normal_3d.h>

using namespace std;

int main()
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

     if (pcl::io::loadPCDFile<pcl::PointXYZ> ("/home/luca/gabriele.pcd", *cloud) == -1) //* load the file
     {
       PCL_ERROR ("Couldn't read file test_pcd.pcd \n");
       return (-1);
     }
     //std::cout << "Loaded " << cloud->width * cloud->height << " data points from test_pcd.pcd with the following fields: " << std::endl;
     /*for (size_t i = 0; i < cloud->points.size (); ++i)
       std::cout << "    " << cloud->points[i].x << " "    << cloud->points[i].y << " "    << cloud->points[i].z << std::endl;
    */
     // Create the normal estimation class, and pass the input dataset to it

     pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
     ne.setInputCloud (cloud);
    // Create an empty kdtree representation, and pass it to the normal estimation object.
       // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
       pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
       ne.setSearchMethod (tree);

       // Output datasets
       pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

       // Use all neighbors in a sphere of radius 3cm
       ne.setRadiusSearch (0.03);

       // Compute the features
       ne.compute (*cloud_normals);

       //std::cout << endl << cloud_normals->size() << endl;


       // cloud_normals->size () should have the same size as the input cloud->size ()*

       int i = 0;
       for (pcl::Normal n : *cloud_normals) {
           //std::cerr << i << " n = " << n.normal_x << ", " << n.normal_y << ", " << n.normal_z << "\n";
           std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z << "\n";
           i++;
       }



    return 0;
}




sabato 24 ottobre 2020

Intel TV Sport 1004

 Il mio piccolo museo dell'informatico ha un nuovo ingresso. Un modello di Pong Intel TV Sport 1004 comprato per ben due euro ad un mercatino. Intel non e' la famosa ditta id processori ma un oscuro produttore tedesco 

L'alimentazione riporta un 9V AC (???). Non ho avuto il coraggio di inserire un trasformatore ... troppo strana una corrente alternata per un dispositivo di questo tipo...anche se il C64 aveva in effetti due alimentazioni di una in AC...ho inserito le batterie e....non funzionava. Ho lasciato distrattamente il pulsante su ON e lo ho appoggiato su un tavolo...dopo circa 10 minuti ha iniziato ad emettere il suono della pallina che rimbalza ...non e' completamente morto

Putroppo lo switch dell'angolo di rimbalzo e' rotto

Incredibilmente dentro al vano batterie ho trovato il cavo RF originale










venerdì 23 ottobre 2020

Hackathon Devs4Health

 Ecco il motivo di silenzio del blog, da Luglio sono stato impegnato nell'Hackathon Devs4Health di Codemotion. Rispetto al Hackathon di Tim questa volta la mia squadra non ha vinto ma abbiamo avuto in ogni caso una menzione speciale e la possibilita' di partecipare ai bootcamps

Per i piu' curiosi il progetto e' disponibile su https://www.pgpmedicalcard.it







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