Πέμπτη 27 Φεβρουαρίου 2020

Κατασκευή DIY RTK ROVER V.3

Κατασκευή και Προγραμματισμός ενός  RTK ROVER

Πριν από 2 περίπου χρόνια είχα κατασκευάσει ένα RTK BASE-ROVER μονής συχνότητας που βασιζόταν την επίλυση από RTKNAVI του προγράμματος RTKLIB με χρήση 2 raspberry zero.
Ήρθε η ώρα να κάνουμε το επόμενο βήμα και να κατασκευάσουμε ένα πλήρη δέκτη GPS με τις δυνατότητες και την ακρίβεια των εταιρικών μοντέλων.

Στην προσπάθεια αυτή τον κύριο ρόλο παίζει ο δέκτης GPS. Έγινε επιλογή του νέου δέκτη zed-f9p της UBLOX ο οποίος έχει τα παρακάτω χαρακτηριστικά :

184 κανάλια GPS L1/L2 -  GLONASS L1/L2 - GALILEO E1/E5 - BDS B1/B2 και QZSS L1/L2
RTK 20 hz
INPUT : UBX - RTCM 3.x
OUTPUT : NMEA - UBX - RTCM 3.x

Αποκτήθηκε μία μονάδα η οποία έχει USB και UART με κόστος 210 € :
Από το πίσω μέρος της μονάδας επιλέχτηκαν οι ακροδέκτες του UART και έγιναν οι ανάλογες κολλήσεις. Η έξοδος του UART συνδέθηκε με USB to UART προκειμένου να έχουμε δύο θύρες USB στη μονάδα του GPS. 

 Ως κεραία επιλέχθηκε μία Hg-Goyh7201 Rtk Cors Station Gnss Antenna στα 70 περίπου ευρώ.


Προχωράμε στα μικρο-υλικά :

Διακόπτης ON-OFF χωνευτός :
LED:
4 πράσινου χρώματος και ένα κόκκινου.
Στο κόκκινο led μπήκε σε σειρά μία αντίσταση 100 Ohm καθώς η τάση που θέλει το κόκκινο χρώμα είναι μικρότερη






Ένα RGB LED με 4 "πόδια". Ομοίως στο κόκκινο μπήκε αντίσταση 100 Ohm.



και ένα power bank  5000 mah και πάνω.

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ του  zed-f9p



Μέσω του προγράμματος U-CENTER της UBLOX έγινε ο προγραμματισμός του δέκτη GPS. Η έξοδος USB ορίστηκε σε έξοδος NMEA και η έξοδος UART σε είσοδο RTCM3 και έξοδο NMEA.

Η ώρα του RASPBERRY

Στο project έγινε χρήση ενός rasperry pi 3 B+ αν και μπορεί να χρησιμοποιηθεί και το 4 και πιθανόν το zero w.
Αρχικά έγινε εγκατάσταση του Raspian
Στη συνέχεια από εδώ κατεβάσαμε το RTKLIB και εγκαταστήσαμε το str2str με τις παρακάτω εντολές :
 

cd rtklib/app/str2str/gcc
make
sudo cp str2str /usr/local/bin/str2str
sudo chmod +x /usr/local/bin/str2str

ενώ έγινε και εγκατάσταση του SOCAT με τις παρακάτω εντολές :

sudo apt-get update
sudo apt-get install socat

ενώ έγινε και εγκατάσταση του προγράμματος interceptty

Δημιουργούμε τους κανόνες tty για να αναφερόμαστε στη συσκευή GPS.
 

sudo nano /etc/udev/rules.d/10-myrules.rules

Αυτή η εντολή μας ανοίγει ένα επεξεργαστή κειμένου στον οποίο προσθέτουμε τα παρακάτω :

SUBSYSTEM=="tty",ATTRS{idVendor}=="10c4",ATTRS{idProduct}=="ea60",SYMLINK+="ttyLINK"
SUBSYSTEM=="tty",ATTRS{idVendor}=="1546",ATTRS{idProduct}=="01a9",SYMLINK+="ttyGPS"

και κάνουμε επανεκκίνηση.

Αντιγράφουμε στο φάκελο home/pi τα δύο αρχεία https://drive.google.com/open?id=1xhpxjulYX6JQZgqEnb0EyncVrcqrNZPI και https://drive.google.com/open?id=1k3pGt2w3cgzWnqCMMeU4weewsJ06RVeD 
όπου στο αρχείο rtk2.sh αντικαθιστούμε τα USERNAME , PASSWORD , IP-ADDRESS , MOUND με τα δικά μας από το δίκτυο διορθώσεων που έχουμε. Καλό είναι να βάλουμε στο MOUND το moundpoint που μας δίνει VRS

