Πίνακας περιεχομένων:

Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV: 7 βήματα (με εικόνες)
Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV: 7 βήματα (με εικόνες)

Βίντεο: Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV: 7 βήματα (με εικόνες)

Βίντεο: Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV: 7 βήματα (με εικόνες)
Βίντεο: Part 1 - Ann Veronica Audiobook by H. G. Wells (Chs 01 -03) 2024, Ιούλιος
Anonim
Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV
Αυτόνομο αυτοκίνητο που διατηρεί τη λωρίδα χρησιμοποιώντας Raspberry Pi και OpenCV

Σε αυτές τις οδηγίες, θα εφαρμοστεί ένα αυτόνομο ρομπότ διατήρησης λωρίδας και θα περάσει από τα ακόλουθα βήματα:

  • Συγκέντρωση ανταλλακτικών
  • Εγκατάσταση προαπαιτούμενων λογισμικού
  • Συναρμολόγηση υλικού
  • Πρώτη Δοκιμή
  • Ανίχνευση γραμμών λωρίδας κυκλοφορίας και εμφάνιση γραμμής καθοδήγησης με χρήση openCV
  • Εφαρμογή ελεγκτή PD
  • Αποτελέσματα

Βήμα 1: Συγκέντρωση εξαρτημάτων

Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων
Συγκέντρωση εξαρτημάτων

Οι παραπάνω εικόνες δείχνουν όλα τα στοιχεία που χρησιμοποιούνται σε αυτό το έργο:

  • RC car: Πήρα το δικό μου από ένα τοπικό κατάστημα στη χώρα μου. Είναι εξοπλισμένο με 3 κινητήρες (2 για γκάζι και 1 για το τιμόνι). Το κύριο μειονέκτημα αυτού του αυτοκινήτου είναι ότι το τιμόνι περιορίζεται μεταξύ "χωρίς τιμόνι" και "πλήρους τιμονιού". Με άλλα λόγια, δεν μπορεί να κατευθύνει σε συγκεκριμένη γωνία, σε αντίθεση με τα σερβο-τιμόνι RC αυτοκίνητα. Μπορείτε να βρείτε παρόμοιο κιτ αυτοκινήτου σχεδιασμένο ειδικά για raspberry pi από εδώ.
  • Raspberry pi 3 μοντέλο b+: αυτός είναι ο εγκέφαλος του αυτοκινήτου που θα χειριστεί πολλά στάδια επεξεργασίας. Βασίζεται σε τετραπύρηνο επεξεργαστή 64-bit χρονισμένο στα 1,4 GHz. Πήρα το δικό μου από εδώ.
  • Υπομονάδα κάμερας Raspberry pi 5 mp: Υποστηρίζει 1080p @ 30 fps, 720p @ 60 fps και εγγραφή 640x480p 60/90. Υποστηρίζει επίσης σειριακή διεπαφή που μπορεί να συνδεθεί απευθείας στο raspberry pi. Δεν είναι η καλύτερη επιλογή για εφαρμογές επεξεργασίας εικόνας, αλλά είναι αρκετή για αυτό το έργο καθώς είναι πολύ φθηνή. Πήρα το δικό μου από εδώ.
  • Πρόγραμμα οδήγησης κινητήρα: Χρησιμοποιείται για τον έλεγχο των κατευθύνσεων και των ταχυτήτων των κινητήρων DC. Υποστηρίζει τον έλεγχο 2 κινητήρων DC σε 1 πλακέτα και μπορεί να αντέξει 1,5 Α.
  • Power Bank (προαιρετικό): Χρησιμοποίησα ένα power bank (βαθμολογίας 5V, 3A) για να τροφοδοτήσω το raspberry pi ξεχωριστά. Θα πρέπει να χρησιμοποιηθεί ένας μετατροπέας βήμα προς τα κάτω (μετατροπέας buck: ρεύμα εξόδου 3Α) για να τροφοδοτήσει το βατόμουρο pi από 1 πηγή.
  • Μπαταρία LiPo 3s (12 V): Οι μπαταρίες πολυμερών λιθίου είναι γνωστές για την εξαιρετική τους απόδοση στον τομέα της ρομποτικής. Χρησιμοποιείται για την τροφοδοσία του οδηγού κινητήρα. Αγόρασα το δικό μου από εδώ.
  • Καλώδια από αρσενικό σε αρσενικό και θηλυκό σε θηλυκό.
  • Ταινία διπλής όψης: Χρησιμοποιείται για την τοποθέτηση των εξαρτημάτων στο αυτοκίνητο RC.
  • Μπλε ταινία: Αυτό είναι ένα πολύ σημαντικό συστατικό αυτού του έργου, χρησιμοποιείται για να κάνει τις δύο γραμμές λωρίδων στις οποίες το αυτοκίνητο θα κινείται μεταξύ τους. Μπορείτε να επιλέξετε οποιοδήποτε χρώμα θέλετε, αλλά προτείνω να επιλέξετε χρώματα διαφορετικά από αυτά του περιβάλλοντος.
  • Φερμουάρ και ξύλινες μπάρες.
  • Κατσαβίδι.

Βήμα 2: Εγκατάσταση του OpenCV στο Raspberry Pi και ρύθμιση απομακρυσμένης οθόνης

Εγκατάσταση OpenCV στο Raspberry Pi και ρύθμιση απομακρυσμένης οθόνης
Εγκατάσταση OpenCV στο Raspberry Pi και ρύθμιση απομακρυσμένης οθόνης

Αυτό το βήμα είναι λίγο ενοχλητικό και θα πάρει λίγο χρόνο.

Το OpenCV (Open Source Computer Vision) είναι μια βιβλιοθήκη λογισμικού οπτικής και μηχανικής μάθησης ανοιχτού κώδικα. Η βιβλιοθήκη διαθέτει πάνω από 2500 βελτιστοποιημένους αλγόριθμους. Ακολουθήστε αυτόν τον πολύ απλό οδηγό για να εγκαταστήσετε το openCV στο raspberry pi σας καθώς και να εγκαταστήσετε το raspberry pi OS (αν δεν το κάνατε ακόμα). Λάβετε υπόψη ότι η διαδικασία κατασκευής του openCV μπορεί να διαρκέσει περίπου 1,5 ώρες σε ένα καλά δροσισμένο δωμάτιο (αφού η θερμοκρασία του επεξεργαστή θα γίνει πολύ υψηλή!), Οπότε πιείτε λίγο τσάι και περιμένετε υπομονετικά: D.

