Tutorial Pemrograman Robot Pengantar
Diterbitkan: 2022-03-11Mari kita hadapi itu, robot itu keren. Mereka juga akan menjalankan dunia suatu hari nanti, dan mudah-mudahan, pada saat itu mereka akan mengasihani pencipta mereka yang lemah lembut (alias pengembang robotika) dan membantu kami membangun utopia ruang yang penuh dengan banyak hal. Aku bercanda tentu saja, tapi hanya semacam.
Dalam ambisi saya untuk memiliki pengaruh kecil atas masalah ini, saya mengambil kursus teori kontrol robot otonom tahun lalu, yang memuncak dalam membangun simulator robot berbasis Python yang memungkinkan saya untuk berlatih teori kontrol pada robot sederhana, mobile, dan dapat diprogram. .
Dalam artikel ini, saya akan menunjukkan cara menggunakan kerangka kerja robot Python untuk mengembangkan perangkat lunak kontrol, menjelaskan skema kontrol yang saya kembangkan untuk robot simulasi saya, mengilustrasikan bagaimana ia berinteraksi dengan lingkungannya dan mencapai tujuannya, dan mendiskusikan beberapa tantangan mendasar pemrograman robotika yang saya temui di sepanjang jalan.
Untuk mengikuti tutorial pemrograman robotika untuk pemula ini, Anda harus memiliki pengetahuan dasar tentang dua hal:
- Matematika—kita akan menggunakan beberapa fungsi trigonometri dan vektor
- Python—karena Python adalah salah satu bahasa pemrograman robot dasar yang lebih populer—kami akan menggunakan pustaka dan fungsi dasar Python
Cuplikan kode yang ditampilkan di sini hanyalah bagian dari keseluruhan simulator, yang bergantung pada kelas dan antarmuka, jadi untuk membaca kode secara langsung, Anda mungkin memerlukan beberapa pengalaman dalam Python dan pemrograman berorientasi objek.
Terakhir, topik opsional yang akan membantu Anda mengikuti tutorial ini dengan lebih baik adalah mengetahui apa itu state machine dan bagaimana sensor jangkauan dan encoder bekerja.
Tantangan Robot yang Dapat Diprogram: Persepsi vs. Realitas, dan Kerapuhan Kontrol
Tantangan mendasar dari semua robotika adalah: Tidak mungkin mengetahui keadaan lingkungan yang sebenarnya. Perangkat lunak kontrol robot hanya dapat menebak keadaan dunia nyata berdasarkan pengukuran yang dikembalikan oleh sensornya. Itu hanya dapat mencoba untuk mengubah keadaan dunia nyata melalui generasi sinyal kontrol.
Jadi, salah satu langkah pertama dalam desain kontrol adalah membuat abstraksi dari dunia nyata, yang dikenal sebagai model , yang dapat digunakan untuk menginterpretasikan pembacaan sensor dan membuat keputusan. Selama dunia nyata berperilaku sesuai dengan asumsi model, kita dapat membuat tebakan yang baik dan melakukan kontrol. Namun, segera setelah dunia nyata menyimpang dari asumsi ini, kita tidak akan lagi dapat membuat tebakan yang baik, dan kendali akan hilang. Seringkali, begitu kontrol hilang, itu tidak akan pernah bisa diperoleh kembali. (Kecuali beberapa kekuatan luar yang baik hati memulihkannya.)
Ini adalah salah satu alasan utama mengapa pemrograman robotika begitu sulit. Kita sering melihat video robot penelitian terbaru di lab, melakukan ketangkasan, navigasi, atau kerja tim yang fantastis, dan kita tergoda untuk bertanya, “Mengapa ini tidak digunakan di dunia nyata?” Nah, lain kali Anda melihat video seperti itu, lihat betapa terkontrolnya lingkungan lab. Dalam kebanyakan kasus, robot-robot ini hanya mampu melakukan tugas-tugas yang mengesankan ini selama kondisi lingkungan tetap dalam batas-batas sempit model internalnya. Jadi, salah satu kunci kemajuan robotika adalah pengembangan model yang lebih kompleks, fleksibel, dan kuat—dan kemajuan tersebut tunduk pada batasan sumber daya komputasi yang tersedia.
[Catatan Tambahan: Filsuf dan psikolog sama-sama akan mencatat bahwa makhluk hidup juga menderita ketergantungan pada persepsi internal mereka sendiri tentang apa yang dikatakan indra mereka. Banyak kemajuan dalam robotika datang dari mengamati makhluk hidup dan melihat bagaimana mereka bereaksi terhadap rangsangan yang tidak terduga. Pikirkan tentang itu. Apa model internal Anda tentang dunia? Ini berbeda dari semut, dan ikan? (Mudah-mudahan.) Namun, seperti semut dan ikan, mungkin terlalu menyederhanakan beberapa realitas dunia. Ketika asumsi Anda tentang dunia tidak benar, itu dapat menempatkan Anda pada risiko kehilangan kendali atas berbagai hal. Terkadang kita menyebutnya “bahaya”. Dengan cara yang sama robot kecil kita berjuang untuk bertahan hidup melawan alam semesta yang tidak dikenal, begitu juga kita semua. Ini adalah wawasan yang kuat untuk ahli robotik.]
Simulator Robot yang Dapat Diprogram
Simulator yang saya buat ditulis dengan Python dan dengan sangat cerdik dijuluki Sobot Rimulator . Anda dapat menemukan v1.0.0 di GitHub. Itu tidak memiliki banyak lonceng dan peluit tetapi dibangun untuk melakukan satu hal dengan sangat baik: memberikan simulasi robot seluler yang akurat dan memberikan kerangka kerja sederhana untuk mempraktekkan pemrograman perangkat lunak robot. Meskipun selalu lebih baik memiliki robot sungguhan untuk dimainkan, simulator robot Python yang baik jauh lebih mudah diakses dan merupakan tempat yang bagus untuk memulai.
Dalam robot dunia nyata, perangkat lunak yang menghasilkan sinyal kontrol ("pengontrol") diperlukan untuk berjalan pada kecepatan yang sangat tinggi dan membuat perhitungan yang rumit. Ini mempengaruhi pilihan bahasa pemrograman robot mana yang terbaik untuk digunakan: Biasanya, C++ digunakan untuk skenario semacam ini, tetapi dalam aplikasi robotika yang lebih sederhana, Python adalah kompromi yang sangat baik antara kecepatan eksekusi dan kemudahan pengembangan dan pengujian.
Perangkat lunak yang saya tulis mensimulasikan robot penelitian kehidupan nyata yang disebut Khepera tetapi dapat disesuaikan dengan berbagai robot seluler dengan dimensi dan sensor yang berbeda. Karena saya mencoba memprogram simulator semirip mungkin dengan kemampuan robot asli, logika kontrol dapat dimuat ke dalam robot Khepera nyata dengan refactoring minimal, dan akan melakukan hal yang sama seperti robot simulasi. Fitur khusus yang diterapkan mengacu pada Khepera III, tetapi dapat dengan mudah disesuaikan dengan Khepera IV yang baru.
Dengan kata lain, pemrograman robot simulasi analog dengan pemrograman robot nyata. Ini sangat penting jika simulator akan berguna untuk mengembangkan dan mengevaluasi pendekatan perangkat lunak kontrol yang berbeda.
Dalam tutorial ini, saya akan menjelaskan arsitektur perangkat lunak kontrol robot yang disertakan dengan v1.0.0 dari Sobot Rimulator , dan memberikan cuplikan dari sumber Python (dengan sedikit modifikasi untuk kejelasan). Namun, saya mendorong Anda untuk menyelami sumbernya dan mengacaukannya. Simulator telah bercabang dan digunakan untuk mengontrol robot seluler yang berbeda, termasuk Roomba2 dari iRobot. Demikian juga, jangan ragu untuk memotong proyek dan memperbaikinya.
Logika kontrol robot dibatasi untuk kelas/file Python ini:
-
models/supervisor.py—kelas ini bertanggung jawab atas interaksi antara dunia simulasi di sekitar robot dan robot itu sendiri. Ini mengembangkan mesin status robot kami dan memicu pengontrol untuk menghitung perilaku yang diinginkan. -
models/supervisor_state_machine.py—kelas ini mewakili berbagai status di mana robot dapat berada, bergantung pada interpretasinya terhadap sensor. - File-file dalam direktori
models/controllers—kelas-kelas ini mengimplementasikan perilaku robot yang berbeda dengan keadaan lingkungan yang diketahui. Secara khusus, pengontrol tertentu dipilih tergantung pada mesin negara.
Target
Robot, seperti halnya manusia, membutuhkan tujuan dalam hidup. Tujuan dari perangkat lunak kami yang mengendalikan robot ini akan sangat sederhana: Ia akan berusaha mencapai titik tujuan yang telah ditentukan. Ini biasanya fitur dasar yang harus dimiliki setiap robot bergerak, mulai dari mobil otonom hingga penyedot debu robot. Koordinat tujuan diprogram ke dalam perangkat lunak kontrol sebelum robot diaktifkan tetapi dapat dihasilkan dari aplikasi Python tambahan yang mengawasi pergerakan robot. Misalnya, bayangkan mengemudi melalui banyak titik jalan.
Namun, untuk memperumit masalah, lingkungan robot dapat dipenuhi dengan rintangan. Robot TIDAK BOLEH bertabrakan dengan rintangan dalam perjalanannya ke gawang. Oleh karena itu, jika robot menemui rintangan, ia harus mencari jalan agar dapat melanjutkan perjalanannya ke tujuan.
Robot yang Dapat Diprogram
Setiap robot hadir dengan kemampuan dan masalah kontrol yang berbeda. Mari berkenalan dengan robot simulasi yang dapat diprogram.
Hal pertama yang perlu diperhatikan adalah, dalam panduan ini, robot kita akan menjadi robot bergerak otonom . Ini berarti bahwa ia akan bergerak di ruang angkasa dengan bebas dan akan melakukannya di bawah kendalinya sendiri. Ini berbeda dengan, katakanlah, robot kendali jarak jauh (yang tidak otonom) atau lengan robot pabrik (yang tidak bergerak). Robot kita harus mencari tahu sendiri bagaimana mencapai tujuannya dan bertahan hidup di lingkungannya. Ini terbukti menjadi tantangan yang sangat sulit bagi programmer robotika pemula.
Input Kontrol: Sensor
Ada banyak cara berbeda yang bisa dilakukan robot untuk memantau lingkungannya. Ini dapat mencakup apa saja dari sensor jarak, sensor cahaya, bumper, kamera, dan sebagainya. Selain itu, robot dapat berkomunikasi dengan sensor eksternal yang memberi mereka informasi yang tidak dapat mereka amati secara langsung.
Robot referensi kami dilengkapi dengan sembilan sensor inframerah — model yang lebih baru memiliki delapan sensor jarak inframerah dan lima ultrasonik — yang disusun dalam “rok” di segala arah. Ada lebih banyak sensor yang menghadap ke depan robot daripada ke belakang karena biasanya lebih penting bagi robot untuk mengetahui apa yang ada di depannya daripada apa yang ada di belakangnya.
Selain sensor jarak, robot memiliki sepasang ticker roda yang melacak pergerakan roda. Ini memungkinkan Anda untuk melacak berapa banyak putaran yang dilakukan setiap roda, dengan satu putaran penuh ke depan menjadi 2.765 tick. Berputar ke arah yang berlawanan menghitung mundur, mengurangi jumlah centang alih-alih meningkatkannya. Anda tidak perlu khawatir tentang angka tertentu dalam tutorial ini karena perangkat lunak yang akan kami tulis menggunakan jarak tempuh yang dinyatakan dalam meter. Nanti saya akan menunjukkan cara menghitungnya dari kutu dengan fungsi Python yang mudah.
Output Kontrol: Mobilitas
Beberapa robot bergerak dengan kaki. Ada yang menggelinding seperti bola. Beberapa bahkan merayap seperti ular.
Robot kami adalah robot penggerak diferensial, artinya robot ini berputar dengan dua roda. Ketika kedua roda berputar dengan kecepatan yang sama, robot bergerak dalam garis lurus. Ketika roda bergerak dengan kecepatan yang berbeda, robot berputar. Dengan demikian, mengendalikan pergerakan robot ini menjadi pengontrol kecepatan putaran kedua roda ini dengan benar.
API
Di Sobot Rimulator, pemisahan antara "komputer" robot dan dunia fisik (simulasi) diwujudkan oleh file robot_supervisor_interface.py , yang mendefinisikan seluruh API untuk berinteraksi dengan sensor dan motor "robot nyata":
-
read_proximity_sensors()mengembalikan larik sembilan nilai dalam format asli sensor -
read_wheel_encoders()mengembalikan larik dua nilai yang menunjukkan jumlah kutu sejak awal -
set_wheel_drive_rates( v_l, v_r )mengambil dua nilai (dalam radian per detik) dan menyetel kecepatan roda kiri dan kanan ke dua nilai tersebut
Antarmuka ini secara internal menggunakan objek robot yang menyediakan data dari sensor dan kemungkinan untuk menggerakkan motor atau roda. Jika Anda ingin membuat robot yang berbeda, Anda hanya perlu menyediakan kelas robot Python yang berbeda yang dapat digunakan oleh antarmuka yang sama, dan kode lainnya (pengontrol, pengawas, dan simulator) akan bekerja di luar kotak!
Simulator
Karena Anda akan menggunakan robot nyata di dunia nyata tanpa terlalu memperhatikan hukum fisika yang terlibat, Anda dapat mengabaikan bagaimana robot disimulasikan dan langsung beralih ke cara perangkat lunak pengontrol diprogram, karena akan hampir sama. antara dunia nyata dan simulasi. Namun jika Anda penasaran, saya akan memperkenalkannya secara singkat di sini.
File world.py adalah kelas Python yang mewakili dunia simulasi, dengan robot dan rintangan di dalamnya. Fungsi langkah di dalam kelas ini menangani perkembangan dunia sederhana kita dengan:
- Menerapkan aturan fisika pada gerakan robot
- Mengingat tabrakan dengan rintangan
- Memberikan nilai baru untuk sensor robot
Pada akhirnya, ia memanggil pengawas robot yang bertanggung jawab untuk mengeksekusi perangkat lunak otak robot.
Fungsi langkah dijalankan dalam satu lingkaran sehingga robot.step_motion() menggerakkan robot menggunakan kecepatan roda yang dihitung oleh supervisor pada langkah simulasi sebelumnya.
# step the simulation through one time interval def step( self ): dt = self.dt # step all the robots for robot in self.robots: # step robot motion robot.step_motion( dt ) # apply physics interactions self.physics.apply_physics() # NOTE: The supervisors must run last to ensure they are observing the "current" world # step all of the supervisors for supervisor in self.supervisors: supervisor.step( dt ) # increment world time self.world_time += dt Fungsi apply_physics() secara internal memperbarui nilai sensor jarak robot sehingga supervisor dapat memperkirakan lingkungan pada langkah simulasi saat ini. Konsep yang sama berlaku untuk encoder.
Model Sederhana
Pertama, robot kita akan memiliki model yang sangat sederhana. Itu akan membuat banyak asumsi tentang dunia. Beberapa yang penting antara lain:
- Medannya selalu datar dan rata
- Rintangan tidak pernah bulat
- Roda tidak pernah tergelincir
- Tidak ada yang akan mendorong robot itu
- Sensor tidak pernah gagal atau memberikan pembacaan yang salah
- Roda selalu berputar saat disuruh
Meskipun sebagian besar asumsi ini masuk akal di dalam lingkungan seperti rumah, rintangan bulat bisa saja ada. Perangkat lunak penghindar rintangan kami memiliki implementasi yang sederhana dan mengikuti batas rintangan untuk melewatinya. Kami akan memberi petunjuk kepada pembaca tentang cara meningkatkan kerangka kontrol robot kami dengan pemeriksaan tambahan untuk menghindari rintangan melingkar.
Lingkaran Kontrol
Kami sekarang akan masuk ke inti perangkat lunak kontrol kami dan menjelaskan perilaku yang ingin kami program di dalam robot. Perilaku tambahan dapat ditambahkan ke kerangka kerja ini, dan Anda harus mencoba ide Anda sendiri setelah selesai membaca! Perangkat lunak robotika berbasis perilaku telah diusulkan lebih dari 20 tahun yang lalu dan masih merupakan alat yang ampuh untuk robotika seluler. Sebagai contoh, pada tahun 2007 serangkaian perilaku digunakan dalam DARPA Urban Challenge—kompetisi pertama untuk mobil yang dapat mengemudi secara otonom!
Robot adalah sistem yang dinamis. Keadaan robot, pembacaan sensornya, dan efek dari sinyal kontrolnya selalu berubah-ubah. Mengontrol cara acara bermain melibatkan tiga langkah berikut:
- Terapkan sinyal kontrol.
- Ukur hasilnya.
- Hasilkan sinyal kontrol baru yang dihitung untuk membawa kita lebih dekat ke tujuan kita.
Langkah-langkah ini diulangi terus menerus sampai kita mencapai tujuan kita. Semakin sering kita melakukan ini per detik, kontrol yang lebih baik akan kita miliki atas sistem. Robot Sobot Rimulator mengulangi langkah-langkah ini 20 kali per detik (20 Hz), tetapi banyak robot harus melakukan ini ribuan atau jutaan kali per detik untuk memiliki kontrol yang memadai. Ingat pengantar kami sebelumnya tentang bahasa pemrograman robot yang berbeda untuk sistem robotika yang berbeda dan persyaratan kecepatan.
Secara umum, setiap kali robot kami melakukan pengukuran dengan sensornya, ia menggunakan pengukuran ini untuk memperbarui perkiraan internalnya tentang keadaan dunia—misalnya, jarak dari tujuannya. Ini membandingkan keadaan ini dengan nilai referensi dari apa yang diinginkannya (untuk jarak, ia menginginkannya menjadi nol), dan menghitung kesalahan antara keadaan yang diinginkan dan keadaan sebenarnya. Setelah informasi ini diketahui, pembangkitan sinyal kontrol baru dapat dikurangi menjadi masalah meminimalkan kesalahan yang pada akhirnya akan menggerakkan robot ke arah tujuan.
Trik Bagus: Menyederhanakan Model
Untuk mengontrol robot yang ingin kita program, kita harus mengirim sinyal ke roda kiri yang memberitahu seberapa cepat untuk berbelok, dan sinyal terpisah ke roda kanan yang memberitahu seberapa cepat untuk berbelok. Sebut saja sinyal-sinyal ini v L dan v R . Namun, terus-menerus berpikir dalam hal v L dan v R sangat rumit. Alih-alih bertanya, "Seberapa cepat kita ingin roda kiri berputar, dan seberapa cepat kita ingin roda kanan berputar?" lebih wajar untuk bertanya, "Seberapa cepat kita ingin robot bergerak maju, dan seberapa cepat kita ingin robot itu berputar, atau mengubah arahnya?" Sebut saja parameter ini kecepatan v dan kecepatan sudut ( rotasi ) (baca “omega”). Ternyata kita dapat mendasarkan seluruh model kita pada v dan alih -alih v L dan v R , dan hanya setelah kita menentukan bagaimana kita ingin robot terprogram kita bergerak, secara matematis mengubah kedua nilai ini menjadi v L dan v R yang kita butuhkan untuk benar-benar mengontrol roda robot. Ini dikenal sebagai model kontrol unicycle .
Berikut adalah kode Python yang mengimplementasikan transformasi terakhir di supervisor.py . Perhatikan bahwa jika adalah 0 , kedua roda akan berputar dengan kecepatan yang sama:
# generate and send the correct commands to the robot def _send_robot_commands( self ): # ... v_l, v_r = self._uni_to_diff( v, omega ) self.robot.set_wheel_drive_rates( v_l, v_r ) def _uni_to_diff( self, v, omega ): # v = translational velocity (m/s) # omega = angular velocity (rad/s) R = self.robot_wheel_radius L = self.robot_wheel_base_length v_l = ( (2.0 * v) - (omega*L) ) / (2.0 * R) v_r = ( (2.0 * v) + (omega*L) ) / (2.0 * R) return v_l, v_rMemperkirakan Status: Robot, Kenali Dirimu
Menggunakan sensornya, robot harus mencoba memperkirakan keadaan lingkungan serta keadaannya sendiri. Estimasi ini tidak akan pernah sempurna, tetapi harus cukup baik karena robot akan mendasarkan semua keputusannya pada estimasi ini. Dengan menggunakan sensor proximity dan wheel ticker saja, ia harus mencoba menebak hal berikut:
- Arah rintangan
- Jarak dari rintangan
- Posisi robot
- Judul robot
Dua properti pertama ditentukan oleh pembacaan sensor jarak dan cukup mudah. Fungsi API read_proximity_sensors() mengembalikan larik sembilan nilai, satu untuk setiap sensor. Kita tahu sebelumnya bahwa pembacaan ketujuh, misalnya, sesuai dengan sensor yang menunjuk 75 derajat ke kanan robot.
Jadi, jika nilai ini menunjukkan pembacaan yang sesuai dengan jarak 0,1 meter, kita tahu bahwa ada rintangan yang berjarak 0,1 meter, 75 derajat ke kiri. Jika tidak ada halangan, sensor akan mengembalikan pembacaan kisaran maksimum 0,2 meter. Jadi, jika kita membaca 0,2 meter pada sensor tujuh, kita akan menganggap bahwa sebenarnya tidak ada hambatan ke arah itu.
Karena cara kerja sensor infra merah (mengukur pantulan infra merah), angka yang dikembalikan adalah transformasi non-linier dari jarak sebenarnya yang terdeteksi. Dengan demikian, fungsi Python untuk menentukan jarak yang ditunjukkan harus mengubah pembacaan ini menjadi meter. Ini dilakukan di supervisor.py sebagai berikut:

# update the distances indicated by the proximity sensors def _update_proximity_sensor_distances( self ): self.proximity_sensor_distances = [ 0.02-( log(readval/3960.0) )/30.0 for readval in self.robot.read_proximity_sensors() ]Sekali lagi, kami memiliki model sensor khusus dalam kerangka robot Python ini, sementara di dunia nyata, sensor datang dengan perangkat lunak yang menyertainya yang harus menyediakan fungsi konversi serupa dari nilai non-linear ke meter.
Menentukan posisi dan heading robot (bersama-sama dikenal sebagai pose dalam pemrograman robotika) agak lebih menantang. Robot kami menggunakan odometri untuk memperkirakan posenya. Di sinilah roda ticker masuk. Dengan mengukur berapa banyak setiap roda telah berputar sejak iterasi terakhir dari loop kontrol, adalah mungkin untuk mendapatkan perkiraan yang baik tentang bagaimana pose robot telah berubah—tetapi hanya jika perubahannya kecil .
Ini adalah salah satu alasan penting untuk mengulangi loop kontrol dengan sangat sering di robot dunia nyata, di mana motor yang menggerakkan roda mungkin tidak sempurna. Jika kita menunggu terlalu lama untuk mengukur ticker roda, kedua roda bisa melakukan cukup banyak, dan tidak mungkin untuk memperkirakan di mana kita berakhir.
Mengingat simulator perangkat lunak kami saat ini, kami dapat menjalankan komputasi odometri pada 20 Hz—frekuensi yang sama dengan pengontrol. Tapi itu bisa menjadi ide yang baik untuk memiliki utas Python terpisah yang berjalan lebih cepat untuk menangkap gerakan ticker yang lebih kecil.
Di bawah ini adalah fungsi odometri lengkap di supervisor.py yang memperbarui estimasi pose robot. Perhatikan bahwa pose robot terdiri dari koordinat x dan y , dan heading theta , yang diukur dalam radian dari sumbu X positif. x positif ke timur dan y positif ke utara. Jadi heading 0 menunjukkan bahwa robot menghadap langsung ke timur. Robot selalu mengasumsikan pose awalnya adalah (0, 0), 0 .
# update the estimated position of the robot using it's wheel encoder readings def _update_odometry( self ): R = self.robot_wheel_radius N = float( self.wheel_encoder_ticks_per_revolution ) # read the wheel encoder values ticks_left, ticks_right = self.robot.read_wheel_encoders() # get the difference in ticks since the last iteration d_ticks_left = ticks_left - self.prev_ticks_left d_ticks_right = ticks_right - self.prev_ticks_right # estimate the wheel movements d_left_wheel = 2*pi*R*( d_ticks_left / N ) d_right_wheel = 2*pi*R*( d_ticks_right / N ) d_center = 0.5 * ( d_left_wheel + d_right_wheel ) # calculate the new pose prev_x, prev_y, prev_theta = self.estimated_pose.scalar_unpack() new_x = prev_x + ( d_center * cos( prev_theta ) ) new_y = prev_y + ( d_center * sin( prev_theta ) ) new_theta = prev_theta + ( ( d_right_wheel - d_left_wheel ) / self.robot_wheel_base_length ) # update the pose estimate with the new values self.estimated_pose.scalar_update( new_x, new_y, new_theta ) # save the current tick count for the next iteration self.prev_ticks_left = ticks_left self.prev_ticks_right = ticks_rightSekarang robot kita mampu menghasilkan perkiraan yang baik tentang dunia nyata, mari gunakan informasi ini untuk mencapai tujuan kita.
Metode Pemrograman Robot Python: Perilaku Menuju Tujuan
Tujuan utama keberadaan robot kecil kami dalam tutorial pemrograman ini adalah untuk mencapai titik tujuan. Jadi bagaimana kita membuat roda berputar untuk sampai ke sana? Mari kita mulai dengan menyederhanakan pandangan dunia kita sedikit dan menganggap tidak ada hambatan di jalan.
Ini kemudian menjadi tugas sederhana dan dapat dengan mudah diprogram dengan Python. Jika kita maju sambil menghadapi gawang, kita akan sampai di sana. Berkat odometri kami, kami tahu apa koordinat dan arah kami saat ini. Kita juga tahu apa koordinat tujuannya karena sudah diprogram sebelumnya. Oleh karena itu, dengan menggunakan sedikit aljabar linier, kita dapat menentukan vektor dari lokasi kita ke tujuan, seperti pada go_to_goal_controller.py :
# return a go-to-goal heading vector in the robot's reference frame def calculate_gtg_heading_vector( self ): # get the inverse of the robot's pose robot_inv_pos, robot_inv_theta = self.supervisor.estimated_pose().inverse().vector_unpack() # calculate the goal vector in the robot's reference frame goal = self.supervisor.goal() goal = linalg.rotate_and_translate_vector( goal, robot_inv_theta, robot_inv_pos ) return goalPerhatikan bahwa kita mendapatkan vektor ke tujuan dalam kerangka referensi robot , dan BUKAN dalam koordinat dunia. Jika gawang berada pada sumbu X pada kerangka acuan robot, berarti gawang berada tepat di depan robot. Jadi, sudut vektor ini dari sumbu X adalah perbedaan antara heading kita dan heading yang kita inginkan. Dengan kata lain, itu adalah kesalahan antara keadaan kita saat ini dan apa yang kita inginkan dari keadaan kita saat ini. Oleh karena itu, kami ingin menyesuaikan kecepatan putar kami sehingga sudut antara heading dan gawang kami akan berubah menuju 0. Kami ingin meminimalkan kesalahan:
# calculate the error terms theta_d = atan2( self.gtg_heading_vector[1], self.gtg_heading_vector[0] ) # calculate angular velocity omega = self.kP * theta_d self.kP dalam cuplikan implementasi pengontrol Python di atas adalah gain kontrol. Ini adalah koefisien yang menentukan seberapa cepat kita berbelok sebanding dengan seberapa jauh dari tujuan yang kita hadapi. Jika error pada heading kita adalah 0 , maka kecepatan putarnya juga 0 . Dalam fungsi Python nyata di dalam file go_to_goal_controller.py , Anda akan melihat lebih banyak keuntungan serupa, karena kami menggunakan pengontrol PID alih-alih koefisien proporsional sederhana.
Sekarang kita memiliki kecepatan sudut , bagaimana kita menentukan kecepatan maju kita v ? Aturan umum yang baik adalah yang mungkin Anda ketahui secara naluriah: Jika kita tidak berbelok, kita bisa maju dengan kecepatan penuh, dan semakin cepat kita berbelok, semakin kita harus melambat. Ini umumnya membantu kami menjaga sistem kami stabil dan bertindak dalam batas-batas model kami. Jadi, v adalah fungsi dari . Di go_to_goal_controller.py persamaannya adalah:
# calculate translational velocity # velocity is v_max when omega is 0, # drops rapidly to zero as |omega| rises v = self.supervisor.v_max() / ( abs( omega ) + 1 )**0.5 Saran untuk menguraikan rumus ini adalah dengan mempertimbangkan bahwa kita biasanya melambat ketika mendekati tujuan untuk mencapainya dengan kecepatan nol. Bagaimana formula ini akan berubah? Itu harus mencakup penggantian v_max() dengan sesuatu yang sebanding dengan jarak. Oke, kita hampir menyelesaikan satu loop kontrol. Satu-satunya yang tersisa untuk dilakukan adalah mengubah dua parameter model unicycle ini menjadi kecepatan roda diferensial, dan mengirim sinyal ke roda. Berikut adalah contoh lintasan robot di bawah kontroler go-to-goal, tanpa hambatan:
Seperti yang bisa kita lihat, vektor ke tujuan adalah referensi yang efektif bagi kita untuk mendasarkan perhitungan kontrol kita. Ini adalah representasi internal dari "ke mana kita ingin pergi." Seperti yang akan kita lihat, satu-satunya perbedaan utama antara pergi ke tujuan dan perilaku lainnya adalah terkadang menuju tujuan adalah ide yang buruk, jadi kita harus menghitung vektor referensi yang berbeda.
Metode Pemrograman Robot Python: Perilaku Hindari-Hambatan
Menuju tujuan ketika ada rintangan di arah itu adalah contohnya. Alih-alih berlari cepat ke hal-hal di jalan kita, mari kita coba memprogram hukum kontrol yang membuat robot menghindarinya.
Untuk menyederhanakan skenario, sekarang mari kita lupakan titik tujuan sepenuhnya dan jadikan tujuan kita berikut ini: Ketika tidak ada halangan di depan kita, majulah. Ketika sebuah rintangan ditemui, berpalinglah darinya sampai tidak lagi di depan kita.
Dengan demikian, ketika tidak ada halangan di depan kita, kita ingin vektor referensi kita hanya mengarah ke depan. Maka akan menjadi nol dan v akan menjadi kecepatan maksimum. Namun, segera setelah kami mendeteksi penghalang dengan sensor jarak kami, kami ingin vektor referensi menunjuk ke arah mana pun yang jauh dari penghalang. Ini akan menyebabkan menembak ke atas untuk menjauhkan kita dari rintangan, dan menyebabkan v jatuh untuk memastikan kita tidak menabrak rintangan secara tidak sengaja dalam prosesnya.
Cara yang rapi untuk menghasilkan vektor referensi yang kita inginkan adalah dengan mengubah sembilan pembacaan kedekatan kita menjadi vektor, dan mengambil jumlah tertimbang. Ketika tidak ada hambatan yang terdeteksi, vektor akan dijumlahkan secara simetris, menghasilkan vektor referensi yang mengarah lurus ke depan sesuai keinginan. Tetapi jika sebuah sensor pada, katakanlah, sisi kanan mengambil hambatan, itu akan menyumbangkan vektor yang lebih kecil ke penjumlahan, dan hasilnya akan menjadi vektor referensi yang digeser ke kiri.
Untuk robot umum dengan penempatan sensor yang berbeda, ide yang sama dapat diterapkan tetapi mungkin memerlukan perubahan bobot dan/atau perawatan tambahan saat sensor simetris di depan dan di belakang robot, karena jumlah bobot bisa menjadi nol .
Berikut adalah kode yang melakukan ini di avoid_obstacles_controller.py :
# sensor gains (weights) self.sensor_gains = [ 1.0+( (0.4*abs(p.theta)) / pi ) for p in supervisor.proximity_sensor_placements() ] # ... # return an obstacle avoidance vector in the robot's reference frame # also returns vectors to detected obstacles in the robot's reference frame def calculate_ao_heading_vector( self ): # initialize vector obstacle_vectors = [ [ 0.0, 0.0 ] ] * len( self.proximity_sensor_placements ) ao_heading_vector = [ 0.0, 0.0 ] # get the distances indicated by the robot's sensor readings sensor_distances = self.supervisor.proximity_sensor_distances() # calculate the position of detected obstacles and find an avoidance vector robot_pos, robot_theta = self.supervisor.estimated_pose().vector_unpack() for i in range( len( sensor_distances ) ): # calculate the position of the obstacle sensor_pos, sensor_theta = self.proximity_sensor_placements[i].vector_unpack() vector = [ sensor_distances[i], 0.0 ] vector = linalg.rotate_and_translate_vector( vector, sensor_theta, sensor_pos ) obstacle_vectors[i] = vector # store the obstacle vectors in the robot's reference frame # accumulate the heading vector within the robot's reference frame ao_heading_vector = linalg.add( ao_heading_vector, linalg.scale( vector, self.sensor_gains[i] ) ) return ao_heading_vector, obstacle_vectors Menggunakan ao_heading_vector yang dihasilkan sebagai referensi kami agar robot mencoba mencocokkan, berikut adalah hasil menjalankan perangkat lunak robot dalam simulasi hanya menggunakan pengontrol penghindar-penghalang, mengabaikan titik tujuan sepenuhnya. Robot memantul tanpa tujuan, tetapi tidak pernah bertabrakan dengan rintangan, dan bahkan berhasil menavigasi beberapa ruang yang sangat sempit:
Metode Pemrograman Robot Python: Hybrid Automata (Mesin Status Perilaku)
Sejauh ini kami telah menjelaskan dua perilaku—menuju tujuan dan menghindari rintangan—secara terpisah. Keduanya menjalankan fungsinya dengan mengagumkan, tetapi untuk berhasil mencapai tujuan di lingkungan yang penuh rintangan, kita perlu menggabungkannya.
Solusi yang akan kami kembangkan terletak pada kelas mesin yang memiliki sebutan hybrid automata yang terdengar sangat keren . Sebuah robot hybrid diprogram dengan beberapa perilaku yang berbeda, atau mode, serta mesin negara yang mengawasi. Mesin negara pengawas beralih dari satu mode ke mode lainnya dalam waktu yang berbeda (ketika tujuan tercapai atau lingkungan tiba-tiba berubah terlalu banyak), sementara setiap perilaku menggunakan sensor dan roda untuk bereaksi terus menerus terhadap perubahan lingkungan. Solusinya disebut hybrid karena berkembang baik secara diskrit dan terus menerus.
Kerangka kerja robot Python kami mengimplementasikan mesin status dalam file supervisor_state_machine.py .
Dilengkapi dengan dua perilaku praktis kami, logika sederhana menyarankan dirinya sendiri: Ketika tidak ada hambatan yang terdeteksi, gunakan perilaku pergi-ke-tujuan. Saat rintangan terdeteksi, alihkan ke perilaku menghindari rintangan sampai rintangan tidak lagi terdeteksi.
Ternyata, bagaimanapun, logika ini akan menghasilkan banyak masalah. Apa yang akan cenderung dilakukan sistem ini ketika menghadapi rintangan adalah berpaling darinya, kemudian segera setelah menjauh darinya, berbalik ke kanan dan menabraknya lagi. Hasilnya adalah pergantian cepat tanpa akhir yang membuat robot tidak berguna. In the worst case, the robot may switch between behaviors with every iteration of the control loop—a state known as a Zeno condition .
There are multiple solutions to this problem, and readers that are looking for deeper knowledge should check, for example, the DAMN software architecture.
What we need for our simple simulated robot is an easier solution: One more behavior specialized with the task of getting around an obstacle and reaching the other side.
Python Robot Programming Methods: Follow-Wall Behavior
Here's the idea: When we encounter an obstacle, take the two sensor readings that are closest to the obstacle and use them to estimate the surface of the obstacle. Then, simply set our reference vector to be parallel to this surface. Keep following this wall until A) the obstacle is no longer between us and the goal, and B) we are closer to the goal than we were when we started. Then we can be certain we have navigated the obstacle properly.
With our limited information, we can't say for certain whether it will be faster to go around the obstacle to the left or to the right. To make up our minds, we select the direction that will move us closer to the goal immediately. To figure out which way that is, we need to know the reference vectors of the go-to-goal behavior and the avoid-obstacle behavior, as well as both of the possible follow-wall reference vectors. Here is an illustration of how the final decision is made (in this case, the robot will choose to go left):
Determining the follow-wall reference vectors turns out to be a bit more involved than either the avoid-obstacle or go-to-goal reference vectors. Take a look at the Python code in follow_wall_controller.py to see how it's done.
Final Control Design
The final control design uses the follow-wall behavior for almost all encounters with obstacles. However, if the robot finds itself in a tight spot, dangerously close to a collision, it will switch to pure avoid-obstacles mode until it is a safer distance away, and then return to follow-wall. Once obstacles have been successfully negotiated, the robot switches to go-to-goal. Here is the final state diagram, which is programmed inside the supervisor_state_machine.py :
Here is the robot successfully navigating a crowded environment using this control scheme:
An additional feature of the state machine that you can try to implement is a way to avoid circular obstacles by switching to go-to-goal as soon as possible instead of following the obstacle border until the end (which does not exist for circular objects!)
Tweak, Tweak, Tweak: Trial and Error
The control scheme that comes with Sobot Rimulator is very finely tuned. It took many hours of tweaking one little variable here, and another equation there, to get it to work in a way I was satisfied with. Robotics programming often involves a great deal of plain old trial-and-error. Robots are very complex and there are few shortcuts to getting them to behave optimally in a robot simulator environment…at least, not much short of outright machine learning, but that's a whole other can of worms.
I encourage you to play with the control variables in Sobot Rimulator and observe and attempt to interpret the results. Changes to the following all have profound effects on the simulated robot's behavior:
- The error gain
kPin each controller - The sensor gains used by the avoid-obstacles controller
- The calculation of v as a function of ω in each controller
- The obstacle standoff distance used by the follow-wall controller
- The switching conditions used by
supervisor_state_machine.py - Pretty much anything else
When Programmable Robots Fail
We've done a lot of work to get to this point, and this robot seems pretty clever. Yet, if you run Sobot Rimulator through several randomized maps, it won't be long before you find one that this robot can't deal with. Sometimes it drives itself directly into tight corners and collides. Sometimes it just oscillates back and forth endlessly on the wrong side of an obstacle. Occasionally it is legitimately imprisoned with no possible path to the goal. After all of our testing and tweaking, sometimes we must come to the conclusion that the model we are working with just isn't up to the job, and we have to change the design or add functionality.
In the mobile robot universe, our little robot's “brain” is on the simpler end of the spectrum. Many of the failure cases it encounters could be overcome by adding some more advanced software to the mix. More advanced robots make use of techniques such as mapping , to remember where it's been and avoid trying the same things over and over; heuristics , to generate acceptable decisions when there is no perfect decision to be found; and machine learning , to more perfectly tune the various control parameters governing the robot's behavior.
A Sample of What's to Come
Robots are already doing so much for us, and they are only going to be doing more in the future. While even basic robotics programming is a tough field of study requiring great patience, it is also a fascinating and immensely rewarding one.
In this tutorial, we learned how to develop reactive control software for a robot using the high-level programming language Python. But there are many more advanced concepts that can be learned and tested quickly with a Python robot framework similar to the one we prototyped here. I hope you will consider getting involved in the shaping of things to come!
Acknowledgement: I would like to thank Dr. Magnus Egerstedt and Jean-Pierre de la Croix of the Georgia Institute of Technology for teaching me all this stuff, and for their enthusiasm for my work on Sobot Rimulator.
