Kobuki: Rozdiel medzi revíziami
Zo stránky SensorWiki
Vytvorená stránka „= Návod na cvičenie: Rekonštrukcia trajektórie a mapovanie bludiska = == Cieľ cvičenia == Cieľom tohto cvičenia je prakticky si vyskúšať zber a spracovanie surových dát z mobilného robota. Úlohou je manuálne prejsť s robotom neznáme bludisko a následne zo zaznamenaných dát (odometria, gyroskop a lidar) zrekonštruovať mapu prostredia a presnú trajektóriu pohybu robota. == Technický opis zariadení == === Mobilná platforma Kobuki === Kob…“ |
|||
| (6 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 1: | Riadok 1: | ||
= | = Mapovanie bludiska mobilným robotom = | ||
'''Cvičenie z mobilnej robotiky — Kobuki + RPlidar A1''' | |||
---- | |||
__TOC__ | |||
= | = Cieľ cvičenia = | ||
Cieľom cvičenia je ručne previesť mobilného robota cez bludisko, zaznamenať dáta z odometrie a laserového diaľkomera (lidaru), a následne z nameraných dát zrekonštruovať: | |||
# '''trajektóriu robota''' v rovine ''x''–''y'' (dead reckoning z enkodérov a gyroskopu), | |||
# '''plánik bludiska''' (mračno bodov z lidaru transformované do globálnych súradníc). | |||
=== Technické parametre === | Spracovanie dát vykonáte ''offline'' v prostredí Matlab alebo Python na základe dvoch logovacích súborov: <code>robot.log</code> (odometria) a <code>laser.log</code> (lidar). | ||
= Technický opis zariadení = | |||
== Mobilný robot Kobuki == | |||
Kobuki je kompaktná mobilná platforma s diferenciálnym podvozkom — má dve nezávisle poháňané kolesá a jedno voľnobežné (kastorové) koleso. Pohyb robota sa meria inkrementálnymi optickými enkodérmi na oboch kolesách a jednoosovým gyroskopom. | |||
{| class="wikitable" | |||
|+ Technické parametre robota Kobuki | |||
|- | |||
!| '''Kategória''' | |||
!| '''Parameter''' | |||
!| '''Hodnota''' | |||
|- | |||
| rowspan="2"| Rozmery | |||
|| Priemer tela | |||
|| 351.5 mm (kruh) | |||
|- | |||
|| Výška | |||
|| 124.8 mm | |||
|- | |||
| rowspan="4"| Pohon a kinematika | |||
|| Typ podvozku | |||
|| diferenciálny (2 kolesá + kastor) | |||
|- | |||
|| Rozchod kolies (wheelbase) | |||
|| ''L'' = 230 mm | |||
|- | |||
|| Polomer kolesa | |||
|| ''r'' = 35 mm | |||
|- | |||
|| Šírka kolesa | |||
|| 21 mm | |||
|- | |||
| rowspan="2"| Dynamika | |||
|| Max. translačná rýchlosť | |||
|| 70 cm s<sup>−1</sup> | |||
|- | |||
|| Max. rotačná rýchlosť | |||
|| 180°/s (gyroskop spoľahlivý do 110°/s) | |||
|- | |||
| rowspan="4"| Enkodéry | |||
|| Rozlíšenie enkodéra | |||
|| 52 tick/ot. enkodéra | |||
|- | |||
|| Prevodový pomer | |||
|| 6545 : 132 = 49,5833 | |||
|- | |||
|| Ticky na otáčku kolesa | |||
|| 52 × 49,5833 = 2578,33 tick/ot. | |||
|- | |||
|| Registre enkodérov | |||
|| 16-bit unsigned (0–65 535) | |||
|- | |||
| rowspan="2"| Prepočtové konštanty | |||
|| Tick → metre | |||
|| ''c<sub>f</sub>'' = 0,000085292 m tick<sup>−1</sup> | |||
|- | |||
|| Ekvivalent | |||
|| ≈ 11,7 tick/mm | |||
|- | |||
| rowspan="2"| Gyroskop | |||
|| Typ | |||
|| 1-osový, továrensky kalibrovaný | |||
|- | |||
|| Rozsah | |||
|| do 110°/s | |||
|- | |||
| rowspan="6"| Senzory a bezpečnosť | |||
|| Nárazníky (bumpers) | |||
|| ľavý, stredný, pravý | |||
|- | |||
|| Senzory útesu (cliff) | |||
|| ľavý, stredný, pravý | |||
|- | |||
|| Senzor poklesu kolesa | |||
|| ľavý, pravý | |||
|- | |||
|| Dátová frekvencia | |||
|| 50 Hz | |||
|- | |||
|| Pripojenie k PC | |||
|| USB alebo RX/TX piny | |||
|- | |||
|| Ochrana motora | |||
|| vypnutie pri <math display="inline">I > 3 A</math> | |||
|- | |||
| rowspan="3"| Napájanie | |||
|| Batéria | |||
|| Li-Ion 14.8 V, 2200 mAh | |||
|- | |||
|| Výdrž | |||
|| 3/7 h (malá/veľká batéria) | |||
|- | |||
|| Nosnosť | |||
|| 5 kg (tvrdá podlaha) / 4 kg (koberec) | |||
|} | |||
== Laserový diaľkomer RPlidar A1 == | |||
RPlidar A1 je 2D laserový skener (lidar), ktorý rotáciou laserového lúča okolo zvislej osi sníma vzdialenosti k prekážkam v celom rozsahu 360°. Senzor je upevnený na robot v strede medzi kolesami (offset <math display="inline">= 0</math>). | |||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+ Technické parametre lidaru RPlidar A1 | ||
! Parameter ! | |- | ||
!| '''Kategória''' | |||
!| '''Parameter''' | |||
!| '''Hodnota''' | |||
|- | |||
| rowspan="3"| Rozmery a hmotnosť | |||
|| Rozmery | |||
|| 98,5 × 70 × 60 mm | |||
|- | |||
|| Hmotnosť | |||
|| 170 g | |||
|- | |- | ||
| | || Napájanie | ||
|| externé (z robota) | |||
|- | |- | ||
| | | rowspan="3"| Rozsah | ||
|| Vzdialenosť | |||
|| 0.15 – 12 m (biele objekty) | |||
|- | |- | ||
| | || Uhlový rozsah | ||
|| 0 – 360° | |||
|- | |- | ||
| Rozlíšenie | || Rozlíšenie vzdialenosti | ||
|| < 0,5 mm | |||
|- | |- | ||
| | | rowspan="4"| Rýchlosť | ||
|| Uhlové rozlíšenie | |||
|| < 1° | |||
|- | |- | ||
| | || Doba jedného merania | ||
|| 0.5 ms | |||
|- | |- | ||
| | || Vzorkovacia frekvencia | ||
|| 2000 – 2010 Hz | |||
|- | |- | ||
| | || Rýchlosť otáčania (scan rate) | ||
|| 1 – 10 Hz, typicky 5.5 Hz | |||
|- | |||
|| Kvalita merania | |||
|| Rozsah hodnôt | |||
|| 0 – 15 (vyššia = lepšia) | |||
|} | |} | ||
= Postup merania = | |||
# Umiestnite robota na štartovaciu pozíciu pred vstupom do bludiska. Zapnite robota a spustite záznam dát. | |||
# Pomocou joysticku preveďte robota cez celé bludisko. '''Prejdite aj všetky slepé uličky''' — cieľom je získať kompletný obraz o geometrii bludiska. | |||
# Pohybujte sa plynulo, bez prudkých zmien smeru. Pri otáčaní neprekračujte rotačnú rýchlosť 110°/s (limit gyroskopu). | |||
# Po prechode bludiskom zastavte robota a ukončite záznam. | |||
# Z robota stiahnite dva logovacie súbory: <code>robot.log</code> a <code>laser.log</code>. | |||
= Formát záznamových súborov = | |||
== Súbor <code>robot.log</code> — odometria == | |||
Každý riadok obsahuje jedno meranie, hodnoty sú oddelené medzerami: | |||
<code><timestamp_us> <enc_left> <enc_right> <gyro_deg></code> | |||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+ Štruktúra súboru <code>robot.log</code> | ||
! | |- | ||
!| '''Stĺpec''' | |||
!| '''Typ''' | |||
!| '''Popis''' | |||
|- | |- | ||
| | || 1 — Timestamp | ||
|| <code>uint64</code> | |||
|| Čas merania v mikrosekundách | |||
|- | |- | ||
| | || 2 — Enc. Left | ||
|| <code>uint16</code> | |||
|| Kumulatívny počet tickov ľavého enkodéra (0–65 535) | |||
|- | |- | ||
| | || 3 — Enc. Right | ||
|| <code>uint16</code> | |||
|| Kumulatívny počet tickov pravého enkodéra (0–65 535) | |||
|- | |- | ||
| Frekvencia | || 4 — Gyro | ||
|| <code>float</code> | |||
|| Uhol z gyroskopu v stupňoch (-180 až +180) | |||
|} | |||
Príklad záznamov: | |||
3067284773 1288 58777 174.98 | |||
3067301111 1288 58777 174.98 | |||
3067323915 1288 58777 174.98 | |||
Frekvencia záznamu zodpovedá dátovej frekvencii robota, t. j. približne 50 Hz. | |||
== Súbor <code>laser.log</code> — lidar == | |||
Každý riadok obsahuje jedno meranie laserového lúča: | |||
<code><timestamp_us> <distance_mm> <angle_deg> <quality></code> | |||
{| class="wikitable" | |||
|+ Štruktúra súboru <code>laser.log</code> | |||
|- | |||
!| '''Stĺpec''' | |||
!| '''Typ''' | |||
!| '''Popis''' | |||
|- | |||
|| 1 — Timestamp | |||
|| <code>uint64</code> | |||
|| Čas merania v mikrosekundách | |||
|- | |- | ||
| | || 2 — Distance | ||
|| <code>float</code> | |||
|| Vzdialenosť k prekážke v milimetroch | |||
|- | |- | ||
| | || 3 — Angle | ||
|| <code>float</code> | |||
|| Uhol laserového lúča v stupňoch (0–360), ''v smere hodinových ručičiek'' od prednej časti robota | |||
|- | |- | ||
| | || 4 — Quality | ||
|| <code>int</code> | |||
|| Kvalita merania (0–15); 0 = neplatné | |||
|} | |} | ||
Príklad záznamov: | |||
3067236764 3713.75 351.047 15 | |||
3067237463 3666.75 352.438 15 | |||
3067237467 3708.75 353.812 15 | |||
Jedna kompletná otáčka lidaru (sken) obsahuje typicky okolo 260 bodov. Pri scan rate cca 7,8 Hz to zodpovedá vzorkovacej frekvencii cca 2000 Hz. | |||
== | = Teoretický základ — ideálny prípad = | ||
V tejto kapitole odvodíme všetky vzťahy za predpokladu, že dáta sú bezchybné: enkodéry nepretekajú, gyroskop nedriftuje a časy sú presne synchronizované. Praktické komplikácie riešime v kapitole [[#sec:prakticke|6]]. | |||
=== | == Kinematika diferenciálneho podvozku == | ||
Robot má dve kolesá vzdialené ''L'' = 0,23 m. Stredom medzi nimi je referenčný bod ''P'', ktorého polohu (x, y) a orientáciu θ chceme sledovať. | |||
=== | V každom časovom kroku (medzi vzorkou ''k-1'' a ''k'') enkodéry napočítajú ''Δtick_L'' a ''Δtick_R'' impulzov. Dráha jednotlivých kolies je: ''d_L = Δtick_L · c_f, d_R = Δtick_R · c_f'', | ||
kde ''c<sub>f</sub>'' = 0,000085292 m tick<sup>−1</sup> je prepočtový faktor. | |||
''' | <div id="fig:kinematika" class="figure"></div> | ||
< | '''Obr. 1:''' ''Kinematický model diferenciálneho podvozku. d<sub>L</sub>, d<sub>R</sub> sú dráhy kolies, d<sub>c</sub> je stredná dráha bodu P.'' | ||
</ | |||
''' | Stredná dráha (pohyb bodu ''P'') a zmena orientácie sú: | ||
d_c = (d_L + d_R)/2, | |||
Δθ = (d_R - d_L)/L. | |||
=== Uhol | Intuitívne: ak sa obe kolesá pohnú rovnako (d_L = d_R), robot ide rovno (Δθ = 0). Ak sa pravé koleso pohne viac, robot zatáča doľava (Δθ > 0 v štandardnej konvencii). | ||
Gyroskop | |||
* ''' | == Aktualizácia polohy (dead reckoning) == | ||
Polohu robota aktualizujeme iteratívne. Používame ''aproximáciu stredným uhlom'' (second-order midpoint method), ktorá je výrazne presnejšia než jednoduchá Eulerova metóda: | |||
θ_mid = \theta(k-1) + Δθ/2 | |||
x(k) = x(k-1) + d_c · cos(θ_mid) | |||
y(k) = y(k-1) + d_c · sin(θ_mid) | |||
θ(k) = θ(k-1) + Δθ | |||
Počiatočné podmienky: x(0) = 0, y(0) = 0, θ(0) = 0 (robot smeruje v osi ''x''). | |||
== Tri zdroje orientácie == | |||
Pre výpočet zmeny uhla <math display="inline">\Delta\theta</math> máme tri možnosti: | |||
=== A) Len enkodéry === | |||
Uhol sa počíta podľa rovnice [[#eq:dc_dtheta|[eq:dc_dtheta]]]. Jednoduché, ale citlivé na šmýkanie kolies. | |||
=== B) Len gyroskop === | |||
Gyroskop Kobuki dáva priamo absolútny uhol (firmvér integruje uhlovú rýchlosť interne). Zmenu uhla medzi dvoma vzorkami vypočítame ako: <math display="block">\Delta\theta_{gyro} = [gyro(k) - gyro(k{-}1)] · \frac{\pi}{180} | |||
\label{eq:gyro_delta}</math> s korekciou pretečenia cez <math display="inline">±180^{\circ}</math> (pozri sekciu [[#sec:pretecenie_gyro|6.2]]). Táto metóda je odolná voči šmyku kolies, ale dlhodobo trpí driftom gyroskopu. | |||
=== C) Komplementárny filter === | |||
Fúzia oboch zdrojov jedným parametrom <math display="inline">\alpha \in (0, 1)</math>: <math display="block">\boxed{\Delta\theta = \alpha · \Delta\theta_{gyro} + (1 - \alpha) · \Delta\theta_{enc}} | |||
\label{eq:compfilter}</math> | |||
Parameter <math display="inline">\alpha</math> vyjadruje “dôveru” v gyroskop. Typicky <math display="inline">\alpha = 0,95</math> — gyroskop je na krátkych škálach presnejší, enkodéry kompenzujú jeho dlhodobý drift. Na ladenie použite uzavretú trasu — správne <math display="inline">\alpha</math> minimalizuje odchýlku koncovej polohy od štartu. | |||
== Transformácia lidarových bodov do mapy == | |||
Lidar meria vzdialenosť ''d'' v smere uhla <math display="inline">\alpha</math> (v stupňoch, rastúci ''v smere hodinových ručičiek'' od prednej časti robota). Cieľom je transformovať tento bod zo súradníc robota do globálnych (mapových) súradníc. | |||
<div id="fig:lidar" class="figure"> | |||
Priama transformácia z polárnych súradníc lidaru do mapy: <math display="block">\boxed{ | |||
\begin{aligned} | |||
\alpha_{world} &= \theta - \alpha · \frac{\pi}{180} \\[4pt] | |||
x_{global} &= x_R + \frac{d}{1000} · \cos(\alpha_{world}) \\[2pt] | |||
y_{global} &= y_R + \frac{d}{1000} · \sin(\alpha_{world}) | |||
\end{aligned} | |||
} | |||
\label{eq:lidar_transform}</math> kde <math display="inline">\theta</math> je orientácia robota v radiánoch, <math display="inline">\alpha</math> je uhol lidaru v stupňoch (CW), ''d'' je vzdialenosť v mm a <math display="inline">(x_R, y_R)</math> je poloha robota v metroch. Znamienko mínus pred <math display="inline">\alpha</math> konvertuje smysel otáčania z CW na matematicky kladný (CCW). | |||
= Riešenie praktických problémov = | |||
== Pretečenie enkodérov == | |||
Enkodéry Kobuki sú 16-bitové bezznamienkové čítače s rozsahom <math display="inline">0</math> – <math display="inline">65 535</math>. Keď čítač dosiahne <math display="inline">65 535</math> a koleso sa ďalej otáča, hodnota preskočí na <math display="inline">0</math> (a opačne pri pohybe vzad). V surovom rozdiele <math display="inline">\Deltatick = enc(k) - enc(k{-}1)</math> sa to prejaví obrovským skokom. | |||
Korekcia: <math display="block">\Deltatick_{cor} = | |||
\begin{cases} | |||
\Deltatick - 65536 & \text{ak } \Deltatick > 32768 \\ | |||
\Deltatick + 65536 & \text{ak } \Deltatick < -32768 \\ | |||
\Deltatick & \text{inak} | |||
\end{cases} | |||
\label{eq:wrap_enc}</math> | |||
<div class="center"> | |||
<div style="background-color: yellow!20"> | |||
<span style="color: warnorange">{{Upozornenie|typ=pozor|text=</span> Bez tejto korekcie dostanete v trajektórii obrovské skoky rádovo desiatky metrov. Toto je najčastejší zdroj chýb!}} | |||
Ukážka v Matlabu / Pythone: | |||
<syntaxhighlight lang="matlab">dTick = enc(k) - enc(k-1); | |||
if dTick > 32768 | |||
dTick = dTick - 65536; | |||
end | |||
if dTick < -32768 | |||
dTick = dTick + 65536; | |||
end</syntaxhighlight> | |||
<syntaxhighlight lang="python">dTick = enc[k] - enc[k-1] | |||
if dTick > 32768: | |||
dTick -= 65536 | |||
if dTick < -32768: | |||
dTick += 65536</syntaxhighlight> | |||
== Pretečenie gyroskopu == | |||
Gyroskop vracia uhol v rozsahu <math display="inline">(-180^{\circ}, +180^{\circ}]</math>. Pri prechode cez <math display="inline">±180^{\circ}</math> hodnota skočí, napr. z <math display="inline">+179^{\circ}</math> na <math display="inline">-179^{\circ}</math>. To nie je skutočná zmena o <math display="inline">358^{\circ}</math>, ale o <math display="inline">+2^{\circ}</math>. | |||
Korekcia: <math display="block">\Deltagyro_{cor} = | |||
\begin{cases} | |||
\Deltagyro - 360 & \text{ak } \Deltagyro > 180 \\ | |||
\Deltagyro + 360 & \text{ak } \Deltagyro < -180 \\ | |||
\Deltagyro & \text{inak} | |||
\end{cases} | |||
\label{eq:wrap_gyro}</math> | |||
<syntaxhighlight lang="matlab">dGyro = gyro(k) - gyro(k-1); | |||
if dGyro > 180 | |||
dGyro = dGyro - 360; | |||
end | |||
if dGyro < -180 | |||
dGyro = dGyro + 360; | |||
end | |||
dTheta = dGyro * pi / 180;</syntaxhighlight> | |||
<syntaxhighlight lang="python">dGyro = gyro[k] - gyro[k-1] | |||
if dGyro > 180: | |||
dGyro -= 360 | |||
if dGyro < -180: | |||
dGyro += 360 | |||
dTheta = dGyro * math.pi / 180</syntaxhighlight> | |||
== Synchronizácia časových značiek == | |||
Odometria sa zaznamenáva s frekvenciou <math display="inline">≈ 50 Hz</math>, lidar produkuje <math display="inline">≈ 2000</math> bodov za sekundu. Časové značky pochádzajú z rovnakých hodín, ale nie sú identické. Pre každý lidarový bod musíme nájsť najbližšiu polohu robota. | |||
Efektívne riešenie — paralelný prechod oboma logmi s jedným ukazovateľom: | |||
<syntaxhighlight lang="matlab">ridx = 1; | |||
for i = 1:length(laser_ts) | |||
while ridx < N && ... | |||
abs(robot_ts(ridx+1) ... | |||
- laser_ts(i)) < ... | |||
abs(robot_ts(ridx) ... | |||
- laser_ts(i)) | |||
ridx = ridx + 1; | |||
end | |||
% robot_ts(ridx) je | |||
% najblizsi k laser_ts(i) | |||
end</syntaxhighlight> | |||
<syntaxhighlight lang="python">ridx = 0 | |||
for i in range(len(laser_ts)): | |||
while ridx < N-1 and \ | |||
abs(robot_ts[ridx+1] | |||
- laser_ts[i]) < \ | |||
abs(robot_ts[ridx] | |||
- laser_ts[i]): | |||
ridx += 1 | |||
# robot_ts[ridx] je | |||
# najblizsi k laser_ts[i]</syntaxhighlight> | |||
Tento algoritmus má lineárnu zložitosť <math display="inline">O(N+M)</math>, pretože ukazovateľ <code>ridx</code> sa nikdy nevracia — funguje to, pretože oba logy sú chronologicky usporiadané. | |||
== Filtrovanie neplatných lidarových meraní == | |||
Nie všetky lidarové body sú platné. Pred spracovaním je nutné vyradiť: | |||
* merania s nulovou vzdialenosťou (<math display="inline">d = 0</math>) — lidar nezachytil odraz, | |||
* merania s nízkou kvalitou (odporúčame prah <math display="inline">\text{quality} ≥ 10</math>), | |||
* voliteľne merania mimo dosahu senzora (<math display="inline">d > 12000 mm</math>). | |||
<syntaxhighlight lang="matlab">if laser_dist(i) < 20 || ... | |||
laser_qual(i) < 10 | |||
continue; | |||
end</syntaxhighlight> | |||
<syntaxhighlight lang="python">if dist < 20 or quality < 10: | |||
continue</syntaxhighlight> | |||
= Postup spracovania dát = | |||
Tu je celkový algoritmus zhrnutý do piatich krokov. Kúsky kódu z predchádzajúcich sekcií spojte do jedného skriptu. | |||
<ol> | |||
<li><p>'''Načítajte oba súbory.'''</p> | |||
<syntaxhighlight lang="matlab">R = load('robot.log'); | |||
robot_ts = R(:,1); | |||
encL = R(:,2); encR = R(:,3); | |||
gyro = R(:,4); | |||
L = load('laser.log'); | |||
laser_ts = L(:,1); | |||
laser_d = L(:,2); | |||
laser_a = L(:,3); | |||
laser_q = L(:,4);</syntaxhighlight> | |||
<syntaxhighlight lang="python">import numpy as np, math | |||
R = np.loadtxt('robot.log') | |||
robot_ts = R[:,0] | |||
encL = R[:,1]; encR = R[:,2] | |||
gyro = R[:,3] | |||
L = np.loadtxt('laser.log') | |||
laser_ts = L[:,0] | |||
laser_d = L[:,1] | |||
laser_a = L[:,2] | |||
laser_q = L[:,3]</syntaxhighlight></li> | |||
<li><p>'''Vypočítajte trajektóriu robota''' (sekcie [[#sec:kinematika|5.1]]–[[#sec:deadreckoning|5.2]]).</p> | |||
<p>Pre každý krok <math display="inline">k = 2, \ldots, N</math>:</p> | |||
<ol> | |||
<li><p>Vypočítajte <math display="inline">\Deltatick_L</math>, <math display="inline">\Deltatick_R</math> s korekciou pretečenia (rov. [[#eq:wrap_enc|[eq:wrap_enc]]]).</p></li> | |||
<li><p>Prepočítajte na dráhy ''d_L'', ''d_R'' (rov. [[#eq:dl_dr|[eq:dl_dr]]]).</p></li> | |||
<li><p>Vypočítajte ''d_c'' a <math display="inline">\Delta\theta</math> (rov. [[#eq:dc_dtheta|[eq:dc_dtheta]]] alebo [[#eq:gyro_delta|[eq:gyro_delta]]] alebo [[#eq:compfilter|[eq:compfilter]]]).</p></li> | |||
<li><p>Aktualizujte <math display="inline">x(k)</math>, <math display="inline">y(k)</math>, <math display="inline">\theta(k)</math> (rov. [[#eq:update|[eq:update]]]).</p></li></ol> | |||
<p>Výsledkom sú polia <code>robot_x</code>, <code>robot_y</code>, <code>robot_theta</code> o dĺžke ''N''.</p></li> | |||
<li><p>'''Transformujte lidarové body do mapy''' (sekcia [[#sec:lidar_transform|5.4]]).</p> | |||
<p>Pre každé platné meranie ''i'' (po filtrovaní, sekcia [[#sec:filter|6.4]]):</p> | |||
<ol> | |||
<li><p>Nájdite najbližšiu polohu robota ''k'' podľa timestampu (sekcia [[#sec:sync|6.3]]).</p></li> | |||
<li><p>Aplikujte transformáciu (rov. [[#eq:lidar_transform|[eq:lidar_transform]]]) s použitím <math display="inline">(x_R, y_R, \theta)</math> z kroku ''k''.</p></li> | |||
<li><p>Uložte globálne súradnice bodu do poľa mapy.</p></li></ol> | |||
</li> | |||
<li><p>'''Vykreslite výsledok.'''</p> | |||
<syntaxhighlight lang="matlab">figure; hold on; axis equal; | |||
grid on; | |||
% Steny bludiska | |||
plot(map_x, map_y, '.', ... | |||
'Color', [.5 .5 .5], ... | |||
'MarkerSize', 1); | |||
% Trajektoria | |||
plot(robot_x, robot_y, ... | |||
'b-', 'LineWidth', 2); | |||
% Start a ciel | |||
plot(0, 0, 'go', ... | |||
'MarkerSize', 10, ... | |||
'MarkerFaceColor', 'g'); | |||
plot(robot_x(end), ... | |||
robot_y(end), 'rs', ... | |||
'MarkerSize', 8, ... | |||
'MarkerFaceColor', 'r'); | |||
xlabel('x [m]'); | |||
ylabel('y [m]'); | |||
title('Mapa bludiska'); | |||
legend('Steny', 'Trajektoria', ... | |||
'Start', 'Ciel');</syntaxhighlight> | |||
<syntaxhighlight lang="python">import matplotlib.pyplot as plt | |||
fig, ax = plt.subplots() | |||
ax.set_aspect('equal') | |||
ax.grid(True) | |||
# Steny bludiska | |||
ax.plot(map_x, map_y, '.', | |||
color='gray', | |||
markersize=0.5) | |||
# Trajektoria | |||
ax.plot(robot_x, robot_y, | |||
'b-', linewidth=2) | |||
# Start a ciel | |||
ax.plot(0, 0, 'go', | |||
markersize=10) | |||
ax.plot(robot_x[-1], | |||
robot_y[-1], 'rs', | |||
markersize=8) | |||
ax.set_xlabel('x [m]') | |||
ax.set_ylabel('y [m]') | |||
ax.set_title('Mapa bludiska') | |||
ax.legend(['Steny', | |||
'Trajektoria', | |||
'Start', 'Ciel']) | |||
plt.show()</syntaxhighlight></li> | |||
<li><p>'''Porovnajte tri metódy orientácie.'''</p> | |||
<p>Vykreslite trajektóriu a priebeh uhla <math display="inline">\theta(t)</math> pre všetky tri prístupy (A, B, C) do spoločného grafu. Diskutujte rozdiely.</p></li></ol> | |||
= Kontrolné otázky a bonusové úlohy = | |||
# Prečo používame ''stredný uhol'' <math display="inline">\theta_{mid}</math> a nie <math display="inline">\theta(k{-}1)</math>? Aká chyba by vznikla pri čistej Eulerovej metóde? | |||
# Čo sa stane, ak vynecháte korekciu pretečenia enkodérov? Vyskúšajte a porovnajte výsledky. | |||
# Pomocou mapy z lidaru odmerajte rozmery bludiska. Porovnajte so skutočnosťou. | |||
# Dokážete z lidarových dát ''overiť'' prepočtovú konštantu ''c_f''? ''Pomôcka:'' lidar meria vzdialenosti v milimetroch absolútne — nezávisí od ''c_f''. Nájdite vzdialenú stenu viditeľnú zo začiatku aj z konca trasy a porovnajte posun nameraný lidarom s počtom tickov enkodéra. | |||
# '''(Bonus)''' Implementujte jednoduchú mriežkovú mapu (occupancy grid) a porovnajte s mračnom bodov. | |||
# '''(Bonus)''' Čo sa stane s mapou pri dlhšej jazde (desiatky metrov)? Prečo sa steny “rozmazávajú”? Aké riešenie ponúka SLAM? | |||
= Príloha: Užitočné konštanty a vzorce = | |||
<div class="center"> | |||
{| class="wikitable" | |||
|- | |||
|| Rozchod kolies | |||
|| ''L'' = 0,230 m | |||
|- | |||
|| Prepočtový faktor | |||
|| ''c<sub>f</sub>'' = 0,000085292 m tick<sup>−1</sup> | |||
|- | |||
|| Pretečenie enkodérov | |||
|| 16-bit: rozsah <math display="inline">0</math> – <math display="inline">65 535</math>, prah <math display="inline">±32 768</math> | |||
|- | |||
|| Pretečenie gyroskopu | |||
|| rozsah <math display="inline">±180^{\circ}</math>, prah <math display="inline">±180^{\circ}</math> | |||
|- | |||
|| Konverzia stupne → radiány | |||
|| <math display="inline">\alpha_{rad} = \alpha_{\deg} · \pi / 180</math> | |||
|- | |||
|| Konverzia mm → m | |||
|| <math display="inline">d_{\text{m}} = d_{mm} / 1000</math> | |||
|} | |||
[[Kategória:Cvičenia]] | |||
[[Kategória:Mobilná robotika]] | |||
[[Kategória:Kobuki]] | |||
Aktuálna revízia z 19:16, 6. máj 2026
Mapovanie bludiska mobilným robotom
Cvičenie z mobilnej robotiky — Kobuki + RPlidar A1
Cieľ cvičenia
Cieľom cvičenia je ručne previesť mobilného robota cez bludisko, zaznamenať dáta z odometrie a laserového diaľkomera (lidaru), a následne z nameraných dát zrekonštruovať:
- trajektóriu robota v rovine x–y (dead reckoning z enkodérov a gyroskopu),
- plánik bludiska (mračno bodov z lidaru transformované do globálnych súradníc).
Spracovanie dát vykonáte offline v prostredí Matlab alebo Python na základe dvoch logovacích súborov: robot.log (odometria) a laser.log (lidar).
Technický opis zariadení
Mobilný robot Kobuki
Kobuki je kompaktná mobilná platforma s diferenciálnym podvozkom — má dve nezávisle poháňané kolesá a jedno voľnobežné (kastorové) koleso. Pohyb robota sa meria inkrementálnymi optickými enkodérmi na oboch kolesách a jednoosovým gyroskopom.
| Kategória | Parameter | Hodnota |
|---|---|---|
| Rozmery | Priemer tela | 351.5 mm (kruh) |
| Výška | 124.8 mm | |
| Pohon a kinematika | Typ podvozku | diferenciálny (2 kolesá + kastor) |
| Rozchod kolies (wheelbase) | L = 230 mm | |
| Polomer kolesa | r = 35 mm | |
| Šírka kolesa | 21 mm | |
| Dynamika | Max. translačná rýchlosť | 70 cm s−1 |
| Max. rotačná rýchlosť | 180°/s (gyroskop spoľahlivý do 110°/s) | |
| Enkodéry | Rozlíšenie enkodéra | 52 tick/ot. enkodéra |
| Prevodový pomer | 6545 : 132 = 49,5833 | |
| Ticky na otáčku kolesa | 52 × 49,5833 = 2578,33 tick/ot. | |
| Registre enkodérov | 16-bit unsigned (0–65 535) | |
| Prepočtové konštanty | Tick → metre | cf = 0,000085292 m tick−1 |
| Ekvivalent | ≈ 11,7 tick/mm | |
| Gyroskop | Typ | 1-osový, továrensky kalibrovaný |
| Rozsah | do 110°/s | |
| Senzory a bezpečnosť | Nárazníky (bumpers) | ľavý, stredný, pravý |
| Senzory útesu (cliff) | ľavý, stredný, pravý | |
| Senzor poklesu kolesa | ľavý, pravý | |
| Dátová frekvencia | 50 Hz | |
| Pripojenie k PC | USB alebo RX/TX piny | |
| Ochrana motora | vypnutie pri | |
| Napájanie | Batéria | Li-Ion 14.8 V, 2200 mAh |
| Výdrž | 3/7 h (malá/veľká batéria) | |
| Nosnosť | 5 kg (tvrdá podlaha) / 4 kg (koberec) |
Laserový diaľkomer RPlidar A1
RPlidar A1 je 2D laserový skener (lidar), ktorý rotáciou laserového lúča okolo zvislej osi sníma vzdialenosti k prekážkam v celom rozsahu 360°. Senzor je upevnený na robot v strede medzi kolesami (offset ).
| Kategória | Parameter | Hodnota |
|---|---|---|
| Rozmery a hmotnosť | Rozmery | 98,5 × 70 × 60 mm |
| Hmotnosť | 170 g | |
| Napájanie | externé (z robota) | |
| Rozsah | Vzdialenosť | 0.15 – 12 m (biele objekty) |
| Uhlový rozsah | 0 – 360° | |
| Rozlíšenie vzdialenosti | < 0,5 mm | |
| Rýchlosť | Uhlové rozlíšenie | < 1° |
| Doba jedného merania | 0.5 ms | |
| Vzorkovacia frekvencia | 2000 – 2010 Hz | |
| Rýchlosť otáčania (scan rate) | 1 – 10 Hz, typicky 5.5 Hz | |
| Kvalita merania | Rozsah hodnôt | 0 – 15 (vyššia = lepšia) |
Postup merania
- Umiestnite robota na štartovaciu pozíciu pred vstupom do bludiska. Zapnite robota a spustite záznam dát.
- Pomocou joysticku preveďte robota cez celé bludisko. Prejdite aj všetky slepé uličky — cieľom je získať kompletný obraz o geometrii bludiska.
- Pohybujte sa plynulo, bez prudkých zmien smeru. Pri otáčaní neprekračujte rotačnú rýchlosť 110°/s (limit gyroskopu).
- Po prechode bludiskom zastavte robota a ukončite záznam.
- Z robota stiahnite dva logovacie súbory:
robot.logalaser.log.
Formát záznamových súborov
Súbor robot.log — odometria
Každý riadok obsahuje jedno meranie, hodnoty sú oddelené medzerami:
<timestamp_us> <enc_left> <enc_right> <gyro_deg>
| Stĺpec | Typ | Popis |
|---|---|---|
| 1 — Timestamp | uint64
|
Čas merania v mikrosekundách |
| 2 — Enc. Left | uint16
|
Kumulatívny počet tickov ľavého enkodéra (0–65 535) |
| 3 — Enc. Right | uint16
|
Kumulatívny počet tickov pravého enkodéra (0–65 535) |
| 4 — Gyro | float
|
Uhol z gyroskopu v stupňoch (-180 až +180) |
Príklad záznamov:
3067284773 1288 58777 174.98 3067301111 1288 58777 174.98 3067323915 1288 58777 174.98
Frekvencia záznamu zodpovedá dátovej frekvencii robota, t. j. približne 50 Hz.
Súbor laser.log — lidar
Každý riadok obsahuje jedno meranie laserového lúča:
<timestamp_us> <distance_mm> <angle_deg> <quality>
| Stĺpec | Typ | Popis |
|---|---|---|
| 1 — Timestamp | uint64
|
Čas merania v mikrosekundách |
| 2 — Distance | float
|
Vzdialenosť k prekážke v milimetroch |
| 3 — Angle | float
|
Uhol laserového lúča v stupňoch (0–360), v smere hodinových ručičiek od prednej časti robota |
| 4 — Quality | int
|
Kvalita merania (0–15); 0 = neplatné |
Príklad záznamov:
3067236764 3713.75 351.047 15 3067237463 3666.75 352.438 15 3067237467 3708.75 353.812 15
Jedna kompletná otáčka lidaru (sken) obsahuje typicky okolo 260 bodov. Pri scan rate cca 7,8 Hz to zodpovedá vzorkovacej frekvencii cca 2000 Hz.
Teoretický základ — ideálny prípad
V tejto kapitole odvodíme všetky vzťahy za predpokladu, že dáta sú bezchybné: enkodéry nepretekajú, gyroskop nedriftuje a časy sú presne synchronizované. Praktické komplikácie riešime v kapitole 6.
Kinematika diferenciálneho podvozku
Robot má dve kolesá vzdialené L = 0,23 m. Stredom medzi nimi je referenčný bod P, ktorého polohu (x, y) a orientáciu θ chceme sledovať.
V každom časovom kroku (medzi vzorkou k-1 a k) enkodéry napočítajú Δtick_L a Δtick_R impulzov. Dráha jednotlivých kolies je: d_L = Δtick_L · c_f, d_R = Δtick_R · c_f, kde cf = 0,000085292 m tick−1 je prepočtový faktor.
Obr. 1: Kinematický model diferenciálneho podvozku. dL, dR sú dráhy kolies, dc je stredná dráha bodu P.
Stredná dráha (pohyb bodu P) a zmena orientácie sú:
d_c = (d_L + d_R)/2, Δθ = (d_R - d_L)/L.
Intuitívne: ak sa obe kolesá pohnú rovnako (d_L = d_R), robot ide rovno (Δθ = 0). Ak sa pravé koleso pohne viac, robot zatáča doľava (Δθ > 0 v štandardnej konvencii).
Aktualizácia polohy (dead reckoning)
Polohu robota aktualizujeme iteratívne. Používame aproximáciu stredným uhlom (second-order midpoint method), ktorá je výrazne presnejšia než jednoduchá Eulerova metóda:
θ_mid = \theta(k-1) + Δθ/2 x(k) = x(k-1) + d_c · cos(θ_mid) y(k) = y(k-1) + d_c · sin(θ_mid) θ(k) = θ(k-1) + Δθ
Počiatočné podmienky: x(0) = 0, y(0) = 0, θ(0) = 0 (robot smeruje v osi x).
Tri zdroje orientácie
Pre výpočet zmeny uhla máme tri možnosti:
A) Len enkodéry
Uhol sa počíta podľa rovnice [eq:dc_dtheta]. Jednoduché, ale citlivé na šmýkanie kolies.
B) Len gyroskop
Gyroskop Kobuki dáva priamo absolútny uhol (firmvér integruje uhlovú rýchlosť interne). Zmenu uhla medzi dvoma vzorkami vypočítame ako: Syntaktická analýza (parsing) neúspešná (syntaktická chyba): {\displaystyle \Delta\theta_{gyro} = [gyro(k) - gyro(k{-}1)] · \frac{\pi}{180} \label{eq:gyro_delta}} s korekciou pretečenia cez Syntaktická analýza (parsing) neúspešná (syntaktická chyba): {\textstyle ±180^{\circ}} (pozri sekciu 6.2). Táto metóda je odolná voči šmyku kolies, ale dlhodobo trpí driftom gyroskopu.
C) Komplementárny filter
Fúzia oboch zdrojov jedným parametrom : Syntaktická analýza (parsing) neúspešná (neznáma funkcia „\boxed“): {\displaystyle \boxed{\Delta\theta = \alpha · \Delta\theta_{gyro} + (1 - \alpha) · \Delta\theta_{enc}} \label{eq:compfilter}}
Parameter vyjadruje “dôveru” v gyroskop. Typicky — gyroskop je na krátkych škálach presnejší, enkodéry kompenzujú jeho dlhodobý drift. Na ladenie použite uzavretú trasu — správne Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \alpha} minimalizuje odchýlku koncovej polohy od štartu.
Transformácia lidarových bodov do mapy
Lidar meria vzdialenosť d v smere uhla Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \alpha} (v stupňoch, rastúci v smere hodinových ručičiek od prednej časti robota). Cieľom je transformovať tento bod zo súradníc robota do globálnych (mapových) súradníc.
Priama transformácia z polárnych súradníc lidaru do mapy: Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\displaystyle \boxed{ \begin{aligned} \alpha_{world} &= \theta - \alpha · \frac{\pi}{180} \\[4pt] x_{global} &= x_R + \frac{d}{1000} · \cos(\alpha_{world}) \\[2pt] y_{global} &= y_R + \frac{d}{1000} · \sin(\alpha_{world}) \end{aligned} } \label{eq:lidar_transform}} kde je orientácia robota v radiánoch, Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \alpha} je uhol lidaru v stupňoch (CW), d je vzdialenosť v mm a Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle (x_R, y_R)} je poloha robota v metroch. Znamienko mínus pred Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \alpha} konvertuje smysel otáčania z CW na matematicky kladný (CCW).
Riešenie praktických problémov
Pretečenie enkodérov
Enkodéry Kobuki sú 16-bitové bezznamienkové čítače s rozsahom – Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 65 535} . Keď čítač dosiahne Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 65 535} a koleso sa ďalej otáča, hodnota preskočí na Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 0} (a opačne pri pohybe vzad). V surovom rozdiele Syntaktická analýza (parsing) neúspešná (neznáma funkcia „\Deltatick“): {\textstyle \Deltatick = enc(k) - enc(k{-}1)} sa to prejaví obrovským skokom.
Korekcia: Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\displaystyle \Deltatick_{cor} = \begin{cases} \Deltatick - 65536 & \text{ak } \Deltatick > 32768 \\ \Deltatick + 65536 & \text{ak } \Deltatick < -32768 \\ \Deltatick & \text{inak} \end{cases} \label{eq:wrap_enc}}
Ukážka v Matlabu / Pythone:
dTick = enc(k) - enc(k-1);
if dTick > 32768
dTick = dTick - 65536;
end
if dTick < -32768
dTick = dTick + 65536;
end
dTick = enc[k] - enc[k-1]
if dTick > 32768:
dTick -= 65536
if dTick < -32768:
dTick += 65536
Pretečenie gyroskopu
Gyroskop vracia uhol v rozsahu Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle (-180^{\circ}, +180^{\circ}]} . Pri prechode cez Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle ±180^{\circ}} hodnota skočí, napr. z na Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle -179^{\circ}} . To nie je skutočná zmena o Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 358^{\circ}} , ale o Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle +2^{\circ}} .
Korekcia: Syntaktická analýza (parsing) neúspešná (neznáma funkcia „\Deltagyro“): {\displaystyle \Deltagyro_{cor} = \begin{cases} \Deltagyro - 360 & \text{ak } \Deltagyro > 180 \\ \Deltagyro + 360 & \text{ak } \Deltagyro < -180 \\ \Deltagyro & \text{inak} \end{cases} \label{eq:wrap_gyro}}
dGyro = gyro(k) - gyro(k-1);
if dGyro > 180
dGyro = dGyro - 360;
end
if dGyro < -180
dGyro = dGyro + 360;
end
dTheta = dGyro * pi / 180;
dGyro = gyro[k] - gyro[k-1]
if dGyro > 180:
dGyro -= 360
if dGyro < -180:
dGyro += 360
dTheta = dGyro * math.pi / 180
Synchronizácia časových značiek
Odometria sa zaznamenáva s frekvenciou Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle ≈ 50 Hz} , lidar produkuje Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle ≈ 2000} bodov za sekundu. Časové značky pochádzajú z rovnakých hodín, ale nie sú identické. Pre každý lidarový bod musíme nájsť najbližšiu polohu robota.
Efektívne riešenie — paralelný prechod oboma logmi s jedným ukazovateľom:
ridx = 1;
for i = 1:length(laser_ts)
while ridx < N && ...
abs(robot_ts(ridx+1) ...
- laser_ts(i)) < ...
abs(robot_ts(ridx) ...
- laser_ts(i))
ridx = ridx + 1;
end
% robot_ts(ridx) je
% najblizsi k laser_ts(i)
end
ridx = 0
for i in range(len(laser_ts)):
while ridx < N-1 and \
abs(robot_ts[ridx+1]
- laser_ts[i]) < \
abs(robot_ts[ridx]
- laser_ts[i]):
ridx += 1
# robot_ts[ridx] je
# najblizsi k laser_ts[i]
Tento algoritmus má lineárnu zložitosť Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle O(N+M)}
, pretože ukazovateľ ridx sa nikdy nevracia — funguje to, pretože oba logy sú chronologicky usporiadané.
Filtrovanie neplatných lidarových meraní
Nie všetky lidarové body sú platné. Pred spracovaním je nutné vyradiť:
- merania s nulovou vzdialenosťou () — lidar nezachytil odraz,
- merania s nízkou kvalitou (odporúčame prah Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \text{quality} ≥ 10} ),
- voliteľne merania mimo dosahu senzora (Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle d > 12000 mm} ).
if laser_dist(i) < 20 || ...
laser_qual(i) < 10
continue;
end
if dist < 20 or quality < 10:
continue
Postup spracovania dát
Tu je celkový algoritmus zhrnutý do piatich krokov. Kúsky kódu z predchádzajúcich sekcií spojte do jedného skriptu.
Načítajte oba súbory.
R = load('robot.log'); robot_ts = R(:,1); encL = R(:,2); encR = R(:,3); gyro = R(:,4); L = load('laser.log'); laser_ts = L(:,1); laser_d = L(:,2); laser_a = L(:,3); laser_q = L(:,4);
import numpy as np, math R = np.loadtxt('robot.log') robot_ts = R[:,0] encL = R[:,1]; encR = R[:,2] gyro = R[:,3] L = np.loadtxt('laser.log') laser_ts = L[:,0] laser_d = L[:,1] laser_a = L[:,2] laser_q = L[:,3]
Vypočítajte trajektóriu robota (sekcie 5.1–5.2).
Pre každý krok Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle k = 2, \ldots, N} :
Vypočítajte Syntaktická analýza (parsing) neúspešná (neznáma funkcia „\Deltatick“): {\textstyle \Deltatick_L} , Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \Deltatick_R} s korekciou pretečenia (rov. [eq:wrap_enc]).
Prepočítajte na dráhy d_L, d_R (rov. [eq:dl_dr]).
Vypočítajte d_c a Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \Delta\theta} (rov. [eq:dc_dtheta] alebo [eq:gyro_delta] alebo [eq:compfilter]).
Aktualizujte Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle x(k)} , , Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \theta(k)} (rov. [eq:update]).
Výsledkom sú polia
robot_x,robot_y,robot_thetao dĺžke N.Transformujte lidarové body do mapy (sekcia 5.4).
Pre každé platné meranie i (po filtrovaní, sekcia 6.4):
Nájdite najbližšiu polohu robota k podľa timestampu (sekcia 6.3).
Aplikujte transformáciu (rov. [eq:lidar_transform]) s použitím Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle (x_R, y_R, \theta)} z kroku k.
Uložte globálne súradnice bodu do poľa mapy.
Vykreslite výsledok.
figure; hold on; axis equal; grid on; % Steny bludiska plot(map_x, map_y, '.', ... 'Color', [.5 .5 .5], ... 'MarkerSize', 1); % Trajektoria plot(robot_x, robot_y, ... 'b-', 'LineWidth', 2); % Start a ciel plot(0, 0, 'go', ... 'MarkerSize', 10, ... 'MarkerFaceColor', 'g'); plot(robot_x(end), ... robot_y(end), 'rs', ... 'MarkerSize', 8, ... 'MarkerFaceColor', 'r'); xlabel('x [m]'); ylabel('y [m]'); title('Mapa bludiska'); legend('Steny', 'Trajektoria', ... 'Start', 'Ciel');
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.set_aspect('equal') ax.grid(True) # Steny bludiska ax.plot(map_x, map_y, '.', color='gray', markersize=0.5) # Trajektoria ax.plot(robot_x, robot_y, 'b-', linewidth=2) # Start a ciel ax.plot(0, 0, 'go', markersize=10) ax.plot(robot_x[-1], robot_y[-1], 'rs', markersize=8) ax.set_xlabel('x [m]') ax.set_ylabel('y [m]') ax.set_title('Mapa bludiska') ax.legend(['Steny', 'Trajektoria', 'Start', 'Ciel']) plt.show()
Porovnajte tri metódy orientácie.
Vykreslite trajektóriu a priebeh uhla Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \theta(t)} pre všetky tri prístupy (A, B, C) do spoločného grafu. Diskutujte rozdiely.
Kontrolné otázky a bonusové úlohy
- Prečo používame stredný uhol a nie Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \theta(k{-}1)} ? Aká chyba by vznikla pri čistej Eulerovej metóde?
- Čo sa stane, ak vynecháte korekciu pretečenia enkodérov? Vyskúšajte a porovnajte výsledky.
- Pomocou mapy z lidaru odmerajte rozmery bludiska. Porovnajte so skutočnosťou.
- Dokážete z lidarových dát overiť prepočtovú konštantu c_f? Pomôcka: lidar meria vzdialenosti v milimetroch absolútne — nezávisí od c_f. Nájdite vzdialenú stenu viditeľnú zo začiatku aj z konca trasy a porovnajte posun nameraný lidarom s počtom tickov enkodéra.
- (Bonus) Implementujte jednoduchú mriežkovú mapu (occupancy grid) a porovnajte s mračnom bodov.
- (Bonus) Čo sa stane s mapou pri dlhšej jazde (desiatky metrov)? Prečo sa steny “rozmazávajú”? Aké riešenie ponúka SLAM?
Príloha: Užitočné konštanty a vzorce
| Rozchod kolies | L = 0,230 m |
| Prepočtový faktor | cf = 0,000085292 m tick−1 |
| Pretečenie enkodérov | 16-bit: rozsah Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 0} – Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle 65 535} , prah Syntaktická analýza (parsing) neúspešná (syntaktická chyba): {\textstyle ±32 768} |
| Pretečenie gyroskopu | rozsah Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle ±180^{\circ}} , prah Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle ±180^{\circ}} |
| Konverzia stupne → radiány | Syntaktická analýza (parsing) neúspešná (MathML s fallbackom na SVG alebo PNG (odporúčané pre moderné prehliadače a nástroje pre zjednodušenie prístupu): Neplatná odpověď („Math extension cannot connect to Restbase.“) od serveru „https://wikimedia.org/api/rest_v1/“:): {\textstyle \alpha_{rad} = \alpha_{\deg} · \pi / 180} |
| Konverzia mm → m |