Bir Giriş Robotu Programlama Eğitimi
Yayınlanan: 2022-03-11Kabul edelim, robotlar havalı. Ayrıca bir gün dünyayı yönetecekler ve umarım o zaman zavallı yumuşak etli yaratıcılarına (diğer adıyla robotik geliştiricileri) acıyacaklar ve bolluk dolu bir uzay ütopyası inşa etmemize yardım edecekler. Şaka yapıyorum elbette, ama sadece bir çeşit.
Konu üzerinde biraz etki sahibi olma tutkumla, geçen yıl otonom robot kontrol teorisi dersi aldım ve bu, kontrol teorisini basit, mobil, programlanabilir bir robot üzerinde uygulamama izin veren Python tabanlı bir robotik simülatör inşa etmemle sonuçlandı. .
Bu makalede, kontrol yazılımı geliştirmek için bir Python robot çerçevesinin nasıl kullanılacağını, simüle edilmiş robotum için geliştirdiğim kontrol şemasını anlatacağım, çevresiyle nasıl etkileşime girdiğini ve hedeflerine nasıl ulaştığını göstereceğim ve bazılarını tartışacağım. yol boyunca karşılaştığım robotik programlamanın temel zorlukları.
Yeni başlayanlar için robotik programlamayla ilgili bu öğreticiyi takip etmek için iki şey hakkında temel bilgilere sahip olmalısınız:
- Matematik—bazı trigonometrik fonksiyonları ve vektörleri kullanacağız
- Python—Python en popüler temel robot programlama dilleri arasında olduğundan—temel Python kitaplıklarını ve işlevlerini kullanacağız.
Burada gösterilen kod parçacıkları, sınıflara ve arayüzlere dayanan tüm simülatörün yalnızca bir parçasıdır, bu nedenle kodu doğrudan okumak için Python ve nesne yönelimli programlama konusunda biraz deneyime ihtiyacınız olabilir.
Son olarak, bu öğreticiyi daha iyi takip etmenize yardımcı olacak isteğe bağlı konular, durum makinesinin ne olduğunu ve menzil sensörlerinin ve kodlayıcıların nasıl çalıştığını bilmektir.
Programlanabilir Robotun Zorluğu: Algıya Karşı Gerçeklik ve Kontrolün Kırılganlığı
Tüm robotiklerin temel zorluğu şudur: Çevrenin gerçek durumunu asla bilmek imkansızdır. Robot kontrol yazılımı, sensörleri tarafından döndürülen ölçümlere dayanarak gerçek dünyanın durumunu yalnızca tahmin edebilir. Yalnızca kontrol sinyallerinin üretilmesi yoluyla gerçek dünyanın durumunu değiştirmeye çalışabilir.
Bu nedenle, kontrol tasarımındaki ilk adımlardan biri, sensör okumalarımızı yorumlamak ve karar vermek için model olarak bilinen gerçek dünyanın bir soyutlamasını bulmaktır. Gerçek dünya, modelin varsayımlarına göre davrandığı sürece, iyi tahminlerde bulunabilir ve kontrol uygulayabiliriz. Ancak gerçek dünya bu varsayımlardan sapar sapmaz, artık iyi tahminler yapamayacak ve kontrol kaybedilecek. Çoğu zaman, kontrol bir kez kaybedildiğinde, bir daha asla geri alınamaz. (Eğer bazı hayırsever dış güçler onu geri getirmedikçe.)
Bu, robotik programlamanın bu kadar zor olmasının temel nedenlerinden biridir. Laboratuarda el becerisi, navigasyon veya ekip çalışması gibi harika başarılar sergileyen en son araştırma robotunun videolarını sık sık görüyoruz ve "Bu neden gerçek dünyada kullanılmıyor?" diye sormaya başlıyoruz. Bir daha böyle bir video gördüğünüzde, laboratuvar ortamının ne kadar kontrollü olduğuna bir bakın. Çoğu durumda, bu robotlar bu etkileyici görevleri ancak çevresel koşullar kendi iç modelinin dar sınırları içinde kaldığı sürece gerçekleştirebilir. Bu nedenle, robotiğin ilerlemesinin bir anahtarı, daha karmaşık, esnek ve sağlam modellerin geliştirilmesidir ve söz konusu ilerleme, mevcut hesaplama kaynaklarının sınırlarına tabidir.
[Yan Not: Hem filozoflar hem de psikologlar, canlıların da duyularının onlara ne söylediğine dair kendi içsel algılarına bağımlılıktan muzdarip olduklarını belirteceklerdir. Robotikteki birçok ilerleme, canlıları gözlemlemek ve beklenmedik uyaranlara nasıl tepki verdiklerini görmekten gelir. Bunu düşün. Dünyanın içsel modeliniz nedir? Bir karıncanınkinden ve bir balığınkinden farklı mı? (İnşallah.) Ancak karınca ve balık gibi, dünyanın bazı gerçeklerini fazla basitleştirmesi muhtemeldir. Dünya hakkındaki varsayımlarınız doğru olmadığında, sizi bir şeylerin kontrolünü kaybetme riskine sokabilir. Bazen buna “tehlike” diyoruz. Aynı şekilde küçük robotumuz bilinmeyen evrene karşı hayatta kalmak için mücadele ediyor, hepimiz de öyle. Bu, robotikçiler için güçlü bir içgörüdür.]
Programlanabilir Robot Simülatörü
Yaptığım simülatör Python ile yazılmış ve çok akıllıca Sobot Rimulator olarak adlandırılmıştır. v1.0.0'ı GitHub'da bulabilirsiniz. Çok fazla özelliği yoktur, ancak bir şeyi çok iyi yapmak için yapılmıştır: bir mobil robotun doğru bir simülasyonunu sağlamak ve hevesli bir robot uzmanına robot yazılım programlaması için basit bir çerçeve vermek. Oynamak için gerçek bir robota sahip olmak her zaman daha iyi olsa da, iyi bir Python robot simülatörü çok daha erişilebilirdir ve başlamak için harika bir yerdir.
Gerçek dünyadaki robotlarda, kontrol sinyallerini üreten yazılımın ("kontrolör") çok yüksek bir hızda çalışması ve karmaşık hesaplamalar yapması gerekir. Bu, hangi robot programlama dillerinin en iyi kullanılacağı seçimini etkiler: Genellikle, bu tür senaryolar için C++ kullanılır, ancak daha basit robotik uygulamalarında Python, yürütme hızı ile geliştirme ve test kolaylığı arasında çok iyi bir uzlaşmadır.
Yazdığım yazılım, Khepera adlı gerçek hayattaki bir araştırma robotunu simüle ediyor ancak farklı boyut ve sensörlere sahip bir dizi mobil robota uyarlanabiliyor. Simülatörü mümkün olduğu kadar gerçek robotun yeteneklerine benzer şekilde programlamaya çalıştığım için, kontrol mantığı minimum yeniden düzenleme ile gerçek bir Khepera robotuna yüklenebilir ve simüle edilmiş robotla aynı işlevi görecektir. Uygulanan belirli özellikler Khepera III'e atıfta bulunur, ancak bunlar yeni Khepera IV'e kolayca uyarlanabilir.
Başka bir deyişle, simüle edilmiş bir robotu programlamak, gerçek bir robotu programlamaya benzer. Simülatör, farklı kontrol yazılımı yaklaşımlarını geliştirmek ve değerlendirmek için herhangi bir şekilde kullanılacaksa, bu çok önemlidir.
Bu derste, Sobot Rimulator v1.0.0 ile gelen robot kontrol yazılımı mimarisini açıklayacağım ve Python kaynağından snippet'ler sağlayacağım (açıklık için küçük değişikliklerle). Ancak, sizi kaynağa dalmaya ve ortalığı karıştırmaya teşvik ediyorum. Simülatör, iRobot'tan bir Roomba2 dahil olmak üzere farklı mobil robotları kontrol etmek için çatallandı ve kullanıldı. Aynı şekilde, lütfen projeyi çatallamaktan ve geliştirmekten çekinmeyin.
Robotun kontrol mantığı şu Python sınıfları/dosyaları ile sınırlıdır:
-
models/supervisor.py
— bu sınıf, robotun etrafındaki simüle edilmiş dünya ile robotun kendisi arasındaki etkileşimden sorumludur. Robot durum makinemizi geliştirir ve istenen davranışı hesaplamak için kontrolörleri tetikler. -
models/supervisor_state_machine.py
— bu sınıf, sensörlerin yorumuna bağlı olarak robotun içinde bulunabileceği farklı durumları temsil eder. -
models/controllers
dizinindeki dosyalar—bu sınıflar, ortamın bilinen bir durumuna göre robotun farklı davranışlarını uygular. Özellikle, durum makinesine bağlı olarak belirli bir kontrolör seçilir.
Amaç
Robotlar, insanlar gibi, hayatta bir amaca ihtiyaç duyarlar. Bu robotu kontrol eden yazılımımızın amacı çok basit olacak: Önceden belirlenmiş bir hedef noktasına doğru yol almaya çalışacak. Bu genellikle otonom arabalardan robotik elektrikli süpürgelere kadar herhangi bir mobil robotun sahip olması gereken temel özelliktir. Hedefin koordinatları, robot etkinleştirilmeden önce kontrol yazılımına programlanır, ancak robot hareketlerini denetleyen ek bir Python uygulamasından oluşturulabilir. Örneğin, birden fazla geçiş noktasından geçtiğini düşünün.
Ancak, işleri karmaşık hale getirmek için robotun çevresi engellerle dolu olabilir. Robot, hedefe giden yolda bir engelle ÇarpışmaYABİLİR. Bu nedenle, robot bir engelle karşılaşırsa, hedefe doğru yoluna devam edebilmesi için yolunu bulması gerekecektir.
Programlanabilir Robot
Her robot farklı yetenekler ve kontrol endişeleri ile birlikte gelir. Simüle edilmiş programlanabilir robotumuzu tanıyalım.
İlk olarak bu kılavuzda robotumuz otonom bir mobil robot olacak. Bu, uzayda serbestçe dolaşacağı ve bunu kendi kontrolü altında yapacağı anlamına gelir. Bu, örneğin bir uzaktan kumandalı robotun (otonom olmayan) veya bir fabrika robot kolunun (mobil olmayan) aksine. Robotumuz, hedeflerine nasıl ulaşacağını ve çevresinde nasıl hayatta kalacağını kendisi bulmalıdır. Bu, acemi robotik programcıları için şaşırtıcı derecede zor bir zorluk olduğunu kanıtlıyor.
Kontrol Girişleri: Sensörler
Bir robotun çevresini izlemek için donatılabileceği birçok farklı yol vardır. Bunlar, yakınlık sensörlerinden, ışık sensörlerinden, tamponlardan, kameralardan vb. her şeyi içerebilir. Ayrıca robotlar, kendilerine doğrudan gözlemleyemedikleri bilgileri veren harici sensörlerle iletişim kurabilir.
Referans robotumuz, her yönde bir "etek" içinde düzenlenmiş dokuz kızılötesi sensörle donatılmıştır (yeni modelde sekiz kızılötesi ve beş ultrasonik yakınlık sensörü vardır). Robotun önüne, arkasından daha fazla bakan sensör vardır, çünkü robotun önünde ne olduğunu bilmesi, arkasında ne olduğundan daha önemlidir.
Robot, yakınlık sensörlerine ek olarak, tekerlek hareketini izleyen bir çift tekerlek işaretine sahiptir. Bunlar, bir tekerleğin bir tam ileri dönüşü 2.765 tik olmak üzere her bir tekerleğin kaç dönüş yaptığını takip etmenizi sağlar. Ters yönde dönerek geriye doğru sayar, tik sayısını artırmak yerine azaltır. Bu derste belirli sayılar hakkında endişelenmenize gerek yok çünkü yazacağımız yazılım kat edilen mesafeyi metre olarak ifade ediyor. Daha sonra, kolay bir Python işleviyle kenelerden nasıl hesaplanacağını göstereceğim.
Kontrol Çıkışları: Hareketlilik
Bazı robotlar ayakları üzerinde hareket eder. Bazıları top gibi yuvarlanır. Hatta bazıları yılan gibi kayıyor.
Robotumuz diferansiyel tahrikli bir robottur, yani iki tekerlek üzerinde döner. Her iki tekerlek de aynı hızda döndüğünde robot düz bir çizgide hareket eder. Tekerlekler farklı hızlarda hareket ettiğinde robot döner. Böylece, bu robotun hareketini kontrol etmek, bu iki tekerleğin her birinin dönme hızlarını düzgün bir şekilde kontrol etmeye gelir.
API
Sobot Rimulator'da, robot "bilgisayar" ve (simüle edilmiş) fiziksel dünya arasındaki ayrım, "gerçek robot" sensörleri ve motorları ile etkileşim için tüm API'yi tanımlayan robot_supervisor_interface.py
dosyası tarafından somutlaştırılır:
-
read_proximity_sensors()
, sensörlerin yerel biçiminde dokuz değerlik bir dizi döndürür -
read_wheel_encoders()
, başlangıçtan bu yana toplam keneleri gösteren iki değerden oluşan bir dizi döndürür -
set_wheel_drive_rates( v_l, v_r )
iki değer alır (saniyede radyan cinsinden) ve tekerleklerin sol ve sağ hızını bu iki değere ayarlar
Bu arayüz dahili olarak, sensörlerden gelen verileri ve motorları veya tekerlekleri hareket ettirme olanağı sağlayan bir robot nesnesi kullanır. Farklı bir robot oluşturmak istiyorsanız, aynı arayüz tarafından kullanılabilecek farklı bir Python robot sınıfı sağlamanız yeterlidir; kodun geri kalanı (kontrolörler, süpervizör ve simülatör) kutudan çıktığı gibi çalışacaktır!
Simülatör
Gerçek dünyada gerçek bir robotu, ilgili fizik yasalarına çok fazla dikkat etmeden kullanacağınız için, robotun nasıl simüle edildiğini görmezden gelebilir ve hemen hemen aynı olacağı için doğrudan kontrolör yazılımının nasıl programlandığına geçebilirsiniz. gerçek dünya ve bir simülasyon arasında. Ama merak ediyorsanız burada kısaca tanıtacağım.
world.py
dosyası, içinde robotlar ve engeller bulunan simüle edilmiş dünyayı temsil eden bir Python sınıfıdır. Bu sınıfın içindeki adım işlevi, basit dünyamızı şu şekilde geliştirmeyi sağlar:
- Robotun hareketlerine fizik kurallarının uygulanması
- Engellerle çarpışmaları göz önünde bulundurarak
- Robot sensörleri için yeni değerler sağlamak
Sonunda, robot beyin yazılımını yürütmekten sorumlu robot süpervizörlerini çağırır.
Adım işlevi bir döngüde yürütülür, böylece robot.step_motion()
, robotu önceki simülasyon adımında süpervizör tarafından hesaplanan tekerlek hızını kullanarak hareket ettirir.
# 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
apply_physics()
işlevi, süpervizörün mevcut simülasyon adımında çevreyi tahmin edebilmesi için robot yakınlık sensörlerinin değerlerini dahili olarak günceller. Aynı kavramlar kodlayıcılar için de geçerlidir.
Basit Bir Model
Öncelikle robotumuz çok basit bir modele sahip olacak. Dünya hakkında birçok varsayımda bulunacak. Önemli olanlardan bazıları şunlardır:
- Arazi her zaman düz ve eşittir
- Engeller asla yuvarlak değildir
- Tekerlekler asla kaymaz
- Hiçbir şey robotu itip kakmayacak
- Sensörler asla başarısız olmaz veya yanlış okumalar verir
- Çarklar her zaman söylendiğinde döner
Bu varsayımların çoğu ev benzeri bir ortamda makul olsa da, yuvarlak engeller mevcut olabilir. Engellerden kaçınma yazılımımız basit bir uygulamaya sahiptir ve engelleri aşmak için engellerin sınırlarını takip eder. Okuyuculara, dairesel engellerden kaçınmak için ek bir kontrolle robotumuzun kontrol çerçevesini nasıl geliştireceklerine dair ipuçları vereceğiz.
Kontrol Döngüsü
Şimdi kontrol yazılımımızın çekirdeğine gireceğiz ve robot içinde programlamak istediğimiz davranışları açıklayacağız. Bu çerçeveye ek davranışlar eklenebilir ve okumayı bitirdikten sonra kendi fikirlerinizi denemelisiniz! Davranış tabanlı robotik yazılımı 20 yıldan fazla bir süre önce önerildi ve hala mobil robotik için güçlü bir araç. Örnek olarak, 2007'de, otonom sürüş arabaları için ilk yarışma olan DARPA Urban Challenge'da bir dizi davranış kullanıldı!
Robot dinamik bir sistemdir. Robotun durumu, sensörlerinin okumaları ve kontrol sinyallerinin etkileri sürekli bir akış halindedir. Olayların gidişatını kontrol etmek aşağıdaki üç adımı içerir:
- Kontrol sinyallerini uygulayın.
- Sonuçları ölçün.
- Bizi hedefimize yaklaştırmak için hesaplanan yeni kontrol sinyalleri üretin.
Bu adımlar, amacımıza ulaşana kadar defalarca tekrarlanır. Bunu saniyede ne kadar çok yaparsak, sistem üzerinde o kadar iyi kontrol sahibi oluruz. Sobot Rimulator robotu bu adımları saniyede 20 kez (20 Hz) tekrarlar, ancak birçok robotun yeterli kontrole sahip olması için bunu saniyede binlerce veya milyonlarca kez yapması gerekir. Farklı robot sistemleri ve hız gereksinimleri için farklı robot programlama dilleri hakkında önceki tanıtımımızı hatırlayın.
Genel olarak, robotumuz sensörleriyle her ölçüm yaptığında, bu ölçümleri dünyanın durumuna ilişkin dahili tahminini, örneğin hedefine olan uzaklığını güncellemek için kullanır. Bu durumu, durumun ne olmasını istediğinin (mesafe için sıfır olmasını istiyor) bir referans değeriyle karşılaştırır ve istenen durum ile gerçek durum arasındaki hatayı hesaplar. Bu bilgi bir kez bilindiğinde, yeni kontrol sinyallerinin üretilmesi, sonunda robotu hedefe doğru hareket ettirecek olan hatayı en aza indirme problemine indirgenebilir.
Şık Bir Numara: Modeli Basitleştirmek
Programlamak istediğimiz robotu kontrol etmek için sol tekerleğe ne kadar hızlı döneceğini, sağ tekerleğe ise ne kadar hızlı döneceğini söyleyen ayrı bir sinyal göndermemiz gerekiyor. Bu sinyalleri v L ve v R olarak adlandıralım. Ancak, sürekli v L ve v R açısından düşünmek çok hantaldır. “Sol tekerleğin ne kadar hızlı dönmesini istiyoruz ve sağ tekerleğin ne kadar hızlı dönmesini istiyoruz?” Diye sormak yerine. "Robotun ne kadar hızlı ilerlemesini, ne kadar hızlı dönmesini veya yönünü değiştirmesini istiyoruz?" diye sormak daha doğaldır. Bu parametrelere hız v ve açısal (dönme) hız ω diyelim ("omega" okuyun). Görünüşe göre tüm modelimizi v L ve v R yerine v ve ω'ye dayandırabiliriz ve ancak programlanmış robotumuzun nasıl hareket etmesini istediğimizi belirlediğimizde, bu iki değeri matematiksel olarak ihtiyacımız olan v L ve v R'ye dönüştürün. robot tekerleklerini gerçekten kontrol etmek için. Bu, tek tekerlekli bisiklet kontrol modeli olarak bilinir.
İşte supervisor.py
içindeki son dönüşümü uygulayan Python kodu. ω 0 ise, her iki tekerleğin de aynı hızda döneceğine dikkat edin:
# 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_r
Tahmin Durumu: Robot, Kendini Bil
Robot, sensörlerini kullanarak çevrenin durumunu ve kendi durumunu tahmin etmeye çalışmalıdır. Bu tahminler asla mükemmel olmayacak, ancak oldukça iyi olmalılar çünkü robot tüm kararlarını bu tahminlere dayandıracak. Yalnızca yakınlık sensörlerini ve tekerlek işaretlerini kullanarak aşağıdakileri tahmin etmeye çalışması gerekir:
- engellerin yönü
- Engellerden uzaklık
- robotun konumu
- Robotun başlığı
İlk iki özellik, yakınlık sensörü okumalarıyla belirlenir ve oldukça basittir. API işlevi read_proximity_sensors()
, her sensör için bir tane olmak üzere dokuz değerden oluşan bir dizi döndürür. Örneğin yedinci okumanın robotun 75 derece sağını gösteren sensöre karşılık geldiğini önceden biliyoruz.
Dolayısıyla bu değer 0,1 metre mesafeye karşılık gelen bir okuma gösteriyorsa, 0,1 metre ötede 75 derece solda bir engel olduğunu biliyoruz. Herhangi bir engel yoksa, sensör 0,2 metrelik maksimum aralığının bir okumasını verecektir. Böylece, sensör yedide 0.2 metre okursak, aslında o yönde hiçbir engel olmadığını varsayacağız.
Kızılötesi sensörlerin çalışma şekli nedeniyle (kızılötesi yansımayı ölçer), döndürdükleri sayılar algılanan gerçek mesafenin doğrusal olmayan bir dönüşümüdür. Bu nedenle, belirtilen mesafeyi belirlemek için Python işlevi bu okumaları metreye çevirmelidir. Bu, supervisor.py
şu şekilde yapılır:
# 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() ]
Yine, bu Python robot çerçevesinde belirli bir sensör modelimiz varken, gerçek dünyada sensörler, doğrusal olmayan değerlerden metrelere kadar benzer dönüştürme işlevleri sağlaması gereken yazılımlarla birlikte gelir.