Για την απομακρυσμένη οθόνη, ακολουθήστε επίσης αυτόν τον οδηγό για να ρυθμίσετε την απομακρυσμένη πρόσβαση στο raspberry pi από τη συσκευή σας Windows/Mac.

Βήμα 3: Σύνδεση εξαρτημάτων μαζί

Συνδέοντας μέρη μαζί
Συνδέοντας μέρη μαζί
Συνδέοντας μέρη μαζί
Συνδέοντας μέρη μαζί
Συνδέοντας μέρη μαζί
Συνδέοντας μέρη μαζί

Οι παραπάνω εικόνες δείχνουν τις συνδέσεις μεταξύ raspberry pi, μονάδας κάμερας και οδηγού κινητήρα. Λάβετε υπόψη ότι οι κινητήρες που χρησιμοποίησα απορροφούν 0,35 A στα 9 V ο καθένας, γεγονός που καθιστά ασφαλή για τον οδηγό του κινητήρα να λειτουργεί 3 κινητήρες ταυτόχρονα. Και επειδή θέλω να ελέγξω την ταχύτητα των 2 κινητήρων πεταλούδας (1 πίσω και 1 εμπρός) ακριβώς με τον ίδιο τρόπο, τους ένωσα στην ίδια θύρα. Τοποθέτησα τον οδηγό του κινητήρα στη δεξιά πλευρά του αυτοκινήτου χρησιμοποιώντας διπλή ταινία. Όσον αφορά τη μονάδα κάμερας, έβαλα μια φερμουάρ ανάμεσα στις οπές των βιδών, όπως δείχνει η παραπάνω εικόνα. Στη συνέχεια, τοποθετώ την κάμερα σε μια ξύλινη ράβδο, ώστε να μπορώ να προσαρμόσω τη θέση της κάμερας όπως θέλω. Προσπαθήστε να εγκαταστήσετε την κάμερα στη μέση του αυτοκινήτου όσο το δυνατόν περισσότερο. Σας συνιστώ να τοποθετήσετε την κάμερα τουλάχιστον 20 cm πάνω από το έδαφος, έτσι ώστε το οπτικό πεδίο μπροστά από το αυτοκίνητο να βελτιωθεί. Το σχήμα Fritzing επισυνάπτεται παρακάτω.

Βήμα 4: Πρώτη δοκιμή

Πρώτη Δοκιμή
Πρώτη Δοκιμή
Πρώτη Δοκιμή
Πρώτη Δοκιμή

Δοκιμή κάμερας:

Μόλις εγκατασταθεί η κάμερα και δημιουργηθεί η βιβλιοθήκη openCV, ήρθε η ώρα να δοκιμάσουμε την πρώτη μας εικόνα! Θα τραβήξουμε μια φωτογραφία από το pi cam και θα την αποθηκεύσουμε ως "original.jpg". Μπορεί να γίνει με 2 τρόπους:

1. Χρήση εντολών τερματικού:

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

raspistill -o original.jpg

Αυτό θα πάρει μια ακίνητη εικόνα και θα την αποθηκεύσει στον κατάλογο "/pi/original.jpg".

2. Χρησιμοποιώντας οποιοδήποτε python IDE (χρησιμοποιώ το IDLE):

Ανοίξτε ένα νέο σκίτσο και γράψτε τον ακόλουθο κώδικα:

εισαγωγή cv2

video = cv2. VideoCapture (0) while True: ret, frame = video.read () frame = cv2.flip (frame, -1) # χρησιμοποιείται για να αναστρέψει κάθετα την εικόνα cv2.imshow («αρχικό», καρέ) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Ας δούμε τι συνέβη σε αυτόν τον κώδικα. Η πρώτη γραμμή είναι η εισαγωγή της βιβλιοθήκης openCV για χρήση όλων των λειτουργιών της. η λειτουργία VideoCapture (0) ξεκινά τη ροή ενός ζωντανού βίντεο από την πηγή που καθορίζεται από αυτήν τη λειτουργία, στην περίπτωση αυτή είναι 0 που σημαίνει κάμερα raspi. εάν έχετε πολλές κάμερες, πρέπει να τοποθετήσετε διαφορετικούς αριθμούς. Το video.read () θα διαβάσει κάθε καρέ που προέρχεται από την κάμερα και θα το αποθηκεύσει σε μια μεταβλητή που ονομάζεται "πλαίσιο". Η λειτουργία flip () θα αναστρέψει την εικόνα σε σχέση με τον άξονα y (κάθετα) αφού τοποθετώ την κάμερα μου αντίστροφα. Το imshow () θα εμφανίσει τα καρέ μας με επικεφαλίδα τη λέξη "πρωτότυπο" και το imwrite () θα αποθηκεύσει τη φωτογραφία μας ως original.jpg. waitKey (1) θα περιμένει 1 ms για να πατηθεί οποιοδήποτε κουμπί πληκτρολογίου και επιστρέφει τον κωδικό ASCII. αν πατηθεί το κουμπί διαφυγής (esc), επιστρέφεται μια δεκαδική τιμή 27 και θα σπάσει αναλόγως τον βρόχο. Το video.release () θα σταματήσει την εγγραφή και θα καταστρέψει τοAllWindows () θα κλείσει κάθε εικόνα που ανοίγει με τη λειτουργία imshow ().

Σας συνιστώ να δοκιμάσετε τη φωτογραφία σας με τη δεύτερη μέθοδο για να εξοικειωθείτε με τις λειτουργίες openCV. Η εικόνα αποθηκεύεται στον κατάλογο "/pi/original.jpg". Η αρχική φωτογραφία που τράβηξε η κάμερα μου φαίνεται παραπάνω.

Δοκιμές κινητήρων:

