Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Robot Pemecah Labirin, menggunakan Kecerdasan Buatan

Komponen dan persediaan

Arduino Nano R3
× 1
Sensor RedBot SparkFun - Pengikut Garis
× 1
ZX03 (berdasarkan TCRT5000) Sensor Inframerah Reflektif (Output analog)
× 2
Perangkat Android
× 1
Servo Rotasi Berkelanjutan RobotGeek
× 2
tempat baterai 4xAA
× 2

Aplikasi dan layanan online

Arduino IDE
MIT App Inventor 2

Tentang proyek ini

Pendahuluan

Tutorial ini dikembangkan pada proyek terakhir saya:Robot Pengikut Garis - Kontrol PID - Pengaturan Android. Setelah Anda memiliki robot dengan kemampuan mengikuti garis, langkah alami berikutnya adalah memberinya beberapa tingkat kecerdasan. Jadi, "Rex, the Robot" kita yang tersayang sekarang akan mencoba menemukan cara melarikan diri dari "labirin" dengan cara terpendek dan tercepat (omong-omong, dia membenci Minotaurus.

Untuk memulai, apa perbedaan antara Labirin dan Labirin ? Menurut http://www.labyrinthos.net, di dunia berbahasa Inggris sering dianggap bahwa untuk memenuhi syarat sebagai labirin, sebuah desain harus memiliki pilihan di jalur. Jelas, ini akan mencakup banyak instalasi modern di taman hiburan dan tempat wisata, termasuk labirin 2D kami di sini. Konsensus populer juga menunjukkan bahwa labirin memiliki satu jalur yang mengarah tak terhindarkan dari pintu masuk ke tujuan, meskipun seringkali melalui rute yang paling rumit dan berliku.

Mayoritas labirin, betapapun rumitnya desainnya, pada dasarnya terbentuk dari satu dinding kontinu dengan banyak persimpangan dan cabang. Jika dinding yang mengelilingi tujuan labirin terhubung ke perimeter labirin di pintu masuk, labirin selalu dapat diselesaikan dengan menjaga satu tangan tetap bersentuhan dengan dinding, betapapun banyak jalan memutar yang mungkin terjadi. Labirin 'sederhana' itu dikenal dengan benar sebagai "Terhubung sederhana " atau "labirin sempurna " atau dengan kata lain, yang mengandung tidak ada loop .

Kembali ke proyek kami, itu akan dibagi menjadi dua bagian (atau "pass "):

  • (Jalan Pertama) :Robot menemukan jalan keluar dari "labirin sempurna yang tidak diketahui ". Tidak masalah di mana Anda meletakkannya di dalam labirin, itu akan menemukan "solusi ".
  • (Umpan Kedua) :Setelah robot menemukan kemungkinan solusi labirin, ia harus mengoptimalkan solusinya dengan menemukan "jalur terpendek dari awal hingga akhir ".

Video di bawah ini, akan menunjukkan contoh Rex menemukan jalan keluarnya. Saat pertama kali robot menjelajahi labirin, tentu akan membuang banyak waktu "berpikir " tentang apa yang harus dilakukan di persimpangan mana pun. Menguji berbagai kemungkinan, itu akan mengambil beberapa jalur yang salah dan jalan buntu, yang memaksanya untuk menjalankan jalur yang lebih panjang dan melakukan yang tidak perlu "U-Turns ". Selama "1st Pass" , robot akan mengumpulkan pengalaman, "membuat catatan " tentang persimpangan yang berbeda dan menghilangkan cabang yang buruk. Dalam "2nd Pass ", robot berjalan lurus dan cepat sampai akhir tanpa kesalahan atau keraguan. Sepanjang tutorial ini, kita akan mengeksplorasi secara detail bagaimana melakukannya:

Langkah 1:Tagihan Bahan

Daftar bahan pada dasarnya sama dengan yang digunakan dengan Robot Pengikut Garis, kecuali bahwa saya menyertakan 2 Sensor tambahan untuk akurasi yang lebih baik dalam mendeteksi persimpangan KIRI dan KANAN:

Robot terakhir masih sangat murah (sekitar $85,00):

Body (bisa disesuaikan dengan kebutuhan atau bahan yang tersedia):

  • 2 X Kotak kayu (80X80mm)
  • 3 X Klip pengikat
  • 2 X Roda kayu (diameter:50mm)
  • 1 X Kastor bola
  • 9 X Pita elastis
  • Strip bingkai Perintah 3M
  • Sambungan plastik untuk pemasangan sensor
  • BreadBoard dan kabel
  • 2 X set Baterai Hidrida Logam 4XNi (setiap set 5V)
  • 2 X SM-S4303R Rotasi Terus Menerus Servo Plastik 360 Derajat
  • Arduino Nano
  • Modul Bluetooth HC-06
  • 5 X Sensor garis (Modul Sensor Pengikut Jalur Inframerah 4CH TCRT5000 + 1 sensor Track independen)
  • 2 X ZX03 (berdasarkan TCRT5000) Sensor Inframerah Reflektif (Output analog)
  • 1 LED
  • 1 tombol

Catatan :Saya menggunakan item 7 di atas dengan output analog, karena saya tidak memiliki sensor di tangan dengan output digital seperti pada item 6. Idealnya adalah memiliki semua sensor yang sama, jika memungkinkan. Saya juga menguji proyek dengan hanya menyimpan 5 sensor asli. Ini akan berhasil, tetapi membutuhkan penyesuaian yang lebih sensitif dalam menemukan persimpangan.

Langkah 2:Perubahan Tubuh

Hapus set asli 5 Line Follow Sensors dan perbaiki "Far LEFT" . yang baru dan "Jauh KANAN " sensor reflektif di setiap ujung batang plastik penopang. Disarankan untuk membuat 7 sensor sebaris mungkin.

Langkah 3:Memasang dan menguji sensor baru

Array baru sekarang 7 sensor , dipasang sedemikian rupa sehingga 5 yang asli digunakan secara eksklusif untuk kontrol PID (dan deteksi "garis penuh", dijelaskan kemudian) dan 2 yang baru, dibiarkan digunakan secara eksklusif untuk deteksi persimpangan KIRI dan KANAN.

Sebagai tinjauan singkat, mari kita ingat bagaimana 5 sensor "digital" asli bekerja:

Jika satu sensor dipusatkan dalam kaitannya dengan garis hitam, hanya sensor tertentu yang akan menghasilkan TINGGI. Di sisi lain, jarak antar sensor harus dihitung agar 2 sensor dapat menutupi seluruh lebar garis hitam secara bersamaan, juga menghasilkan sinyal TINGGI pada kedua sensor.

Cara kerja 2 sensor "analog" baru:

Jika salah satu sensor dipusatkan sehubungan dengan garis hitam, outputnya akan menjadi nilai analog, biasanya menghasilkan output pada ADC Arduino di bawah "100" (ingat bahwa ADC menghasilkan output dari 0 hingga 1023). Dengan permukaan yang lebih terang, nilai output akan lebih tinggi (saya menguji 500 hingga 600 di atas kertas putih, misalnya). Nilai ini harus diuji pada situasi cahaya dan material permukaan yang berbeda untuk menentukan konstanta THRESHOLD yang tepat untuk digunakan dalam kasus Anda (lihat gambar di sini).

Melihat kode Arduino, masing-masing sensor akan ditentukan dengan nama tertentu (pertimbangkan bahwa Sensor Line Follow asli lebih ke Kiri harus diberi label "0 "):

const int lineFollowSensor0 =12; //Menggunakan input Digitalconst int lineFollowSensor1 =18; //Menggunakan Pin Analog A4 sebagai input Digitalconst int lineFollowSensor2 =17; //Menggunakan Pin Analog A3 sebagai input Digitalconst int lineFollowSensor3 =16; //Menggunakan Pin Analog A2 sebagai input Digitalconst int lineFollowSensor4 =19; //Menggunakan Pin Analog A5 sebagai input Digitalconst int farRightSensorPin =0; //Pin Analog A0const int farLeftSensorPin =1; //pin analog A1 

Untuk diingat, kemungkinan 5 keluaran larik sensor asli saat mengikuti garis adalah:

1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 

Dengan penambahan 2 yang baru, kemungkinan keluarannya adalah:

  • Sensor KIRI Jauh:Output Analog lebih besar atau lebih rendah dari THRESHOLD
  • Sensor KANAN Jauh:Output Analog lebih besar atau lebih rendah dari THRESHOLD

Untuk menyimpan nilai setiap sensor, variabel array dibuat untuk 5 sensor digital asli:

int LFSensor[5]={0, 0, 0, 0, 0}; 

Dan dua variabel integer untuk 2 sensor analog baru:

int farRightSensor =0;int farLeftSensor =0; 

Setiap posisi larik dan variabel akan terus diperbarui dengan output dari masing-masing sensor:

LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead(lineFollowSensor4);farRightSensor =analogRead(farRightSensorPin);farLeftSensor =analogRead(farLeftSensorPin); 

Memiliki 5 sensor, seperti yang terlihat dalam proyek Robot Garis Pengikut, memungkinkan pembuatan "variabel kesalahan" yang akan membantu mengontrol posisi robot di atas garis. Juga, variabel yang disebut "mode" akan digunakan untuk definisi jika robot mengikuti garis , melalui garis berkelanjutan , persimpangan atau tidak ada garis sama sekali.

Variabel ini "mode " juga akan digunakan dengan "Far LEFT/RIGHT " sensor. Untuk representasi, mari pertimbangkan sensor paling kiri dan kanan yang memiliki 3 kemungkinan status:

  • H (lebih tinggi dari THRESHOLD),
  • L (lebih kecil dari THRESHOLD) dan
  • X (tidak relevan).

Untuk output digital , akan biasa 0, 1 dan kami juga akan memperkenalkan X:

  • H 0 X X X X L ==> mode =KANAN_TURN; kesalahan =0; (lihat contoh pada gambar di atas)
  • L X X X X 0 H ==> mode =KIRI_TURN; kesalahan =0;
  • X 0 0 0 0 0 X ==> mode =NO_LINE; kesalahan =0;
  • H 0 0 0 0 1 H ==> mode =FOLLOWING_LINE; kesalahan =4;
  • H 0 0 0 1 1 H ==> mode =FOLLOWING_LINE; kesalahan =3;
  • H 0 0 0 1 0 H ==> mode =FOLLOWING_LINE; kesalahan =2;
  • H 0 0 1 1 0 H ==> mode =FOLLOWING_LINE; kesalahan =1;
  • H 0 0 1 0 0 H ==> mode =FOLLOWING_LINE; kesalahan =0;
  • H 0 1 1 0 0 H ==> mode =FOLLOWING_LINE; kesalahan =-1;
  • H 0 1 0 0 0 H ==> mode =FOLLOWING_LINE; kesalahan =-2
  • H 1 1 0 0 0 H ==> mode =FOLLOWING_LINE; kesalahan =-3;
  • H 1 0 0 0 0 H ==> mode =FOLLOWING_LINE; kesalahan =-4;
  • X 1 1 1 1 1 X ==> mode =CONT_LINE; kesalahan =0;

Jadi, implementasikan logika di atas dalam fungsi:

void readLFSsensors() 

akan mengembalikan variabel "mode " dan "kesalahan " yang akan digunakan pada logika program. Penting untuk menguji logika sensor sebelum mengikuti proyek. Fungsi di bawah disertakan dalam kode dan dapat digunakan untuk tujuan pengujian:

void testSensorLogic(void) { Serial.print (farLeftSensor); Serial.print("<==KIRI KANAN==>"); Serial.print (farRightSensor); Serial.print("modus:"); Serial.print (modus); Serial.print("kesalahan:"); Serial.println (kesalahan);} 

Langkah 4:Memecahkan Labirin - Aturan Tangan Kiri

Seperti yang dibahas pada pendahuluan, sebagian besar labirin betapapun rumitnya desainnya, pada dasarnya terbentuk dari satu dinding kontinu dengan banyak persimpangan dan cabang. Jika dinding yang mengelilingi tujuan labirin terhubung ke perimeter labirin di pintu masuk, labirin selalu dapat diselesaikan dengan menjaga satu tangan tetap bersentuhan dengan dinding, betapapun banyak jalan memutar yang mungkin terjadi. Labirin 'sederhana' ini dikenal dengan benar sebagai "Terhubung sederhana ."

Mencari di Wikipedia, kami mengetahui bahwa:

Singkatnya, Aturan Tangan Kiri dapat digambarkan seperti:

Di setiap persimpangan, dan di sepanjang labirin, jaga agar tangan kiri Anda tetap menyentuh dinding di sebelah kiri Anda.

  • Letakkan tangan kirimu di dinding.
  • Mulailah berjalan ke depan
  • Akhirnya, Anda akan mencapai ujung labirin. Anda mungkin tidak akan menempuh jalan terpendek dan langsung, tetapi Anda akan sampai di sana.

Jadi, kuncinya di sini adalah mengidentifikasi persimpangan , menentukan kursus apa yang harus diambil berdasarkan aturan di atas. Khususnya di Labirin 2D jenis kami, kami dapat menemukan 8 jenis persimpangan yang berbeda (lihat gambar pertama di atas):

Melihat gambar tersebut, kita dapat menyadari bahwa kemungkinan tindakan di persimpangan adalah:

1. Pada "Salib ":

  • Ke Kiri, atau
  • Ke Kanan, atau
  • Langsung

2. Pada "T ":

  • Ke Kiri, atau
  • Ke Kanan

3. Di "Kanan Saja ":

  • Ke Kanan

4. Di "Kiri Saja ":

  • Ke Kiri

5. Di "Lurus atau Kiri ":

  • Ke Kiri, atau
  • Langsung

6. Di "Lurus atau Kanan ":

  • Ke Kanan, atau
  • Langsung

7. Di "Buntut ":

  • Kembalilah ("Putar balik")

8. Di "Akhir Labirin ":

  • Berhenti

Namun, menerapkan "Aturan Tangan Kiri", tindakan akan dikurangi menjadi satu opsi masing-masing:

  • Pada "Salib":Ke Kiri
  • Pada "T":Ke Kiri
  • Di "Kanan Saja":Ke Kanan
  • Pada "Kiri Saja":Ke Kiri
  • Pada "Lurus atau Kiri":Ke Kiri
  • Di "Lurus atau Kanan":Lurus
  • Di "Jalan buntu":Kembali ("Putar balik")
  • Di "Akhir Labirin":Berhenti

Kami hampir sampai! "Tenang!"

Ketika robot mencapai "Akhir Mati" atau "Akhir Labirin", mudah untuk mengidentifikasi mereka, karena tidak ada situasi ambigu (kami telah menerapkan tindakan tersebut pada Robot Pengikut Garis, ingat?). Masalahnya adalah ketika robot menemukan "GARIS" misalnya, karena garis dapat berupa "Silang" (1) atau "T" (2). Juga ketika mencapai "BELI KANAN atau KIRI", itu bisa berupa belokan sederhana (opsi 3 atau 4) atau opsi untuk lurus (5 atau 6). Untuk mengetahui dengan tepat pada jenis persimpangan apa robot itu, langkah tambahan harus diambil:robot harus berlari "seinci ekstra" dan melihat apa yang berikutnya (lihat gambar kedua di atas, sebagai contoh).

Jadi, dalam hal aliran, tindakan yang mungkin sekarang dapat digambarkan sebagai:

1. Pada "AKHIR MATI":

  • Kembalilah ("Putar balik")

2. Pada "LINE":Lari satu inci ekstra

  • Jika ada garis:Ini adalah "Silang" ==> Ke KIRI
  • Jika tidak ada baris:itu adalah "T" ==> Pergi ke KIRI
  • Jika ada baris lain:itu adalah "Akhir Labirin" ==> STOP

3. Saat "BELI KANAN":Lari satu inci ekstra

  • jika ada garis :Lurus atau Kanan ==> Lurus
  • Jika tidak ada baris:itu adalah Kanan Saja ==> Pergi ke KANAN

4. Pada "BELI KIRI":Lari satu inci ekstra

  • jika ada garis:Lurus atau KIRI ==> Ke KIRI
  • Jika tidak ada garis:hanya KIRI ==> Pergi ke KIRI

Perhatikan bahwa pada kenyataannya, Dalam kasus "BELIKI KIRI", Anda dapat melewati tes, karena Anda tetap akan mengambil KIRI. Saya meninggalkan penjelasan yang lebih umum hanya untuk kejelasan. Pada kode asli saya akan melewatkan tes ini. Gambar ke-3 di atas, menunjukkan labirin yang sangat sederhana di lantai lab saya, digunakan untuk tujuan pengujian.

Langkah 5:Menerapkan Algoritma "Tangan Kiri di Dinding" pada Kode Arduino

Setelah kita memiliki readLFSsensors() fungsi dimodifikasi untuk memasukkan 2 sensor tambahan, kita dapat menulis ulang Fungsi Loop untuk menjalankan algoritme seperti yang dijelaskan pada Langkah terakhir:

void loop(){ readLFSsensors(); sakelar (mode) { kasus NO_LINE:motorStop(); goAndTurn (KIRI, 180); merusak; kasus CONT_LINE:runExtraInch(); readLFSsensor(); if (mode ==CONT_LINE) labirinEnd(); lain goAndTurn (KIRI, 90); // atau itu adalah "T" atau "Cross"). Dalam kedua kasus, pergi ke LEFT break; kasus RIGHT_TURN:runExtraInch(); readLFSsensor(); if (mode ==NO_LINE) goAndTurn (KANAN, 90); merusak; kasus LEFT_TURN:goAndTurn (KIRI, 90); merusak; kasus FOLLOWING_LINE:followingLine(); merusak; }} 

Beberapa fungsi baru muncul di sini:

  • followingLine() sama halnya dengan Robot Baris Berikut dimana jika hanya mengikuti satu baris harus calculatePID() ; dan mengontrol motor bergantung pada nilai PID:motorPIDcontrol();
  • runExtraInch(): akan mendorong robot ke depan sedikit. Berapa banyak robot akan berjalan akan tergantung pada waktu yang Anda gunakan dalam fungsi penundaan, sebelum Anda memerintahkan motor untuk berhenti.
void runExtraInch(void){ motorPIDcontrol(); penundaan (ekstraInci); motorStop();} 
  • goAndTurn (arah, sudut): fungsi khusus ini penting karena Anda tidak dapat memutar robot segera setelah Anda menyadari jenis persimpangan Anda. Ingatlah bahwa kita memproyeksikan Robot Diferensial bahwa ketika berbelok, ia "memutar kapaknya" dan seterusnya, untuk bergerak 90o dan terus mengikuti garis, pusat roda harus sejajar dengan pusat persimpangan. Setelah garis sensor berada di depan kapaknya, Anda harus menjalankan robot ke depan untuk menyelaraskannya. Konstanta waktu adjGoAndTurn harus disesuaikan tergantung pada jarak antara sumbu dan garis sensor ("d "), kecepatan dan ukuran roda (lihat gambar di atas untuk ilustrasi).
void goAndTurn(int arah, int derajat){ motorPIDcontrol(); tunda(adjGoAndTurn); motorTurn(arah, derajat);} 

Pada titik ini, robot sebenarnya "memecahkan labirin"! Anda baru saja menyelesaikan "First Pass". Tidak masalah di mana Anda mulai di dalam labirin, Anda akan selalu mencapai akhir.

Di bawah ini, pengujian fase proyek ini:

Langkah 6:Menyimpan Jalur

Mari kita perhatikan contoh seperti yang ditunjukkan pada foto di atas. Pada titik awal yang dipilih, robot akan menemukan 15 Persimpangan sebelum mencapai akhir Labirin:

  • Kiri (Kiri)
  • Kembali (B)
  • Kiri (Kiri)
  • Kiri (Kiri)
  • Kiri (Kiri)
  • Kembali (B)
  • Lurus (S)
  • Kembali (B)
  • Kiri (Kiri)
  • Kiri (Kiri)
  • Kembali (B)
  • Lurus (S)
  • Kiri (Kiri)
  • Kiri (Kiri)
  • Akhir

Apa yang harus dilakukan di salah satu persimpangan itu, adalah menyimpan setiap tindakan yang dilakukan tepat pada urutan yang sama dengan yang terjadi. Untuk itu mari kita buat variabel (array) baru yang akan menyimpan path yang telah diambil robot:

char path[100] =" "; 

Kita juga harus membuat 2 variabel indeks untuk digunakan bersama dengan array:

jalur karakter yang tidak ditandatanganiLength =0; // panjang pathIndex pathint =0; // digunakan untuk mencapai elemen larik tertentu. 

Jadi, jika kita menjalankan contoh yang ditunjukkan pada gambar, kita akan berakhir dengan:

path =[LBLLLBSBLLBSLL]dan pathLengh =14 

Langkah 7:Menyederhanakan (mengoptimalkan) Jalur

Mari kembali ke contoh kita. Melihat kelompok persimpangan pertama, kami menyadari bahwa cabang kiri pertama sebenarnya adalah "Jalan buntu" dan jadi, jika robot alih-alih "Kiri-Kembali-Kiri" hanya lewat lurus di persimpangan pertama itu, banyak energi dan waktu akan dihemat! Dengan kata lain, urutan "LBL" sebenarnya akan sama dengan "S". Itulah tepatnya bagaimana path lengkap dapat dioptimalkan. Jika Anda menganalisis semua kemungkinan di mana "Belok U" digunakan, himpunan 3 persimpangan di mana "Putaran U" ("B") ini muncul ("xBx") dapat dikurangi menjadi hanya satu.

Di atas hanya satu contoh, di bawah ini Anda dapat menemukan daftar lengkap kemungkinan (cobalah):

  • LBR =B
  • LBS =R
  • RBL =B
  • SBL =R
  • SBS =B
  • LBL =S

Mengambil jalur lengkap atau contoh kami, kami dapat menguranginya:

path =[LBLLLBSBLLBSLL] ==> LBL =Spath =[SLLBSBLLBSLL] ==> LBS =Rpath =[SLRBLLBSLL] ==> RBL =Bpath =[SLBLBSLL] ==> LBL =Spath =[SSBSLL ] ==> SBS =Bpath =[SBLL] ==> SBL =Rpath =[RL] 

Luar biasa! Melihat contoh ini sangat jelas bahwa jika robot mengambil KANAN di persimpangan pertama dan setelah itu, KIRI, itu akan mencapai Ujung Labirin di jalur terpendek!

Kode total Path of Maze Solver Pertama akan digabungkan dalam fungsi mazeSolve() . Fungsi ini sebenarnya adalah fungsi loop() yang digunakan sebelumnya, tetapi menggabungkan semua langkah penyimpanan dan pengoptimalan jalur tersebut. Saat jalur pertama berakhir, larik jalur[] akan memiliki jalur yang dioptimalkan. Sebuah variabel baru diperkenalkan:

status int tidak ditandatangani =0; // penyelesaian =0; mencapai Ujung Labirin =1 

Di bawah fungsi Jalur Pertama:

void mazeSolve(void){ while (!status) { readLFSsensors(); sakelar (mode) { kasus NO_LINE:motorStop(); goAndTurn (KIRI, 180); recIntersection('B'); merusak; kasus CONT_LINE:runExtraInch(); readLFSsensor(); if (mode !=CONT_LINE) {goAndTurn (KIRI, 90); recIntersection('L');} // atau itu adalah "T" atau "Cross"). Dalam kedua kasus, pergi ke LEFT else mazeEnd(); merusak; kasus RIGHT_TURN:runExtraInch(); readLFSsensor(); if (mode ==NO_LINE) {goAndTurn (KANAN, 90); recIntersection('R');} else recIntersection('S'); merusak; kasus LEFT_TURN:goAndTurn (KIRI, 90); recIntersection('L'); merusak; kasus FOLLOWING_LINE:followingLine(); merusak; } }}  

Di sini fungsi baru diperkenalkan:recIntersection (arah). Fungsi ini akan digunakan untuk menyimpan persimpangan dan juga untuk memanggil fungsi lain simplifyPath() , yang akan mengurangi kelompok 3 persimpangan yang melibatkan "U-Turn" seperti yang kita lihat sebelumnya.

void recIntersection(arah char){ path[pathLength] =arah; // Simpan persimpangan di variabel jalur. panjang jalur ++; menyederhanakanPath(); // Sederhanakan jalur yang dipelajari.} 

KREDIT untuk simplifyPath( ) berfungsi untuk Patrick McCabe untuk kode Pemecahan jalur (untuk detailnya, silakan kunjungi https://patrickmccabemakes.com)! Strategi penyederhanaan Path adalah setiap kali kita menemukan barisan xBx, kita dapat menyederhanakannya dengan memotong jalan buntu. Misalnya, LBL ==> S seperti yang kita lihat pada contoh.

void simplePath(){ // hanya menyederhanakan jalur jika belokan kedua hingga terakhir adalah 'B' if(pathLength <3 || path[pathLength-2] !='B') kembali; int totalSudut =0; di aku; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; merusak; kasus 'L':totalAngle +=270; merusak; kasus 'B':totalAngle +=180; merusak; } } // Dapatkan sudut sebagai angka antara 0 dan 360 derajat. totalAngle =totalAngle % 360; // Ganti semua putaran itu dengan satu putaran. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; merusak; kasus 90:path[pathLength - 3] ='R'; merusak; kasus 180:path[pathLength - 3] ='B'; merusak; kasus 270:path[pathLength - 3] ='L'; merusak; } // Jalur sekarang dua langkah lebih pendek. pathLength -=2; }  

Langkah 8:Pass Kedua:selesaikan Maze secepat mungkin!

Program utama:loop() sederhana seperti itu:

void loop() { ledBlink(1); readLFSsensor(); memecahkan labirin(); // Lulus pertama untuk memecahkan labirin ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // Second Pass:jalankan labirin secepat mungkin ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // indeks lintasan lintasan pertama =0; panjang lintasan =0;} 

Jadi, ketika First Pass berakhir, yang harus kita lakukan hanyalah memberi makan robot dengan array jalur yang dioptimalkan. Ini akan mulai berjalan dan ketika persimpangan ditemukan, sekarang akan menentukan apa yang harus dilakukan berdasarkan apa yang disimpan di path[] .

void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (mode) { kasus FOLLOWING_LINE:followingLine(); merusak; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd(); else {mazeTurn (path[pathIndex]); pathIndex++;} istirahat; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd(); else {mazeTurn (path[pathIndex]); pathIndex++;} istirahat; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd(); else {mazeTurn (path[pathIndex]); pathIndex++;} istirahat; } } } 

Untuk memerintahkan apa yang harus dilakukan, fungsi baru mazeTurn(path[]) telah dibuat. Fungsi mazeTurn (path[]) akan menjadi:

void mazeTurn (char dir) { switch(dir) { case 'L':// Belok Kiri goAndTurn (KIRI, 90); merusak; case 'R':// Belok Kanan goAndTurn (KANAN, 90); merusak; case 'B':// Putar Kembali goAndTurn (KANAN, 800); merusak; case 'S':// Jalan Lurus runExtraInch(); merusak; }} 

Umpan kedua selesai! Video di bawah ini menunjukkan contoh lengkap yang berfungsi di sini, lintasan pertama dan kedua. Di bawah kode Arduino yang digunakan pada tutorial ini:

FV6XNJWINJ45XWM.ino F2FXS8MINJ45XX6.h FX5MHFMINJ45XX7.ino FT2S1WXINJ45XXA.ino F9IC3HQINJ45XXB.ino FU2HRXJINJ45XXV.ino

Langkah 9:Menggunakan Android untuk menyetel

Aplikasi Android yang dikembangkan untuk proyek Garis Berikut juga dapat digunakan di sini (jika Anda membutuhkan, Aplikasi Android dan kodenya tersedia di:Robot Pengikut Garis - Kontrol PID - Pengaturan Android. Kode Arduino yang disajikan pada langkah terakhir sudah mencakup komunikasi dengan perangkat Android. Jika Anda tidak ingin menggunakan aplikasi de Android, tidak masalah karena kodenya "transparan ".

Saya banyak menggunakan Android selama proyek untuk mengirim data pengujian dari robot ke perangkat menggunakan "Pesan Diterima " field. Beberapa variabel harus didefinisikan dengan baik untuk menjamin bahwa robot akan berbelok dengan sudut yang benar. Yang paling penting ada di bawah (yang bertanda Bold saya ubah beberapa kali):

const int adj =0; float adjTurn =8;int adjGoAndTurn =800;THRESHOLD =150const int power =250; const int iniMotorPower =250; int ekstraInci =200;  

Langkah 10:Kesimpulan

Ini adalah bagian kedua dan terakhir dari proyek yang kompleks, mengeksplorasi potensi Robot pengikut garis, di mana Kecerdasan Buatan (AI) konsep sederhana digunakan untuk memecahkan labirin.

Saya bukan AI ahli dan berdasarkan beberapa informasi yang saya dapatkan dari web, saya mengerti bahwa apa yang dilakukan Rex kecil kami, robot, memecahkan labirin dapat dianggap sebagai aplikasi AI. Mari kita lihat 2 sumber di bawah ini:

Dari Wikipedia:

Atau dari makalah universitas ini:"Robot Pemecah Labirin Menggunakan Freeduino dan Algoritma LSRB International Journal of Modern Engineering Research (IJMER)"

File yang diperbarui untuk proyek ini dapat ditemukan di GITHUB. Semoga saya bisa berkontribusi bagi orang lain untuk belajar lebih banyak tentang elektronik, robot, Arduino, dll. Untuk tutorial lebih lanjut, silakan kunjungi Blog saya:MJRoBot.org

Saludos dari selatan dunia!

Terima kasih

Marcelo

Kode

  • Cuplikan kode #1
  • Cuplikan kode #4
  • Cuplikan kode #5
  • Cuplikan kode #6
  • Cuplikan kode #7
  • Cuplikan kode #8
  • Cuplikan kode #12
  • Cuplikan kode #13
  • Cuplikan kode #14
  • Cuplikan kode #15
  • Cuplikan kode #16
  • Cuplikan kode #17
Cuplikan kode #1Teks biasa
const int lineFollowSensor0 =12; //Menggunakan input Digitalconst int lineFollowSensor1 =18; //Menggunakan Pin Analog A4 sebagai input Digitalconst int lineFollowSensor2 =17; //Menggunakan Pin Analog A3 sebagai input Digitalconst int lineFollowSensor3 =16; //Menggunakan Pin Analog A2 sebagai input Digitalconst int lineFollowSensor4 =19; //Menggunakan Pin Analog A5 sebagai input Digitalconst int farRightSensorPin =0; //Pin Analog A0const int farLeftSensorPin =1; //pin analog A1
Cuplikan kode #4Teks biasa
LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead( lineFollowSensor4);farRightSensor =analogRead(farRightSensorPin);farLeftSensor =analogRead(farLeftSensorPin);
Cuplikan kode #5Teks biasa
void testSensorLogic(void) { Serial.print (farLeftSensor); Serial.print("<==KIRI KANAN==>"); Serial.print (farRightSensor); Serial.print("modus:"); Serial.print (modus); Serial.print("kesalahan:"); Serial.println (kesalahan);}
Cuplikan kode #6Teks biasa
void loop(){ readLFSsensors(); sakelar (mode) { kasus NO_LINE:motorStop(); goAndTurn (KIRI, 180); merusak; kasus CONT_LINE:runExtraInch(); readLFSsensor(); if (mode ==CONT_LINE) labirinEnd(); lain goAndTurn (KIRI, 90); // atau itu adalah "T" atau "Cross"). Dalam kedua kasus, pergi ke LEFT break; kasus RIGHT_TURN:runExtraInch(); readLFSsensor(); if (mode ==NO_LINE) goAndTurn (KANAN, 90); merusak; kasus LEFT_TURN:goAndTurn (KIRI, 90); merusak; kasus FOLLOWING_LINE:followingLine(); merusak; }}
Code snippet #7Plain text
void runExtraInch(void){ motorPIDcontrol(); delay(extraInch); motorStop();}
Code snippet #8Plain text
void goAndTurn(int direction, int degrees){ motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(direction, degrees);}
Code snippet #12Plain text
void mazeSolve(void){ while (!status) { readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); goAndTurn (LEFT, 180); recIntersection('B'); merusak; case CONT_LINE:runExtraInch(); readLFSsensors(); if (mode !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // or it is a "T" or "Cross"). In both cases, goes to LEFT else mazeEnd(); merusak; case RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');} else recIntersection('S'); merusak; case LEFT_TURN:goAndTurn (LEFT, 90); recIntersection('L'); merusak; case FOLLOWING_LINE:followingLine(); merusak; } }}
Code snippet #13Plain text
void recIntersection(char direction){ path[pathLength] =direction; // Store the intersection in the path variable. pathLength ++; simplifyPath(); // Simplify the learned path.}
Code snippet #14Plain text
void simplifyPath(){ // only simplify the path if the second-to-last turn was a 'B' if(pathLength <3 || path[pathLength-2] !='B') return; int totalAngle =0; di aku; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; merusak; case 'L':totalAngle +=270; merusak; case 'B':totalAngle +=180; merusak; } } // Get the angle as a number between 0 and 360 degrees. totalAngle =totalAngle % 360; // Replace all of those turns with a single one. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; merusak; case 90:path[pathLength - 3] ='R'; merusak; case 180:path[pathLength - 3] ='B'; merusak; case 270:path[pathLength - 3] ='L'; merusak; } // The path is now two steps shorter. pathLength -=2; } 
Code snippet #15Plain text
void loop() { ledBlink(1); readLFSsensors(); mazeSolve(); // First pass to solve the maze ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // Second Pass:run the maze as fast as possible ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // 1st pass pathIndex =0; pathLength =0;}
Code snippet #16Plain text
void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (mode) { case FOLLOWING_LINE:followingLine(); merusak; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; } } }
Code snippet #17Plain text
void mazeTurn (char dir) { switch(dir) { case 'L':// Turn Left goAndTurn (LEFT, 90); merusak; case 'R':// Turn Right goAndTurn (RIGHT, 90); merusak; case 'B':// Turn Back goAndTurn (RIGHT, 800); merusak; case 'S':// Go Straight runExtraInch(); merusak; }}
Github
https://github.com/Mjrovai/MJRoBot-Maze-Solverhttps://github.com/Mjrovai/MJRoBot-Maze-Solver

Skema

z7IdLkxL1J66qOtphxqC.fzz

Proses manufaktur

  1. Robot Menggunakan Raspberry Pi &Bridge Shield
  2. Robot Terkendali Gerakan Menggunakan Raspberry Pi
  3. Robot Terkendali Wifi Menggunakan Raspberry Pi
  4. DETEKSI MANUSIA ROBOT SONBI MENGGUNAKAN KINECT DAN RASPBERRY PI
  5. Bosch Menambahkan Kecerdasan Buatan ke Industri 4.0
  6. Apakah kecerdasan buatan fiksi atau iseng?
  7. Kecerdasan buatan menerima peningkatan Kubernetes yang sangat besar
  8. Kecerdasan Buatan Membantu Robot Mengenali Objek Dengan Sentuhan
  9. Menggunakan Kecerdasan Buatan untuk Melacak Deforestasi
  10. Robot Kecerdasan Buatan