Robotun konumunu ve yönünü belirlemek (birlikte robotik programlamada poz olarak bilinir) biraz daha zordur. Robotumuz, pozunu tahmin etmek için kilometre sayacı kullanır. Tekerlek işaretlerinin devreye girdiği yer burasıdır. Kontrol döngüsünün son yinelemesinden bu yana her bir tekerleğin ne kadar döndüğünü ölçerek, robotun duruşunun nasıl değiştiğine dair iyi bir tahmin elde etmek mümkündür - ancak değişiklik küçükse .
Bu, tekerlekleri hareket ettiren motorların mükemmel olmayabileceği gerçek dünyadaki bir robotta kontrol döngüsünü çok sık yinelemenin önemli olmasının bir nedenidir. Tekerlek işaretlerini ölçmek için çok uzun süre bekleseydik, her iki tekerlek de oldukça fazla şey yapabilirdi ve nerede olduğumuzu tahmin etmek imkansız olacak.
Mevcut yazılım simülatörümüz göz önüne alındığında, odometri hesaplamasını kontrolörlerle aynı frekansta 20 Hz'de çalıştırmayı göze alabiliriz. Ancak, daha küçük hareketlerin daha küçük hareketlerini yakalamak için daha hızlı çalışan ayrı bir Python iş parçacığına sahip olmak iyi bir fikir olabilir.
Aşağıda, supervisor.py
robot poz tahminini güncelleyen tam kilometre ölçümü işlevi bulunmaktadır. Robotun pozunun, x
ve y
koordinatlarından ve pozitif X ekseninden radyan cinsinden ölçülen theta
başlığından oluştuğuna dikkat edin. Pozitif x
doğuda ve pozitif y
. Böylece 0
başlığı, robotun doğrudan doğuya baktığını gösterir. Robot her zaman ilk pozunun (0, 0), 0
olduğunu varsayar.
# 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_right
Artık robotumuz gerçek dünyanın iyi bir tahminini üretebildiğine göre, bu bilgiyi hedeflerimize ulaşmak için kullanalım.
Python Robot Programlama Yöntemleri: Hedefe Git Davranışı
Bu programlama eğitiminde küçük robotumuzun varoluşunun en büyük amacı, hedef noktasına ulaşmaktır. Peki oraya ulaşmak için tekerlekleri nasıl döndüreceğiz? Dünya görüşümüzü biraz basitleştirerek başlayalım ve bu yolda hiçbir engel olmadığını varsayalım.
Bu daha sonra basit bir görev haline gelir ve Python'da kolayca programlanabilir. Hedefle yüzleşirken ileri gidersek, oraya ulaşırız. Odometrimiz sayesinde mevcut koordinatlarımızın ve yönümüzün ne olduğunu biliyoruz. Ayrıca önceden programlanmış oldukları için hedefin koordinatlarının ne olduğunu da biliyoruz. Bu nedenle, küçük bir lineer cebir kullanarak, konumumuzdan hedefe giden vektörü, go_to_goal_controller.py
gibi belirleyebiliriz:
# 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 goal
Vektörü hedefe robotun referans çerçevesinde aldığımızı ve dünya koordinatlarında DEĞİLDİR. Hedef robotun referans çerçevesinde X ekseni üzerindeyse, bu doğrudan robotun önünde olduğu anlamına gelir. Böylece, bu vektörün X eksenine göre açısı, rotamız ile üzerinde olmak istediğimiz rota arasındaki farktır. Başka bir deyişle, mevcut durumumuz ile mevcut durumumuzun olmasını istediğimiz arasındaki hatadır . Bu nedenle dönüş hızımızı ω istikametimiz ile hedef arasındaki açı 0'a doğru değişecek şekilde ayarlamak istiyoruz. Hatayı en aza indirmek istiyoruz:
# 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
, denetleyicinin yukarıdaki parçacığında Python uygulamasının bir kontrol kazancıdır. Karşı karşıya olduğumuz hedeften ne kadar uzaklaştığımızla orantılı olarak ne kadar hızlı döndüğümüzü belirleyen bir katsayıdır. Başlığımızdaki hata 0
ise dönüş hızı da 0
. go_to_goal_controller.py
dosyasının içindeki gerçek Python işlevinde, basit bir orantılı katsayı yerine bir PID denetleyici kullandığımız için daha benzer kazançlar göreceksiniz.
Şimdi açısal hızımız ω olduğuna göre, ileriye doğru v hızımızı nasıl belirleriz? İyi bir genel kural, muhtemelen içgüdüsel olarak bildiğiniz bir kuraldır: Eğer bir dönüş yapmıyorsak, tam hızla ilerleyebiliriz ve sonra ne kadar hızlı dönersek, o kadar yavaşlamalıyız. Bu genellikle sistemimizi kararlı tutmamıza ve modelimizin sınırları içinde hareket etmemize yardımcı olur. Böylece v , ω'nin bir fonksiyonudur. go_to_goal_controller.py
denklem şöyledir:
# 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
Bu formülü detaylandırmak için bir öneri, hedefe sıfır hızda ulaşmak için genellikle hedefe yaklaştığımızda yavaşladığımızı düşünmektir. Bu formül nasıl değişecek? Bir şekilde v_max()
orantılı bir şeyle değiştirilmesini içermelidir. Tamam, neredeyse tek bir kontrol döngüsünü tamamladık. Geriye sadece bu iki tek tekerlekli bisiklet model parametresini diferansiyel tekerlek hızlarına dönüştürmek ve sinyalleri tekerleklere göndermek kalıyor. Hedefe giden kontrolör altındaki robotun yörüngesinin hiçbir engel olmadan bir örneği:
Gördüğümüz gibi, hedefe giden vektör, kontrol hesaplamalarımızı temel almamız için etkili bir referanstır. “Nereye gitmek istediğimizin” içsel bir temsilidir. Göreceğimiz gibi, hedefe gitme ve diğer davranışlar arasındaki tek büyük fark, bazen hedefe doğru gitmenin kötü bir fikir olmasıdır, bu nedenle farklı bir referans vektörü hesaplamamız gerekir.
Python Robot Programlama Yöntemleri: Engellerden Kaçınma Davranışı
O yönde bir engel olduğunda hedefe doğru gitmek, buna bir örnektir. Önümüze çıkan şeylere kafa yormak yerine, robotun onlardan kaçınmasını sağlayan bir kontrol yasası programlamaya çalışalım.
Senaryoyu basitleştirmek için, şimdi hedef noktasını tamamen unutalım ve sadece şunu hedefimiz haline getirelim: Önümüzde hiçbir engel olmadığında ilerleyin. Bir engelle karşılaştığınızda, önümüzde durana kadar ondan uzaklaşın.
Buna göre önümüzde bir engel yokken referans vektörümüzün sadece ileriyi göstermesini istiyoruz. O zaman ω sıfır olacak ve v maksimum hız olacaktır. Ancak, yakınlık sensörlerimizle bir engel tespit ettiğimiz anda, referans vektörünün engelden uzakta olan yönü göstermesini isteriz. Bu, bizi engelden uzaklaştırmak için ω'nin yukarı fırlamasına ve süreçte yanlışlıkla engelle karşılaşmamamızı sağlamak için v'nin düşmesine neden olur.
İstenen referans vektörümüzü oluşturmanın düzgün bir yolu, dokuz yakınlık okumamızı vektörlere dönüştürmek ve ağırlıklı bir toplam almaktır. Herhangi bir engel algılanmadığında, vektörler simetrik olarak toplanır ve istendiği gibi dümdüz ileriyi gösteren bir referans vektörü elde edilir. Ancak, örneğin sağ taraftaki bir sensör bir engel alırsa, toplama daha küçük bir vektör katacaktır ve sonuç, sola doğru kaydırılan bir referans vektörü olacaktır.
Farklı sensör yerleşimine sahip genel bir robot için, aynı fikir uygulanabilir ancak ağırlıklarda değişiklikler ve/veya sensörler robotun önünde ve arkasında simetrik olduğunda, ağırlıklı toplam sıfır olabileceğinden, ağırlıklarda değişiklikler ve/veya ek bakım gerektirebilir. .
İşte bunu avoid_obstacles_controller.py
içinde yapan kod:
# 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
Elde edilen ao_heading_vector
robotun eşleştirmeye çalışması için referansımız olarak kullanarak, burada robot yazılımını simülasyonda yalnızca engelden kaçınma denetleyicisini kullanarak, hedef noktasını tamamen göz ardı ederek çalıştırmanın sonuçları. Robot amaçsızca etrafta zıplar, ancak hiçbir engelle çarpışmaz ve hatta çok dar alanlarda gezinmeyi başarır:
Python Robot Programlama Yöntemleri: Hibrit Otomatlar (Behavior State Machine)
Şimdiye kadar iki davranışı tek başına tanımladık - hedefe gitmek ve engelleri önlemek. Her ikisi de takdire şayan bir şekilde işlevlerini yerine getiriyor, ancak engellerle dolu bir ortamda hedefe başarıyla ulaşmak için onları birleştirmemiz gerekiyor.
Geliştireceğimiz çözüm, son derece havalı görünen hibrit otomatlara sahip bir makine sınıfında yatmaktadır. Bir hibrit otomat, bir denetleyici durum makinesinin yanı sıra birkaç farklı davranış veya mod ile programlanmıştır. Denetleyici durum makinesi, farklı zamanlarda bir moddan diğerine geçer (hedeflere ulaşıldığında veya ortam aniden çok fazla değiştiğinde), her davranış ortam değişikliklerine sürekli olarak tepki vermek için sensörler ve tekerlekler kullanır. Çözüm, hem ayrık hem de sürekli bir biçimde geliştiği için hibrit olarak adlandırıldı.
Python robot çerçevemiz, durum makinesini supervisor_state_machine.py
dosyasında uygular.
İki kullanışlı davranışımızla donatılmış basit bir mantık kendini önerir: Herhangi bir engel algılanmadığında, hedefe gitme davranışını kullanın. Bir engel algılandığında, engel artık algılanmayana kadar engellerden kaçınma davranışına geçin.
Ancak ortaya çıktığı gibi, bu mantık birçok sorun üretecektir. Bu sistemin bir engelle karşılaştığında yapacağı şey, ondan uzaklaşmak, ondan uzaklaşır uzaklaşmaz hemen geri dönmek ve tekrar onunla karşılaşmak olacaktır. Sonuç, robotu işe yaramaz hale getiren sonsuz bir hızlı geçiş döngüsüdür. 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
kP
in 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.