Τέλος αφού κάνουμε τα αρχεία εκτελέσιμα με την εντολή chmod τα βάζουμε να εκτελούνται αυτόματα με την εκκίνηση κάνοντας τις ανάλογες αλλαγές στο lxsession/lxde-pi/autostart

Τελειώνοντας προσθέτουμε τον κώδικα python σε ένα νέο αρχείο RTK.py στο path  /home/pi :

------------------------------------------------------------------------------------------------------------------------
 import os
import serial
import pynmea2
from pygame import mixer
from gpiozero import LED
import time
mixer.init()
alert=mixer.Sound('/home/pi/Wellcome.wav')
alert.play()
time.sleep(1.5)
duration = alert.get_length()
time.sleep(duration)
port = "/dev/ttyDUMMY"
Fix=0
kNO = 0
kAUTO = 0
kFIX = 0
kFLOAT = 0
kDGPS = 0
kkk=0
# serialPort = serial.Serial(port, baudrate = 38400 )
red = LED(23)
blue = LED(24)
green = LED(25)
sat4 = LED(26)
sat6 = LED(19)
sat8 = LED(13)
sat10 = LED(6)
sat12 = LED(5)
sat4.off()
sat6.off()
sat8.off()
sat10.off()
sat12.off()
OK =  0
red.on()
sat4.on()
OP = 0

while OK < 1 :
    try:
         serialPort = serial.Serial(port, baudrate = 38400 )
         OK = 1
    except serial.SerialException:
         # print ("WAIT",OP)
         if OP < 100000 :
              OP = OP + 1
         else :
              alert=mixer.Sound('/home/pi/STOP.wav')
              alert.play()
              time.sleep(1.5)
              duration = alert.get_length()
              time.sleep(duration)
              os.system('sudo reboot')
             
serialPort.reset_input_buffer()
k3=0

while True:
    str = serialPort.readline()
    #print (str)

    if str.find('GGA') > 0 and k3 > 50 :
        serialPort.reset_input_buffer()
        print ('------------------------------------------------------------------------------------------------------')
        k3=0
    else :
        k3=k3+1

    if str.find('GGA') > 0 :
        try:
             msg = pynmea2.parse(str)
             Fix = msg.gps_qual
             kkk=kkk+1
             sat1= msg.num_sats

             if sat1 == "00" or sat1 == "0" or sat1 == "":
                 sat = 0
                 sat4.off()
                 sat6.off()
                 sat8.off()
                 sat10.off()
                 sat12.off()
             else :
                 sat = int(float(sat1))

             if sat >0 and sat <= 4 :
                 sat4.on()
                 sat6.off()
                 sat8.off()
                 sat10.off()
                 sat12.off()

         if sat > 4 and sat <= 6 :
                 sat4.on()
                 sat6.on()
                 sat8.off()
                 sat10.off()
                 sat12.off()

         if sat > 6 and sat <= 8 :
                 sat4.on()
                 sat6.on()
                 sat8.on()
                 sat10.off()
                 sat12.off()

         if sat > 8 and sat <= 10 :
                 sat4.on()
                 sat6.on()
                 sat8.on()
                 sat10.on()
                 sat12.off()

         if sat > 10  :
                 sat4.on()
                 sat6.on()
                 sat8.on()
                 sat10.on()
                 sat12.on()

             if Fix == 0 and kNO == 0 and kkk >= 30 :
                 print ("No Possition")
                 red.on()
                 blue.off()
                 green.off()
                 alert=mixer.Sound('/home/pi/NO.wav')
                 alert.play()
                 time.sleep(1.5)
                 duration = alert.get_length()
                 time.sleep(duration)
                 kNO = 1
                 kAUTO = 0
                 kFIX = 0
                 kFLOAT = 0
                 kDGPS = 0
                 #serialPort.reset_input_buffer()


             if Fix == 1 and kAUTO == 0:
                 print ("AUTO")
                 red.on()
                 blue.off()
                 green.off()
                 alert=mixer.Sound('/home/pi/AUTO.wav')
                 alert.play()
                 time.sleep(1.5)
                 duration = alert.get_length()
                 time.sleep(duration)
                 kNO=0
                 kAUTO = 1
                 kFIX = 0
                 kFLOAT = 0
                 kDGPS = 0
                 #serialPort.reset_input_buffer()

             if Fix == 2 and kDGPS == 0:
                 print ("DGPS")
                 red.on()
                 blue.off()
                 green.off()
                 alert=mixer.Sound('/home/pi/DGPS.wav')
                 alert.play()
                 time.sleep(1.5)
                 duration = alert.get_length()
                 time.sleep(duration)
                 kNO = 0
                 kAUTO = 0
                 kFIX = 0
                 kFLOAT = 0
                 kDGPS = 1
                 #serialPort.reset_input_buffer()

             if Fix == 5 and kFLOAT == 0:
                 print ("float")
                 red.off()
                 blue.on()
                 green.off()
                 alert=mixer.Sound('/home/pi/Float.wav')
                 alert.play()
                 time.sleep(1.5)
                 duration = alert.get_length()
                 time.sleep(duration)
                 kNO = 0
                 kAUTO = 0
                 kFIX = 0
                 kFLOAT = 1
                 kDGPS = 0
                 #serialPort.reset_input_buffer()

             if Fix == 4 and kFIX == 0:
                 print ("FIX")
                 red.off()
                 blue.off()
                 green.on()
                 alert=mixer.Sound('/home/pi/fix.wav')
                 alert.play()
                 time.sleep(1.5)
                 duration = alert.get_length()
                 time.sleep(duration)
                 kNO = 0
                 kAUTO = 0
                 kFIX = 1
                 kFLOAT = 0
                 kDGPS = 0
                 #serialPort.reset_input_buffer()

             print "Timestamp: %s -- Lat: %s %s -- Lon: %s %s -- Altitude: %s %s -- Satellites: %s -- GPS : %s " % (msg.timestamp,msg.lat,msg.lat_dir,msg.lon,msg.lon_dir,msg.altitude,msg.altitude_units,msg.num_sats,msg.gps_qual)
        except pynmea2.nmea.ChecksumError:
             print('')
             serialPort.reset_input_buffer()
        except AttributeError:
             print('')
             serialPort.reset_input_buffer()
        except pynmea2.nmea.ParseError:
             print('Unable to parse data, trying again.')
             serialPort.reset_input_buffer()

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