Αυτό το βήμα είναι απαραίτητο για τον προσδιορισμό της κατεύθυνσης περιστροφής κάθε κινητήρα. Αρχικά, ας κάνουμε μια σύντομη εισαγωγή σχετικά με την αρχή λειτουργίας ενός οδηγού κινητήρα. Η παραπάνω εικόνα δείχνει το pin-out του οδηγού κινητήρα. Ενεργοποίηση A, Είσοδος 1 και Είσοδος 2 σχετίζονται με το χειριστήριο κινητήρα Α. Ενεργοποίηση B, Είσοδος 3 και Είσοδος 4 σχετίζονται με τον έλεγχο κινητήρα Β. Ο έλεγχος κατεύθυνσης καθορίζεται από το τμήμα "Εισαγωγή" και ο έλεγχος ταχύτητας από το τμήμα "Ενεργοποίηση". Για να ελέγξετε την κατεύθυνση του κινητήρα Α για παράδειγμα, ορίστε την Είσοδο 1 σε Υ HIGHΗΛΗ (3,3 V σε αυτήν την περίπτωση επειδή χρησιμοποιούμε βατόμουρο pi) και ρυθμίστε την Είσοδο 2 σε ΧΑΜΗΛΗ, ο κινητήρας θα περιστρέφεται προς μια συγκεκριμένη κατεύθυνση και ορίζοντας τις αντίθετες τιμές στην είσοδο 1 και στην είσοδο 2, ο κινητήρας θα περιστρέφεται προς την αντίθετη κατεύθυνση. Εάν η Είσοδος 1 = Είσοδος 2 = (Υ HIGHΗΛΗ ή ΧΑΜΗΛΗ), ο κινητήρας δεν θα γυρίσει. Ενεργοποιήστε τους πείρους να λάβετε ένα σήμα εισόδου Pulse Width Modulation (PWM) από το βατόμουρο (0 έως 3,3 V) και να λειτουργήσετε ανάλογα τους κινητήρες. Για παράδειγμα, ένα σήμα 100% PWM σημαίνει ότι εργαζόμαστε στη μέγιστη ταχύτητα και το σήμα 0% PWM σημαίνει ότι ο κινητήρας δεν περιστρέφεται. Ο ακόλουθος κώδικας χρησιμοποιείται για τον προσδιορισμό των κατευθύνσεων των κινητήρων και τον έλεγχο των στροφών τους.

χρόνο εισαγωγής

εισαγωγή RPi. GPIO ως GPIO GPIO.setwarnings (False) # Steering Motor Pins steering_enable = 22 # Physical Pin 15 in1 = 17 # Physical Pin 11 in2 = 27 # Physical Pin 13 #Throttle Motors Pins throttle_enable = 25 # Physical Pin 22 in3 = 23 # Physical Pin 16 in4 = 24 # Physical Pin 18 GPIO.setmode (GPIO. BCM) # Χρησιμοποιήστε αρίθμηση GPIO αντί φυσικής αρίθμησης GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. setup (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (steering_enable, GPIO.out) # Steering Motor Control GPIO.output (in1, GPIO Υ HIGHΗΛΟ) GPIO.output (in2, GPIO. LOW) τιμόνι = GPIO. PWM (steering_enable, 1000) # ρυθμίστε τη συχνότητα μεταγωγής σε τιμόνι διεύθυνσης 1000 Hz. Στάση () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # ρυθμίστε τη συχνότητα μεταγωγής σε 1000 Hz γκάζι. σταματήστε () time.sleep (1) γκάζι. ξεκινήστε (25) # εκκινεί τον κινητήρα στις 25 % PWM σήμα-> (0,25 * Τάση μπαταρίας) - του οδηγού απώλεια τιμονιού. εκκίνηση (100) # εκκινεί τον κινητήρα σε 100% σήμα PWM-> (1 * Τάση μπαταρίας) - χρόνος απώλειας του οδηγού. ύπνος (3) γκάζι. διακοπή () steering.stop ()

Αυτός ο κωδικός θα λειτουργήσει τους κινητήρες πεταλούδας και τον κινητήρα διεύθυνσης για 3 δευτερόλεπτα και στη συνέχεια θα τους σταματήσει. Η (απώλεια οδηγού) μπορεί να προσδιοριστεί χρησιμοποιώντας ένα βολτόμετρο. Για παράδειγμα, γνωρίζουμε ότι ένα σήμα 100% PWM πρέπει να δίνει την πλήρη τάση της μπαταρίας στον ακροδέκτη του κινητήρα. Αλλά, ρυθμίζοντας το PWM στο 100%, διαπίστωσα ότι ο οδηγός προκαλεί πτώση 3 V και ο κινητήρας παίρνει 9 V αντί 12 V (ακριβώς αυτό που χρειάζομαι!). Η απώλεια δεν είναι γραμμική, δηλαδή η απώλεια στο 100% είναι πολύ διαφορετική από την απώλεια στο 25%. Μετά την εκτέλεση του παραπάνω κώδικα, τα αποτελέσματά μου ήταν τα εξής:

Αποτελέσματα πεταλούδας: εάν in3 = HIGH και in4 = LOW, οι κινητήρες πεταλούδας θα έχουν περιστροφή Clock-Wise (CW), δηλαδή το αυτοκίνητο θα προχωρήσει. Διαφορετικά, το αυτοκίνητο θα κινηθεί προς τα πίσω.

Αποτελέσματα διεύθυνσης: εάν in1 = HIGH και in2 = LOW, ο κινητήρας του τιμονιού θα στρίψει στο μέγιστο αριστερά, δηλαδή το αυτοκίνητο θα στραφεί προς τα αριστερά. Διαφορετικά, το αυτοκίνητο θα στρίψει δεξιά. Μετά από ορισμένα πειράματα, διαπίστωσα ότι ο κινητήρας του τιμονιού δεν θα γυρίσει αν το σήμα PWM δεν ήταν 100% (δηλαδή ο κινητήρας θα κατευθύνεται είτε προς τα δεξιά είτε πλήρως προς τα αριστερά).

Βήμα 5: Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας

Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας
Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας
Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας
Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας
Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας
Ανίχνευση γραμμών λωρίδας και υπολογισμός γραμμής επικεφαλίδας

Σε αυτό το βήμα, θα εξηγηθεί ο αλγόριθμος που θα ελέγχει την κίνηση του αυτοκινήτου. Η πρώτη εικόνα δείχνει όλη τη διαδικασία. Η είσοδος του συστήματος είναι εικόνες, η έξοδος είναι η θήτα (γωνία διεύθυνσης σε μοίρες). Σημειώστε ότι, η επεξεργασία γίνεται σε 1 εικόνα και θα επαναληφθεί σε όλα τα καρέ.

ΦΩΤΟΓΡΑΦΙΚΗ ΜΗΧΑΝΗ:

Η κάμερα θα ξεκινήσει την εγγραφή βίντεο με (320 x 240) ανάλυση. Σας συνιστώ να μειώσετε την ανάλυση, ώστε να έχετε καλύτερο ρυθμό καρέ (fps), καθώς θα πέσει το fps μετά την εφαρμογή τεχνικών επεξεργασίας σε κάθε πλαίσιο. Ο παρακάτω κώδικας θα είναι ο κύριος βρόχος του προγράμματος και θα προσθέτει κάθε βήμα σε αυτόν τον κώδικα.

εισαγωγή cv2

import numpy as np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # set the width to 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # set the height to 240 p # The loop while True: ret, frame = video.read () frame = cv2.flip (frame, -1) cv2.imshow ("original", frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Ο κωδικός εδώ θα εμφανίσει την αρχική εικόνα που ελήφθη στο βήμα 4 και εμφανίζεται στις παραπάνω εικόνες.

Μετατροπή σε Χρωματικό Χώρο HSV:

Τώρα, μετά τη λήψη βίντεο ως καρέ από την κάμερα, το επόμενο βήμα είναι να μετατρέψετε κάθε καρέ σε χρωματικό χώρο Hue, Saturation και Value (HSV). Το κύριο πλεονέκτημα είναι να μπορείτε να διαφοροποιήσετε τα χρώματα ανάλογα με το επίπεδο φωτεινότητάς τους. Και εδώ είναι μια καλή εξήγηση του χρώματος του HSV. Η μετατροπή σε HSV γίνεται μέσω της ακόλουθης συνάρτησης:

def convert_to_HSV (πλαίσιο):

hsv = cv2.cvtColor (πλαίσιο, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) επιστροφή hsv

Αυτή η λειτουργία θα κληθεί από τον κύριο βρόχο και θα επιστρέψει το πλαίσιο στο χρωματικό χώρο HSV. Το πλαίσιο που αποκτήθηκε από εμένα στο χώρο χρώματος HSV φαίνεται παραπάνω.

Ανίχνευση μπλε χρώματος και άκρων:

Αφού μετατρέψουμε την εικόνα σε χρωματικό χώρο HSV, ήρθε η ώρα να εντοπίσουμε μόνο το χρώμα που μας ενδιαφέρει (δηλ. Το μπλε χρώμα αφού είναι το χρώμα των γραμμών της λωρίδας). Για να εξαγάγετε μπλε χρώμα από ένα πλαίσιο HSV, θα πρέπει να καθοριστεί ένα εύρος απόχρωσης, κορεσμού και τιμής. ανατρέξτε εδώ για να έχετε μια καλύτερη ιδέα για τις τιμές HSV. Μετά από ορισμένα πειράματα, τα άνω και κάτω όρια του μπλε χρώματος εμφανίζονται στον παρακάτω κώδικα. Και για να μειωθεί η συνολική παραμόρφωση σε κάθε καρέ, οι ακμές ανιχνεύονται μόνο με τον ανιχνευτή άκρων. Περισσότερα για το canny edge θα βρείτε εδώ. Ένας βασικός κανόνας είναι να επιλέξετε τις παραμέτρους της συνάρτησης Canny () με αναλογία 1: 2 ή 1: 3.

def dete_edges (πλαίσιο):

lower_blue = np.array ([90, 120, 0], dtype = "uint8") # κατώτερο όριο μπλε χρώματος upper_blue = np.array ([150, 255, 255], dtype = "uint8") # άνω όριο μάσκα μπλε χρώματος = cv2.inRange (hsv, lower_blue, above_blue) # αυτή η μάσκα θα φιλτράρει τα πάντα εκτός από το μπλε # ανιχνεύει άκρες άκρες = cv2. Canny (μάσκα, 50, 100) cv2.imshow ("άκρες", άκρες) επιστροφή ακμών

Αυτή η συνάρτηση θα κληθεί επίσης από τον κύριο βρόχο, ο οποίος λαμβάνει ως παράμετρο το χρωματικό πλαίσιο του HSV και επιστρέφει το ακραίο πλαίσιο. Το ακραίο πλαίσιο που έλαβα βρίσκεται παραπάνω.

Επιλογή περιοχής ενδιαφέροντος (ROI):

Η επιλογή περιοχής ενδιαφέροντος είναι ζωτικής σημασίας για εστίαση μόνο σε 1 περιοχή του πλαισίου. Σε αυτή την περίπτωση, δεν θέλω το αυτοκίνητο να βλέπει πολλά αντικείμενα στο περιβάλλον. Απλώς θέλω το αυτοκίνητο να εστιάσει στις γραμμές λωρίδας και να αγνοήσει οτιδήποτε άλλο. P. S: το σύστημα συντεταγμένων (άξονες x και y) ξεκινά από την επάνω αριστερή γωνία. Με άλλα λόγια, το σημείο (0, 0) ξεκινά από την επάνω αριστερή γωνία. ο άξονας y είναι το ύψος και ο άξονας x είναι το πλάτος. Ο παρακάτω κώδικας επιλέγει την περιοχή ενδιαφέροντος για εστίαση μόνο στο κάτω μισό του πλαισίου.

def region_of_interest (άκρα):

ύψος, πλάτος = άκρες. σχήμα # εξαγωγή του ύψους και του πλάτους των άκρων μάσκα πλαισίου = np.zeros_like (άκρες) # δημιουργήστε έναν άδειο πίνακα με τις ίδιες διαστάσεις του πλαισίου των άκρων # εστίαση μόνο στο κάτω μισό της οθόνης # καθορίστε τις συντεταγμένες του 4 πόντους (κάτω αριστερά, πάνω αριστερά, πάνω δεξιά, κάτω δεξιά) πολύγωνο = np.array (

Αυτή η συνάρτηση θα λάβει το περιμετρικό πλαίσιο ως παράμετρο και σχεδιάζει ένα πολύγωνο με 4 προκαθορισμένα σημεία. Θα επικεντρωθεί μόνο σε αυτό που βρίσκεται μέσα στο πολύγωνο και θα αγνοήσει τα πάντα έξω από αυτό. Το πλαίσιο της περιοχής ενδιαφέροντός μου φαίνεται παραπάνω.

Εντοπισμός τμημάτων γραμμής:

Ο μετασχηματισμός Hough χρησιμοποιείται για την ανίχνευση τμημάτων γραμμής από ένα ακραίο πλαίσιο. Ο μετασχηματισμός Hough είναι μια τεχνική για τον εντοπισμό οποιουδήποτε σχήματος σε μαθηματική μορφή. Μπορεί να ανιχνεύσει σχεδόν οποιοδήποτε αντικείμενο, ακόμη και αν έχει παραμορφωθεί σύμφωνα με κάποιο αριθμό ψήφων. εδώ φαίνεται μια μεγάλη αναφορά για το Hough transform. Για αυτήν την εφαρμογή, η συνάρτηση cv2. HoughLinesP () χρησιμοποιείται για τον εντοπισμό γραμμών σε κάθε πλαίσιο. Οι σημαντικές παράμετροι που λαμβάνει αυτή η συνάρτηση είναι:

cv2. HoughLinesP (πλαίσιο, rho, theta, min_threshold, minLineLength, maxLineGap)

  • Πλαίσιο: είναι το πλαίσιο στο οποίο θέλουμε να εντοπίσουμε γραμμές.
  • rho: Είναι η ακρίβεια της απόστασης σε pixel (συνήθως είναι = 1)
  • θήτα: γωνιακή ακρίβεια σε ακτίνια (πάντα = np.pi/180 ~ 1 μοίρα)
  • min_threshold: ελάχιστη ψήφος για να θεωρηθεί ως γραμμή
  • minLineLength: ελάχιστο μήκος γραμμής σε εικονοστοιχεία. Οποιαδήποτε γραμμή μικρότερη από αυτόν τον αριθμό δεν θεωρείται γραμμή.
  • maxLineGap: μέγιστο κενό σε εικονοστοιχεία μεταξύ 2 γραμμών που πρέπει να αντιμετωπίζονται ως 1 γραμμή. (Δεν χρησιμοποιείται στην περίπτωσή μου, καθώς οι γραμμές λωρίδας που χρησιμοποιώ δεν έχουν κανένα κενό).

Αυτή η συνάρτηση επιστρέφει τα τελικά σημεία μιας γραμμής. Η ακόλουθη συνάρτηση καλείται από τον κύριο βρόχο μου για τον εντοπισμό γραμμών χρησιμοποιώντας μετασχηματισμό Hough:

def dete_line_segments (cropped_edges):

rho = 1 theta = np.pi / 180 min_threshold = 10 line_segments = cv2. HoughLinesP (cropped_edges, rho, theta, min_threshold, np.array (), minLineLength = 5, maxLineGap = 0) line_segments επιστροφής

Μέση κλίση και τομή (m, b):

υπενθυμίζουμε ότι η εξίσωση της ευθείας δίνεται με y = mx + b. Όπου m είναι η κλίση της ευθείας και b η y-τομή. Σε αυτό το μέρος, θα υπολογιστεί ο μέσος όρος των κλίσεων και των τομών των τμημάτων γραμμής που ανιχνεύονται χρησιμοποιώντας μετασχηματισμό Hough. Πριν το κάνετε αυτό, ας ρίξουμε μια ματιά στην αρχική φωτογραφία πλαισίου που εμφανίζεται παραπάνω. Η αριστερή λωρίδα φαίνεται να ανεβαίνει, οπότε έχει αρνητική κλίση (θυμάστε το σημείο εκκίνησης του συστήματος συντεταγμένων;). Με άλλα λόγια, η αριστερή γραμμή λωρίδας έχει x1 <x2 και y2 x1 και y2> y1 που θα δώσει θετική κλίση. Έτσι, όλες οι γραμμές με θετική κλίση θεωρούνται σωστά σημεία λωρίδας. Σε περίπτωση κάθετων γραμμών (x1 = x2), η κλίση θα είναι άπειρη. Σε αυτήν την περίπτωση, θα παραλείψουμε όλες τις κάθετες γραμμές για να αποτρέψουμε τη λήψη σφάλματος. Για να προσθέσετε περισσότερη ακρίβεια σε αυτόν τον εντοπισμό, κάθε πλαίσιο χωρίζεται σε δύο περιοχές (δεξιά και αριστερά) μέσω 2 οριακών γραμμών. Όλα τα σημεία πλάτους (σημεία άξονα x) μεγαλύτερα από τη δεξιά οριακή γραμμή, σχετίζονται με τον υπολογισμό της δεξιάς λωρίδας. Και αν όλα τα σημεία πλάτους είναι μικρότερα από την αριστερή οριακή γραμμή, σχετίζονται με τον υπολογισμό της αριστερής λωρίδας. Η ακόλουθη συνάρτηση λαμβάνει το πλαίσιο υπό επεξεργασία και τα τμήματα λωρίδας ανιχνεύονται χρησιμοποιώντας μετασχηματισμό Hough και επιστρέφει τη μέση κλίση και τομή δύο γραμμών λωρίδας.

def average_slope_intercept (frame, line_segments):

lane_lines = αν τα τμήματα γραμμής είναι Κανένα: εκτύπωση ("δεν εντοπίστηκε τμήμα γραμμής") επιστρέψτε το ύψος, το πλάτος, _ = πλαίσιο. σχήμα left_fit = right_fit = σύνορα = left_region_boundary = πλάτος * (1 - σύνορα) right_region_boundary = πλάτος * όριο για line_segment σε line_segments: για x1, y1, x2, y2 στο line_segment: αν x1 == x2: print ("παραλείποντας κάθετες γραμμές (κλίση = άπειρο)") συνεχίστε fit = np.polyfit ((x1, x2), (y1, y2), 1) κλίση = (y2 - y1) / (x2 - x1) intercept = y1 - (κλίση * x1) εάν κλίση <0: εάν x1 <left_region_boundary και x2 right_region_boundary και x2> right_region_boundary: right_fit. προσθήκη ((κλίση, ανάσχεση)) left_fit_average = np.average (left_fit, axis = 0) if len (left_fit)> 0: lane_lines.append (make_points (frame, left_fit_average)) right_fit_average = np.average (right_fit, axis = 0) εάν len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines είναι ένας πίνακας 2-D που αποτελείται από τις συντεταγμένες της γραμμής δεξιάς και αριστερής λωρίδας # για παράδειγμα: lan e_lines =

Το make_points () είναι συνάρτηση βοηθού για τη λειτουργία average_slope_intercept (), η οποία θα επιστρέψει τις οριοθετημένες συντεταγμένες των γραμμών λωρίδας (από κάτω προς τα μέσα του πλαισίου).

def make_points (πλαίσιο, γραμμή):

ύψος, πλάτος, _ = πλαίσιο. κλίση σχήματος, τομή = γραμμή y1 = ύψος # κάτω μέρος του πλαισίου y2 = int (y1 / 2) # σημειώστε σημεία από τη μέση του πλαισίου προς τα κάτω εάν η κλίση == 0: κλίση = 0.1 x1 = int ((y1 - τομή) / κλίση) x2 = int ((y2 - τομή) / κλίση) επιστροφή

Για να αποφευχθεί η διαίρεση με το 0, παρουσιάζεται μια συνθήκη. Εάν η κλίση = 0 σημαίνει y1 = y2 (οριζόντια γραμμή), δώστε στην κλίση μια τιμή κοντά στο 0. Αυτό δεν θα επηρεάσει την απόδοση του αλγορίθμου καθώς και θα αποτρέψει την αδύνατη περίπτωση (διαιρώντας με 0).

Για την εμφάνιση των γραμμών λωρίδας στα πλαίσια, χρησιμοποιείται η ακόλουθη λειτουργία:

def_ γραμμές εμφάνισης (πλαίσιο, γραμμές, γραμμή_χρώματος = (0, 255, 0), πλάτος_ευθείας = 6): # χρώμα γραμμής (B, G, R)

line_image = np.zeros_like (frame) αν οι γραμμές δεν είναι None: για γραμμή σε γραμμές: για x1, y1, x2, y2 στη γραμμή: cv2.line (line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted (frame, 0.8, line_image, 1, 1) return line_image

Η συνάρτηση cv2.addWeighted () λαμβάνει τις ακόλουθες παραμέτρους και χρησιμοποιείται για τον συνδυασμό δύο εικόνων, αλλά δίνοντας βάρος σε κάθε μία.

cv2.addWeighted (image1, alpha, image2, beta, gamma)

Και υπολογίζει την εικόνα εξόδου χρησιμοποιώντας την ακόλουθη εξίσωση:

έξοδος = άλφα * εικόνα1 + βήτα * εικόνα2 + γάμμα

Περισσότερες πληροφορίες σχετικά με τη συνάρτηση cv2.addWeighted () προέρχονται εδώ.

Υπολογισμός και εμφάνιση γραμμής επικεφαλίδας:

Αυτό είναι το τελευταίο βήμα πριν εφαρμόσουμε ταχύτητες στους κινητήρες μας. Η γραμμή κατεύθυνσης είναι υπεύθυνη για να δώσει στον κινητήρα του τιμονιού την κατεύθυνση στην οποία πρέπει να περιστρέφεται και στους κινητήρες πεταλούδας την ταχύτητα με την οποία θα λειτουργούν. Ο υπολογισμός της γραμμής επικεφαλίδας είναι καθαρή τριγωνομετρία, χρησιμοποιούνται τριγωνομετρικές συναρτήσεις tan και atan (tan^-1). Ορισμένες ακραίες περιπτώσεις είναι όταν η κάμερα ανιχνεύει μόνο μία γραμμή λωρίδας ή όταν δεν εντοπίζει καμία γραμμή. Όλες αυτές οι περιπτώσεις εμφανίζονται στην ακόλουθη συνάρτηση:

def get_steering_angle (frame, lane_lines):

ύψος, πλάτος, _ = frame.shape if len (lane_lines) == 2: # αν ανιχνευτούν δύο γραμμές λωρίδας _, _, left_x2, _ = lane_lines [0] [0] # εξαγωγή αριστερού x2 από τον πίνακα γραμμών _, _, right_x2, _ = lane_lines [1] [0] # εξαγωγή δεξιού x2 από τον πίνακα_ γραμμών λωρίδας mid = int (πλάτος / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (ύψος / 2) elif len (lane_lines) == 1: # αν ανιχνευτεί μόνο μία γραμμή x1, _, x2, _ = γραμμές_κλίσης [0] [0] x_offset = x2 - x1 y_offset = int (ύψος / 2) elif len (lane_lines) == 0: # αν δεν εντοπιστεί καμία γραμμή x_offset = 0 y_offset = int (ύψος / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180.0 / math.pi) steering_angle = angle_to_mid_deg + 90 steering

Το x_offset στην πρώτη περίπτωση είναι πόσο ο μέσος όρος ((δεξιά x2 + αριστερά x2) / 2) διαφέρει από το μέσο της οθόνης. Το y_offset λαμβάνεται πάντα ως ύψος / 2. Η τελευταία παραπάνω εικόνα δείχνει ένα παράδειγμα γραμμής επικεφαλίδας. Το angle_to_mid_radians είναι το ίδιο με το "theta" που εμφανίζεται στην τελευταία παραπάνω εικόνα. Εάν το steering_angle = 90, σημαίνει ότι το αυτοκίνητο έχει γραμμή κατεύθυνσης κάθετη στη γραμμή "ύψος / 2" και το αυτοκίνητο θα προχωρήσει χωρίς να κατευθύνεται. Εάν το steering_angle> 90, το αυτοκίνητο θα πρέπει να κατευθύνει προς τα δεξιά, διαφορετικά θα πρέπει να κατευθύνει αριστερά. Για να εμφανίσετε τη γραμμή επικεφαλίδας, χρησιμοποιείται η ακόλουθη συνάρτηση:

def display_heading_line (frame, steering_angle, line_color = (0, 0, 255), line_width = 5)

heading_image = np.zeros_like (frame) ύψος, πλάτος, _ = frame.shape steering_angle_radian = steering_angle / 180.0 * math.pi x1 = int (width / 2) y1 = height x2 = int (x1 - height / 2 / math.tan (steering_angle_radian)) y2 = int (ύψος / 2) cv2.line (heading_image, (x1, y1), (x2, y2), line_color, line_width) heading_image = cv2.addWeighted (frame, 0.8, heading_image, 1, 1) επιστροφή heading_image

Η παραπάνω λειτουργία παίρνει το πλαίσιο στο οποίο θα σχεδιαστεί η γραμμή επικεφαλίδας και η γωνία διεύθυνσης ως είσοδος. Επιστρέφει την εικόνα της γραμμής επικεφαλίδας. Το πλαίσιο γραμμής επικεφαλίδας που έχει ληφθεί στην περίπτωσή μου εμφανίζεται στην παραπάνω εικόνα.

Συνδυάζοντας όλους τους κωδικούς μαζί:

Ο κωδικός είναι τώρα έτοιμος για συναρμολόγηση. Ο ακόλουθος κώδικας δείχνει τον κύριο βρόχο του προγράμματος που καλεί κάθε συνάρτηση:

εισαγωγή cv2

εισαγωγή numpy ως np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) ενώ True: ret, frame = video.read () frame = cv2.flip (πλαίσιο, -1) #Κλήση των συναρτήσεων hsv = convert_to_HSV (frame) edge = dete_edges (hsv) roi = region_of_interest (edge) line_segments = dete_line_segments (roi) lane_lines = average_slope_intercept (frame, line_segments) lane_lines_image = display_lines = get_steering_angle (frame, lane_lines) heading_image = display_heading_line (lane_lines_image, steering_angle) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Βήμα 6: Εφαρμογή PD Control

Εφαρμογή PD Control
Εφαρμογή PD Control

Τώρα έχουμε τη γωνία διεύθυνσης έτοιμη να τροφοδοτηθεί με τους κινητήρες. Όπως αναφέρθηκε προηγουμένως, εάν η γωνία διεύθυνσης είναι μεγαλύτερη από 90, το αυτοκίνητο θα πρέπει να στρίψει δεξιά διαφορετικά θα πρέπει να στρίψει αριστερά. Εφάρμοσα έναν απλό κώδικα που στρέφει τον κινητήρα του τιμονιού δεξιά εάν η γωνία είναι πάνω από 90 και στρίβει αριστερά εάν η γωνία διεύθυνσης είναι μικρότερη από 90 με σταθερή ταχύτητα πεταλούδας (10% PWM) αλλά έλαβα πολλά λάθη. Το κύριο σφάλμα που πήρα είναι όταν το αυτοκίνητο πλησιάζει σε κάθε στροφή, ο κινητήρας του τιμονιού λειτουργεί άμεσα, αλλά οι κινητήρες πεταλούδας μπλοκάρουν. Προσπάθησα να αυξήσω την ταχύτητα πεταλούδας (20% PWM) στις στροφές, αλλά τελείωσα με το ρομπότ να βγαίνει από τις λωρίδες. Χρειαζόμουν κάτι που αυξάνει πολύ την ταχύτητα του γκαζιού εάν η γωνία διεύθυνσης είναι πολύ μεγάλη και αυξάνει την ταχύτητα λίγο αν η γωνία διεύθυνσης δεν είναι τόσο μεγάλη, στη συνέχεια μειώνει την ταχύτητα σε μια αρχική τιμή καθώς το αυτοκίνητο πλησιάζει τις 90 μοίρες (κινείται ευθεία). Η λύση ήταν η χρήση ελεγκτή PD.

Ο ελεγκτής PID σημαίνει Αναλογικός, Ολοκληρωτικός και Παράγωγος ελεγκτής. Αυτός ο τύπος γραμμικών ελεγκτών χρησιμοποιείται ευρέως σε εφαρμογές ρομποτικής. Η παραπάνω εικόνα δείχνει τον τυπικό βρόχο ελέγχου ανάδρασης PID. Ο στόχος αυτού του ελεγκτή είναι να φτάσει στο "σημείο ρύθμισης" με τον πιο αποτελεσματικό τρόπο σε αντίθεση με τους ελεγκτές "on -off" που ενεργοποιούν ή απενεργοποιούν το εργοστάσιο σύμφωνα με ορισμένες συνθήκες. Ορισμένες λέξεις -κλειδιά πρέπει να είναι γνωστές:

  • Σημείο ρύθμισης: είναι η επιθυμητή τιμή που θέλετε να φτάσει το σύστημά σας.
  • Πραγματική τιμή: είναι η πραγματική τιμή που ανιχνεύεται από τον αισθητήρα.
  • Σφάλμα: είναι η διαφορά μεταξύ του σημείου ρύθμισης και της πραγματικής τιμής (σφάλμα = Σημείο ρύθμισης - Πραγματική τιμή).
  • Ελεγχόμενη μεταβλητή: από το όνομά της, η μεταβλητή που θέλετε να ελέγξετε.
  • Κ. Π.: Αναλογική σταθερά.
  • Ki: Ολοκληρωτική σταθερά.
  • Kd: Παράγωγη σταθερά.

