sabato 21 novembre 2020

Iphone 12 Pro per la geologia

Avevo provato tempo fa ad usare il tablet Google Tango per scansionare un affioramento roccioso

Adesso che anche Iphone 12 pro (e Ipad Pro 2020) ha un lidar volevo vedere la possibilita' di usare il telefono di Apple per applicazione geologiche



 
Mettendo a confronto Tango con Iphone si nota che entrambi hanno un raggio di scansione massimo tra i 3 ed i 4 metri ma sicuramente la IMU di Iphone e' migliore in quanto spostandosi non decorrela in modo significativo anche su lunghezze superiori ai 10 m

Scansione con Iphone

Scansione con Google Tango Tablet


La cosa che mi impressiona sempre e' come sia possibile ottenere delle misure di lunghezza senza la necessita' di ricalibrare i dati ma questo aspetto e' comune sia a Tango che ad IPhone

Misura di strato : a sinistra di misura reale, a destra misura dal mesh


Non esiste una applicazione ufficiale per esportare i cloudpoint o la mesh da IPhone ma sono disponibili 3DScanner App, PolyCam e Heges



I dati comunque sono leggibili ed elaboraibili da CloudCompare 

giovedì 19 novembre 2020

Compilare VTK su WIndows

 Una volta spacchettato il file tar.gz per esempio c:\vtk\src. Si crea una directory build e con CMake-GUI si indica la sorgente in src e ovviamente la build con build usando come target il compilatore di Visual Studio 2019 (viene richiesta la scelta dopo aver premuto Configure)

Al termine si clicca Generate  e si aprono gli strumenti di Visual Studio/Developer Command Prompt, si entra nella directory dove e' stato creato il file progetto 



e si digita

msbuild /p:Configuration=Debug ALL_BUILD.vcxproj

la compilazione richiede abbastanza tempo

PCL Library e Visual Studio 2019

 Per installare ed usare PCL su Windows la cosa piu' comoda e' utilizzare il pacchetto PCL-1.11.1-AllInOne-msvc2019-win64 

Una volta installato il file exe per esempio in C:\Program Files\PCL 1.11.1


Per creare un progetto in QT con le PCL si devono aggiungere nella path OpenNI2 (stranamente l'installer non la ha aggiunta nella PATH)

Ho sostituito la versione di CMAKE da quella di default di QT con l'installer di CMake  (modificabile dai Kit di Qt) ed come compilatore ho selezionato MSVC2019



il file CMAKE e' il seguente

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

cmake_minimum_required(VERSION 3.5)


project(t3 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(t3 main.cpp)
target_link_libraries(t3 ${PCL_LIBRARIES})

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

mentre un programma di esempio e' 
===============================================
#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> ("c://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;
}


venerdì 13 novembre 2020

Riparazione Soundic Pong

Questo modello di Pong Soundic anno 1979 a colori lo ho comprato come rotto in quanto i controller non rispondevano 


Di fatto pero' i controller sono dei semplici potenziometri


E' stato sufficiente un pulisci contatti a secco 


Pensavo che fosse rotto perche' ad un certo punto nonostante colpissi la pallina la racchetta era come trasparente. Un amico mi ha detto che e' un comportamento normale, a 15 punti la partita e' terminata e nonostante il gioco continui non si puo' piu' colpire la palla



domenica 8 novembre 2020

Misurazione lunghezze ed angoli con CloudCompare

 Per misurare le lunghezze e gli angoli di una nuvola di punti su CloudCompare si clicchi sul pulsante cerchiato in rosso nell'immagine successiva


Si apre il menu

La seconda icona permette di misurare le distanze, la terza di calcolare gli angoli



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

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