Για να μην γίνει λάθος αντιγραφή ολόκληρο το αρχείο βρίσκεται εδώ : https://drive.google.com/open?id=16iE48zcua7CmS0tKcVVePEtOPiiQIQRU

Θα πρέπει να εγκαταστήσουμε φυσικά τις δυνατότητες pyserial , pygame , gpozero και pynmea2 του Python πριν εκτελέσουμε το αρχείο.

Τέλος στο path  /home/pi θα πρέπει να βάλουμε κάποια αρχεία wav.
το Wellcome.wav που θα παίζει με το ξεκίνημα.
το STOP.wav που παίζει όταν υπάρχει γενικό σφάλμα
το NO.wav όταν δεν υπάρχει σήμα GPS
το AUTO.wav όταν δεν υπάρχει RTK λύση.
το DGPS.wav για απλή διαφορική λύση.
το Float.wav για λύση Float
το fix.wav για fix λύση.
Για την δημιουργεία τους μπορείτε να κάνετε ηχογράφιση ή έτοιμους ήχους.

Φυσικά για να παίξει ο ήχος θέλει ....ηχεία.

Τέλος τα συνδέουμε ολα μαζί ως παρακάτω:



ΤΟ ΚΟΥΤΙ

Για το κουτί έγινε 3D σχεδιασμός στα κομάτια που βλέπουμε παρακάτω :






και ..... εκτύπωση σε 3D Printer


έγιναν οι συνέσεις των επιμέρους στοιχείων :
Τοποθετήθηκαν όλα στο κουτί και το τελικό αποτέλεσμα ήταν :



Για την λειτουργεία του έγινε χρήση ενός Android Tablet το οποίο λειτουγεί και ως hotspot για το GPS και με χρήση του προγράμματος για καταγραφή MyRTK STD της TopoDeSia.
Φυσικά είναι δυνατό να φτιαχτεί πρόγραμμα καταγρφής σε Android με χρήση python και το pnmea2 αλλά δεν θα ανακαλύψουμε τον τροχό από την αρχή.

ΔΟΚΙΜΗ ΑΚΡΙΒΕΙΑΣ

Για την δοκιμή του οργάνου έγινε μέτρηση σε τρία τριγωνομετρικά ΓΥΣ με υπολογισμό μέσου όρου μετρήσεων 120 sec σε κάθε τριγωνομετρικό. Για την ορθότητα των υπολογισμών το moundpoint ορίστικε σε RTK (σταθερή βάση) και όχι VRS. Υπολογίστικαν οι αποστάσεις απο τις συνεταγμένες των τριγωνομετρικών σε ΕΓΣΑ και έγινε σύγκριση με τις αποστάσεις των συντεταγμένων του GPS. Η αποκλιση που υπολογίστικε ήταν 0.012 m + 1 ppm που συμπίτει περίπου με την ακρίβεια του Product summary του zed-f9p που είναι 0.01 m + 1 ppm.

Με Λίγα Λόγια :

με κλίμακα 1-5
Δυσκολία κατασκευής κυκλώματος : 2
Δυσκολία προγραμματισμού : 3
Δυσκολία μονταρίσματος : 3
Τελικό αποτέλεσμα ..... αναμένεται.

Δεν υπάρχουν σχόλια: