Operácie

Kobuki: Rozdiel medzi revíziami

Zo stránky SensorWiki

Balogh (diskusia | príspevky)
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…“
 
Balogh (diskusia | príspevky)
 
(6 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 1: Riadok 1:
= Návod na cvičenie: Rekonštrukcia trajektórie a mapovanie bludiska =
= Mapovanie bludiska mobilným robotom =
'''Cvičenie z mobilnej robotiky — Kobuki + RPlidar A1'''


== 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.
__TOC__


== Technický opis zariadení ==
= Cieľ cvičenia =


=== Mobilná platforma Kobuki ===
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ť:
Kobuki je nízkonákladová mobilná platforma s diferenciálnym podvozkom, určená na výskum a vzdelávanie. Robot je vybavený dvoma pohonnými kolesami a pomocnými pasívnymi kolieskami pre stabilitu. Obsahuje inkrementálne enkodéry na motoroch a interný jednoosový gyroskop na meranie uhlovej rýchlosti a natočenia.


=== Laserový skener RPLidar A1 ===
# '''trajektóriu robota''' v rovine ''x''–''y'' (dead reckoning z enkodérov a gyroskopu),
RPLidar A1 je 2D laserový skener (LIDAR) pracujúci na princípe triangulácie. Senzor rotuje v rozsahu 360 stupňov a meria vzdialenosti k prekážkam. Je umiestnený v geometrickom strede robota Kobuki, čo zjednodušuje transformáciu súradníc.
# '''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).
Z nasledujúcich tabuliek vyberte parametre relevantné pre vaše výpočty.
 
= 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"
|+ Parametre robota Kobuki
|+ Technické parametre lidaru RPlidar A1
! Parameter !! Hodnota
|-
!| '''Kategória'''
!| '''Parameter'''
!| '''Hodnota'''
|-
| rowspan="3"| Rozmery a hmotnosť
|| Rozmery
|| 98,5 × 70 × 60 mm
|-
|| Hmotnosť
|| 170 g
|-
|-
| Maximálna translačná rýchlosť || 70 cm/s
|| Napájanie
|| externé (z robota)
|-
|-
| Maximálna rotačná rýchlosť || 180 deg/s
| rowspan="3"| Rozsah
|| Vzdialenosť
|| 0.15 – 12 m (biele objekty)
|-
|-
| Rázvor kolies (L) || 230 mm
|| Uhlový rozsah
|| 0 – 360°
|-
|-
| Rozlíšenie enkodéra || 2578.33 ticks/wheel rev
|| Rozlíšenie vzdialenosti
|| < 0,5 mm
|-
|-
| Hustota impulzov || 11.7 ticks/mm
| rowspan="4"| Rýchlosť
|| Uhlové rozlíšenie
|| < 1°
|-
|-
| Frekvencia senzorových dát || 50 Hz
|| Doba jedného merania
|| 0.5 ms
|-
|-
| Napätie batérie || 14.8 V
|| Vzorkovacia frekvencia
|| 2000 – 2010 Hz
|-
|-
| Hmotnosť || 2.35 kg
|| 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>&lt;timestamp_us&gt; &lt;enc_left&gt; &lt;enc_right&gt; &lt;gyro_deg&gt;</code>


{| class="wikitable"
{| class="wikitable"
|+ Parametre RPLidar A1
|+ Štruktúra súboru <code>robot.log</code>
! Parameter !! Hodnota
|-
!| '''Stĺpec'''
!| '''Typ'''
!| '''Popis'''
|-
|-
| Rozsah merania || 0.15 – 12 m
|| 1 — Timestamp
|| <code>uint64</code>
|| Čas merania v mikrosekundách
|-
|-
| Uhlový rozsah || 0 – 360°
|| 2 — Enc. Left
|| <code>uint16</code>
|| Kumulatívny počet tickov ľavého enkodéra (0–65 535)
|-
|-
| Uhlové rozlíšenie || ≤ 1°
|| 3 — Enc. Right
|| <code>uint16</code>
|| Kumulatívny počet tickov pravého enkodéra (0–65 535)
|-
|-
| Frekvencia skenovania || 5.5 Hz (typická)
|| 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>&lt;timestamp_us&gt; &lt;distance_mm&gt; &lt;angle_deg&gt; &lt;quality&gt;</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
|-
|-
| Smer rotácie || V smere hod. ručičiek (CW)
|| 2 — Distance
|| <code>float</code>
|| Vzdialenosť k prekážke v milimetroch
|-
|-
| Napájanie || 5 V / 500 mA
|| 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
|-
|-
| Presnosť vzdialenosti || < 1% (pri 12 m)
|| 4 — Quality
|| <code>int</code>
|| Kvalita merania (0–15); 0 = neplatné
|}
|}


== Metodika merania ==
Príklad záznamov:
# Pomocou joysticku alebo klávesnice ovládajte robota v prostredí bludiska.
# Prejdite všetky hlavné chodby aj slepé uličky. Pomalší a plynulý pohyb zaručuje kvalitnejšie dáta.
# Počas pohybu systém automaticky ukladá dáta do súborov '''robot.log''' a '''laser.log'''.


== Formát nameraných dát ==
  3067236764  3713.75  351.047  15
  3067237463  3666.75  352.438  15
  3067237467  3708.75  353.812  15


=== Súbor robot.log ===
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.
Každý riadok obsahuje jeden časový záznam:
# '''Timestamp [us]''' – Čas merania v mikrosekundách.
# '''Left Encoder''' – Kumulatívny počet impulzov ľavého kolesa.
# '''Right Encoder''' – Kumulatívny počet impulzov pravého kolesa.
# '''Gyro Angle [deg]''' – Aktuálny uhol natočenia robota v stupňoch.


=== Súbor laser.log ===
= Teoretický základ — ideálny prípad =
Obsahuje surové merania z lidaru:
# '''Timestamp [us]''' – Synchronizačný čas.
# '''Distance [mm]''' – Vzdialenosť k prekážke.
# '''Angle [deg]''' – Uhol lúča voči osi robota (0° = vpred).
# '''Quality''' – Kvalita odrazu (celé číslo).


== Teória výpočtu ==
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]].


=== Ideálny model pohybu ===
== Kinematika diferenciálneho podvozku ==
V ideálnom prípade predpokladáme nekonečné rozlíšenie a nulovú chybu. Pre diferenciálny podvozok platí výpočet elementárneho posunu <math> \Delta s </math>:
:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  \Delta s = \frac{\Delta s_R + \Delta s_L}{2}
</math>


Natočenie robota <math> \theta_i </math> berieme priamo z gyroskopu. Nová poloha v rovine sa vypočíta integráciou:
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ť.
:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  x_i = x_{i-1} + \Delta s \cdot \cos(\theta_i)
</math>
:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  y_i = y_{i-1} + \Delta s \cdot \sin(\theta_i)
</math>


=== Ideálne mapovanie ===
Bod nameraný lidarom (vzdialenosť <math> r </math>, uhol <math> \alpha </math>) prenesieme do globálnej mapy pomocou natočenia robota <math> \theta </math>:
:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  X_{wall} = x_R + r \cdot \cos(\theta + \alpha)
</math>
:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  Y_{wall} = y_R + r \cdot \sin(\theta + \alpha)
</math>


== Praktické problémy a implementácia ==


=== Pretečenie enkodérov ===
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'',
Enkodéry robota sú 16-bitové nesignované integery (0 – 65535). Pri výpočte rozdielu <math> \Delta ticks </math> musíte ošetriť skokovú zmenu hodnoty pri pretečení (overflow).
kde ''c<sub>f</sub>'' = 0,000085292 m tick<sup>−1</sup> je prepočtový faktor.


'''Ukážka v Matlabe:'''
<div id="fig:kinematika" class="figure"></div>
<pre>
'''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.''
diff = current - previous;
if diff > 32767, diff = diff - 65536; end
if diff < -32768, diff = diff + 65536; end
</pre>


'''Ukážka v Pythone:'''
Stredná dráha (pohyb bodu ''P'') a zmena orientácie sú:  
<pre>
d_c = (d_L + d_R)/2,     
diff = current - previous
  Δθ = (d_R - d_L)/L.
if diff > 32767: diff -= 65536
elif diff < -32768: diff += 65536
</pre>


=== Uhol a smerovanie ===
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 vracia uhol v rozsahu ±180°. Pred interpoláciou sa odporúča použiť funkciu '''unwrap''', ktorá odstráni skoky pri prechode cez hranicu.
 
* '''Pozor:''' Lidar meria uhol <math> \alpha </math> v smere CW (hodinové ručičky), zatiaľ čo goniometria v kódovacích jazykoch je CCW. Preto pri výpočte používajte záporný uhol lidaru (<math> -\alpha </math>).
== 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 &gt; 32768
    dTick = dTick - 65536;
end
if dTick &lt; -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 &gt; 180
    dGyro = dGyro - 360;
end
if dGyro &lt; -180
    dGyro = dGyro + 360;
end
dTheta = dGyro * pi / 180;</syntaxhighlight>
<syntaxhighlight lang="python">dGyro = gyro[k] - gyro[k-1]
if dGyro &gt; 180:
    dGyro -= 360
if dGyro &lt; -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 &lt; N &amp;&amp; ...
      abs(robot_ts(ridx+1) ...
        - laser_ts(i)) &lt; ...
      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 &lt; N-1 and \
      abs(robot_ts[ridx+1]
        - laser_ts[i]) &lt; \
      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) &lt; 20 || ...
  laser_qual(i) &lt; 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>
|}


=== Synchronizácia časových značiek ===
Dáta z lidaru a odometrie nie sú zaznamenané v rovnakom čase. Pre každý záznam z laser.log musíte nájsť polohu robota v danom čase pomocou lineárnej interpolácie.


'''Matlab:''' <code>interp1(robot_time, robot_x, laser_time)</code>
'''Python (numpy):''' <code>np.interp(laser_time, robot_time, robot_x)</code>


== Úlohy ==
[[Kategória:Cvičenia]]
# Načítajte logy a vykonajte konverziu jednotiek (impulzy -> metre, mm -> metre).
[[Kategória:Mobilná robotika]]
# Vypočítajte trajektóriu [x, y] a vykreslite ju.
[[Kategória:Kobuki]]
# Transformujte body z lidaru do globálnych súradníc (filtrujte body s kvalitou 0 a vzdialenosťou mimo 0.15 - 6 m).
# Vykreslite mapu bludiska (čierne body) a do nej vložte trajektóriu robota (farebná čiara).

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ť:

  1. trajektóriu robota v rovine xy (dead reckoning z enkodérov a gyroskopu),
  2. 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.

Technické parametre robota Kobuki
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 ).

Technické parametre lidaru RPlidar A1
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

  1. Umiestnite robota na štartovaciu pozíciu pred vstupom do bludiska. Zapnite robota a spustite záznam dát.
  2. 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.
  3. Pohybujte sa plynulo, bez prudkých zmien smeru. Pri otáčaní neprekračujte rotačnú rýchlosť 110°/s (limit gyroskopu).
  4. Po prechode bludiskom zastavte robota a ukončite záznam.
  5. Z robota stiahnite dva logovacie súbory: robot.loglaser.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>

Štruktúra súboru robot.log
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>

Štruktúra súboru laser.log
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-1k) enkodéry napočítajú Δtick_LΔ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}}

Šablóna:Upozornenie

Ukážka v Matlabu / Pythone:

dTick = enc(k) - enc(k-1);
if dTick &gt; 32768
    dTick = dTick - 65536;
end
if dTick &lt; -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 &gt; 180
    dGyro = dGyro - 360;
end
if dGyro &lt; -180
    dGyro = dGyro + 360;
end
dTheta = dGyro * pi / 180;
dGyro = gyro[k] - gyro[k-1]
if dGyro &gt; 180:
    dGyro -= 360
if dGyro &lt; -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 &lt; N &amp;&amp; ...
      abs(robot_ts(ridx+1) ...
        - laser_ts(i)) &lt; ...
      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 &lt; N-1 and \
      abs(robot_ts[ridx+1] 
        - laser_ts[i]) &lt; \
      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) &lt; 20 || ...
   laser_qual(i) &lt; 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.

  1. 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]
    
  2. Vypočítajte trajektóriu robota (sekcie 5.15.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} :

    1. 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]).

    2. Prepočítajte na dráhy d_L, d_R (rov. [eq:dl_dr]).

    3. Vypočítajte d_cSyntaktická 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]).

    4. 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_theta o dĺžke N.

  3. Transformujte lidarové body do mapy (sekcia 5.4).

    Pre každé platné meranie i (po filtrovaní, sekcia 6.4):

    1. Nájdite najbližšiu polohu robota k podľa timestampu (sekcia 6.3).

    2. 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.

    3. Uložte globálne súradnice bodu do poľa mapy.

  4. 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()
    
  5. 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

  1. 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?
  2. Čo sa stane, ak vynecháte korekciu pretečenia enkodérov? Vyskúšajte a porovnajte výsledky.
  3. Pomocou mapy z lidaru odmerajte rozmery bludiska. Porovnajte so skutočnosťou.
  4. 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.
  5. (Bonus) Implementujte jednoduchú mriežkovú mapu (occupancy grid) a porovnajte s mračnom bodov.
  6. (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