Εν ολίγοις, ο βρόχος συστήματος ελέγχου PID λειτουργεί ως εξής:

  • Ο χρήστης καθορίζει το σημείο ρύθμισης που απαιτείται για να φτάσει το σύστημα.
  • Το σφάλμα υπολογίζεται (σφάλμα = σημείο ρύθμισης - πραγματικό).
  • Ο ελεγκτής P παράγει μια ενέργεια ανάλογη της τιμής του σφάλματος. (το σφάλμα αυξάνεται, αυξάνεται επίσης η δράση P)
  • Ο ελεγκτής I θα ενσωματώσει το σφάλμα με την πάροδο του χρόνου, το οποίο εξαλείφει το σφάλμα σταθερής κατάστασης του συστήματος, αλλά αυξάνει την υπέρβαση του.
  • Ο ελεγκτής D είναι απλά ο παράγωγος χρόνου για το σφάλμα. Με άλλα λόγια, είναι η κλίση του σφάλματος. Κάνει μια ενέργεια ανάλογη με το παράγωγο του σφάλματος. Αυτός ο ελεγκτής αυξάνει τη σταθερότητα του συστήματος.
  • Η έξοδος του ελεγκτή θα είναι το άθροισμα των τριών ελεγκτών. Η έξοδος του ελεγκτή θα γίνει 0 αν το σφάλμα γίνει 0.

Μπορείτε να βρείτε μια μεγάλη εξήγηση για τον ελεγκτή PID εδώ.

Επιστρέφοντας στο αυτοκίνητο που διατηρεί τη λωρίδα κυκλοφορίας, η ελεγχόμενη μεταβλητή μου ήταν να πετάξω ταχύτητα (αφού το τιμόνι έχει μόνο δύο καταστάσεις είτε δεξιά είτε αριστερά). Ένας ελεγκτής PD χρησιμοποιείται για το σκοπό αυτό, καθώς η δράση D αυξάνει πολύ την ταχύτητα πεταλούδας εάν η αλλαγή σφάλματος είναι πολύ μεγάλη (δηλ. Μεγάλη απόκλιση) και επιβραδύνει το αυτοκίνητο εάν αυτή η αλλαγή σφάλματος πλησιάζει το 0. Έκανα τα ακόλουθα βήματα για την εφαρμογή ενός PD ελεγκτής:

  • Ρυθμίστε το σημείο ρύθμισης σε 90 μοίρες (θέλω πάντα το αυτοκίνητο να κινείται ευθεία)
  • Υπολογίστηκε η γωνία απόκλισης από τη μέση
  • Η απόκλιση δίνει δύο πληροφορίες: Πόσο μεγάλο είναι το σφάλμα (μέγεθος απόκλισης) και ποια κατεύθυνση πρέπει να πάρει ο κινητήρας διεύθυνσης (σημάδι απόκλισης). Εάν η απόκλιση είναι θετική, το αυτοκίνητο πρέπει να κατευθύνει δεξιά αλλιώς θα πρέπει να κατευθύνει αριστερά.
  • Δεδομένου ότι η απόκλιση είναι είτε αρνητική είτε θετική, ορίζεται μια μεταβλητή "σφάλματος" και πάντα ίση με την απόλυτη τιμή της απόκλισης.
  • Το σφάλμα πολλαπλασιάζεται με μια σταθερά Kp.
  • Το σφάλμα υφίσταται χρονική διαφοροποίηση και πολλαπλασιάζεται με σταθερά Kd.
  • Η ταχύτητα των κινητήρων ενημερώνεται και ο βρόχος ξεκινά ξανά.

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

ταχύτητα = 10 # ταχύτητα λειτουργίας σε % PWM

# Μεταβλητές προς ενημέρωση κάθε βρόχου lastTime = 0 lastError = 0 # PD σταθερές Kp = 0,4 Kd = Kp * 0,65 Ενώ True: τώρα = time.time () # τρέχουσα μεταβλητή ώρας dt = τώρα - lastTime απόκλιση = steering_angle - 90 # ισοδύναμο σε angle_to_mid_deg μεταβλητό σφάλμα = abs (απόκλιση) εάν απόκλιση -5: # μην οδηγείτε εάν υπάρχει απόκλιση εύρους σφάλματος 10 μοιρών = 0 σφάλμα = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) steering.stop () elif deviation> 5: # κατευθύνετε δεξιά εάν η απόκλιση είναι θετική GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) steering.start (100) elif deviation < -5: # οδηγήστε αριστερά εάν η απόκλιση είναι αρνητική * σφάλμα PD = int (ταχύτητα + παράγωγο + αναλογικό) spd = abs (PD) αν spd> 25: spd = 25 γκάζι. εκκίνηση (spd) lastError = σφάλμα lastTime = time.time ()

Εάν το σφάλμα είναι πολύ μεγάλο (η απόκλιση από τη μέση είναι μεγάλη), οι αναλογικές και οι παράγωγες ενέργειες είναι υψηλές με αποτέλεσμα την υψηλή ταχύτητα πεταλούδας. Όταν το σφάλμα πλησιάζει το 0 (η απόκλιση από τη μέση είναι χαμηλή), η παράγωγη δράση δρα αντίστροφα (η κλίση είναι αρνητική) και η ταχύτητα πεταλούδας μειώνεται για να διατηρηθεί η σταθερότητα του συστήματος. Ο πλήρης κωδικός επισυνάπτεται παρακάτω.

Βήμα 7: Αποτελέσματα

Τα παραπάνω βίντεο δείχνουν τα αποτελέσματα που έλαβα. Χρειάζεται περισσότερη ρύθμιση και περαιτέρω προσαρμογές. Συνδέω το raspberry pi με την οθόνη LCD, επειδή η ροή βίντεο στο δίκτυό μου είχε μεγάλη καθυστέρηση και ήταν πολύ απογοητευτικό να δουλέψω, γι 'αυτό υπάρχουν καλώδια που συνδέονται με το raspberry pi στο βίντεο. Χρησιμοποίησα σανίδες αφρού για να σχεδιάσω την πίστα.

Περιμένω να ακούσω τις προτάσεις σας για να γίνει αυτό το έργο καλύτερο! Ελπίζω ότι αυτά τα εκπαιδευτικά ήταν αρκετά καλά για να σας δώσουμε μερικές νέες πληροφορίες.

Συνιστάται: