czech english

Marina

projekt Roboloď

Projekt „Roboloď” pro mne začal na vánočním večírku 2015, tj. skoro před třemi lety. Bývalý kolega z práce říkal, že jeho kamarád shání někoho, kdo by dokázal zrobotizovat lodičku zavážející krmení do sádek na rybníce. Projekt byl (a stále je) takový „tajuplný”, ale nakonec jsme se se dvěma dalšími kamarády rozhodli do toho jít. Když nic, tak ověříme, jak se nám spolupracuje a na vodě jsme s robotama ještě nebyli … Blog update: 8/9 — Vodní test 3

Historické pozadí

Po několika iteracích se ukázalo, že to je vlastně kamarád kamaráda, který to poptává pro svého dobrého zákazníka a ten to možná dělá ještě pro někoho dalšího. První domluva, o co přesně jde, nějaký čas trvala.
Asi první zklamání (setkání s realitou) bylo, když se ukázalo, že „zavážková lodička” je fakticky malý RC model. Do té doby jsem měl představu pramice nebo nějakého motorového člunu, který uveze člověka, který by zavážel návnadu na uživatelem zadanou GPS souřadnici. Zatímco u velké lodě bychom se s elektronikou a i nějakým vývojem do základní ceny možná nějak schovali, u plastové čínské hračky to už tehdy (kdybychom si to přiznali) nepřipadalo v úvahu.
Komerčně jsme to v tento okamžik měli vzdát, což jsme neudělali. „Vždyť pro to všechno máme a v Číně se dají součástky pořídit za pár drobných!” Jo, jo, kolikrát v životě člověk ještě podobnou chybu udělá
Na druhou stranu výhodou malé lodičky bylo, že nám ji jednoho dne mohla kamarádka kamaráda v krabici přivézt, a následně nebyl velký problém jít testovat „k lachtanovi” na Petřín nebo na Vltavu.

Technologie

PPM řízení serv

Ona to vlastně byla celkem pěkná hračka a holt jsme (obávám se, že nemůžu použít minulého času) jak děti a tak jsme ji vyzkoušeli a obratem rozebrali. Řízení lodi je diferenciální, tj. dva motory a dva vodní šrouby. Dálkově je možné ještě zapínat LEDky (hodí se v noci nebo za šera) a vysypávat náklad. Celkově čtyřkanálová vysílačka s přijímačem. Komunikace je jednosměrná, i když je možné dokoupit např. sonarový modul pro detekci ryb, a ten by pravděpodobně obsahoval i posílání dat zpět uživateli.
Připojení přijímače do řídící jednotky (časem pracovně nazývaná „čínská hlava”) je pomocí trojžilového kabelu. Zem, napájení a „nějaký” signál. Pulzy pro řízení serv již léta znám, ale jak je až 8 kanálů kombinovaných do jednoho signálu byla pro mne novinka. Jedná se o takzvané PPM kódování, co používá Futaba: jednotlivé 1.5ms dlouhé pulzy definují náběžnou hranou jednotlivé kanály a hlavní cyklus zachovává 20ms periodu (tj. 50Hz).
Před pár měsíci si Šimi pořídil digitální sondu a z ní lze vyčíst a nakreslit obrázek výše (kéž bychom toto měli na začátku).
A další plán? Napíchneme si na tento kabel „štěnici”, která bude signál odposlouchávat a podle typu navigace buď přijímaný signál přepošle dál nebo vygeneruje vlastní pro autonomní řízení. Snadné, není-liž pravda?

Zavařené čínské hlavy

U čínských výrobků jde hlavně o cenu, takže je očekávané (teď už to vím), že součástky nebudou nijak předimenzované, spíše naopak. Pro řízení motoru nebyl použit žádný H-můstek, jak je známe z našich robotů, ale kombinace relátek a tranzistorů, které na některé signální sekvence reagovaly vyhořením :-(. Trošku nás uklidnila (možná nás měla naopak více znervóznit!) zpráva od dodavatele, že se to občas děje i při normálním používání (?!), ale stavte nad tím autonomii!
Nakonec jsme používali pouze tři příkazy vlevo, rovně a vpravo a dávali si pozor ať z příkazu vlevo nepřecházíme do příkazu vpravo. Stejně tak jsme eliminovali couvání a tedy změnu směru otáčení motorů.

Senzory pro navigaci

Je až neuvěřitelné, jak za ta léta klesla cena GPS a IMU jednotek. Tento trend je jistě spojen s integrací senzorů do telefonů, ale možná i nejrůznějších hraček. Šimi poměrně dost brblal, když jsem chtěl nahradit 2D kompas z první dávky 3D kompasem s gyrem … přeci jenom to zdvojnásobilo cenu součástek (!). Poučení? U prvních prototypů neřešte cenu součástek, stejně nakonec budou v produkci jiné!

Komunikace

Automonmí řízení mělo být realizováno pomocí WiFi připojení. Šimi si již delší dobu hrál s oblíbeným ESP8266, zase v ceně pár dolarů v jednokusech a stačilo „jen to zapojit”.

Arduino

Pro odposlech přijímaného signálu a generování nového jsme chtěli použít Arduino. Zase je to o ceně - to ani obyčejnou destičku vám nevyrobí za cenu již připravené a osazené varianty s Arduinem. Arduino jsem již jednou programoval (viz FireAnt), ale stejně mne to párkrát dostalo (detaily viz dále).

Android aplikace a celkové schema

Aby byl obrázek kompletní, je třeba ještě zmínit hlavní řídící aplikaci, která běžela na Android telefonu (telefon nebyl součástí dodávky). Uživatel se k lodičce připojil pomocí WiFi, realizované ESP8266 čipem. Ten se dále postaral o sběr a přeposílání dat z GPS přes seriový port a IMU a Arduino přes I2C.

Komunikační protokol

Vše to začalo přeposíláním dat ze seriáku přes WiFi (je na to možná i nějaký vzorový příklad, ze kterého Šimi vycházel?). A jelikož na seriáku byla GPS, která mluvila NMEA protokolem, tak i výsledný komunikační protokol tomu zůstal hodně podobný, tj. textový, '$' na začátku a '*' s kontrolním součtem na konci.

TCP vs. UDP

Co je lepší používat na řízení lodičky, TCP nebo UDP protokol? Na první pohled by se zdálo, že samozřejmě TCP, tam je přece zaručený přenos, neztrácí se pakety a o vše se stará systém. Myslím, že i důvod pro první použití TCP byl právě příklad sériová linka-WiFi, který TCP používal (přeci jenom nechcete, aby se vám ztrácely bajty).
A jak to fungovalo? No zvláštně. S ultrastarým notebookem to šlo celkem dobře, ale s mým (teď už asi jenom 10 let starým) notebookem nikoliv. Detaily si teď už moc nepamatuji, ale pokud jsem po každém přijatém paketu něco poslal, tak to začalo fungovat lépe. Důvod? Ukázalo se, že TCP na straně ESP8266 úplně dobře nepodporuje okénko pro potvrzení přijetí, nebo to bylo špatně nakonfigurované, a novější OS jako default počítal s potvrzením až po několika zprávách. ESP8266 však udrželo jenom jeden TCP paket.
UDP s upraveným protokolem byla lepší volba. Co dělat při ztrátě paketu? Poslat ho znova nebo raději již poslat další s novějšími daty? Je to podobné jako u streamování videa - pokud něco vypadne, tak je to hloupé, ale pokud toho není moc, tak to skoro nepoznáte. Do UDP paketů tedy přibyl čítač (index paketu), aby bylo možné výpadky snadno detekovat. Přibyla i časová známka pro měření latence. Konečně lodička posílala i poslední akceptovaný příkaz, podle kterého momentálně naviguje.

Navigační smyčka

Klasicky verze 0 byla nejtriviálnější varianta, která by mohla fungovat:
  • zjisti GPS pozici
  • zjisti úhel z kompasu
  • spočítej úhel k cílovému bodu
  • je-li absolutní rozdíl úhlu (vezme-li se v úvahu 360 stupňů perioda) větší než daný limit, proveď korekci vlevo nebo vpravo, jinak jeď rovně
  • je-li vzdálenost menší než 2m, tak na chvíli zastav a pak naviguj na další bod
Prostě takový „vodní” RoboOrienteering . A kupodivu to nebylo až tak zlé! Resp. začaly vylézat úplně jiné problémy, než, které jsme rešili na začátku. Např. se ukázalo, že nutný požadavek je, aby se lodička autonomně navigovala do cílů vzdálených až 300m. Jako on tam občas nějaký WiFi packet doletí, ale na nějakou spolehlivou navigaci zapomeňte.
S tím byl spojen i problém ultra dlouhé zpětné vazby (navíc s vypadáváním paketů). Pošlete-li příkaz: „zatáčej doprava” a následných 10 příkazových paketů se ztratí, co myslíte, že se stane? HW byl poddimenzovaný, tak jsme to dál flikovali rozšířením příkazů o dobu jejich trvání (de-facto počet 20ms period) Po jejím vypršení se přešlo na defaultní příkaz, který byl „jeď rovně”.
Dobré, resp. lepší než předtím, ale … s novým přesnějším nástrojem se ukázalo, že „čínská hlava” na korekce občas vůbec nereaguje!? Pokud zatočíte volantem doprava a jen někdy na to stroj zareaguje opravdovým zatočením, tak to je skoro … těžké. Bylo by třeba posílat daný přikaz a rovnou nějak ověřovat, že je realizovaný. Asi vás nemusím 2x přesvědčovat, že to už je pak jednodušší si tam dát vlastní H-můstek a ty motory si prostě řídit sám.
Druhá limitace HW, tentokrát našeho, byl výkon. Pokud chcete nějak rozumně zpracovávat gyra, kompas a nedej bože akcelerometry, tak potřebujete vyšší vzorkovací frekvenci, ať můžete průměrovat/filtrovat. My se horkotěžko dostali na 8Hz.

Gyro vs. kompas

Jak jsem psal výše, verze 0 používala pouze kompas pro určení aktuálního směru lodi. Co je ale dobré mít na pozoru, že kompas je typicky hodně filtrovaný a má tedy relativně pomalou odezvu. Pokud se lodička točí jak blázen, tak získané měření z kompasu moc relevantní nebude … to jsem chytrý, co? … to víte, generál po boji.
Ve zkratce to byl jeden ze zdrojů kmitání … prostě lodička si myslela, že kouká špatným směrem, ale fakticky už měla jet rovně. Pro rychlé korekce úhlu je rozhodně lepší použít čistě integraci úhlové rychlosti z gyra.

Android aplikace

Android verzi uživatelské aplikace nám naprogramoval PetrS. Ano, klasicky se ukázalo, že i ta nejtriviálnější verze není jen tak a hned. A pak „drobnosti” typu: „vy určitě chcete být na internetu, ale ten přes lodičku nějak nefunguje, tak více to, já vás raději přepojím na jinou wifinu ...”
Výsledné řešení se mi svoji jednoduchostí libilo . Jelikož manualní řízení přes RC vysílačku stále fungovalo (v libovolný okamžik tak uživatel může převzít řízení automatu), tak na jednotlivé cílove body člověk najel ručně. V Android aplikaci pak pouze zmáčnul plus jako přidej bod. První a automaticky i poslední bod byl HOME, kam se lodička po skončení úkolu vrací.
Autonomní jízda byla pak podobně jako u přehrávání muziky: tlačítko Play, Pause, Stop.
Drobnou vadou na kráse byla nutnost kalibrace kompasu, ale tato akce byla schovaná v nastavení společně s před-výběrem WiFi spotu.

Závěr a „Matrix reloaded”?

Jednou z mých (skoro tajných) motivací v tomto projektu bylo vytvoření „černé krabičky”, kterou dáte do libovolné RC hračky a získáte tím autonomního robota se znalosti GPS pozice a WiFi komunikací. Ono se ale ukazuje, že zmíněný Futaba PPM protokol zas tak běžný v levných RC hračkách není, což je asi ale teprve první problém.
Teď bych šel Šimiho cestou, který si to celé vedle postavil znova, jen místo Arduina použil Raspberry Pi Zero W a to řízení si naprogramoval v Pythonu. Vše to jelo rovnou na lodi, tj. žádné problémy s výpadky paketů. Ano, je to o trošku dražší, ale jestli jsme to definitivně nevzdali (já myslím, že jo), tak toto je cesta (možná).

Appendix — Arduino digitalWrite()

Asi první zrada s Arduino programováním bylo debugování pomocí LEDky. Co jsem si z programování AVRka pamatoval, že dané makra, jako např. sei(), se přímo překládají na 1:1 asemblerovské instrukce. A zápis na port by měl proběhnout v jednom taktu. Toto ale rozhodně neplatí pro funkci digitalWrite() a volat tuto funkci v interruptu rozhodně není dobrý nápad.
Co se vám v podobném případě může hodit je znalost, jak generovat assembler výstup? Odpověď naleznete na Arduino fóru:
m:\arduino-nightly\hardware\tools\avr\bin\avr-objdump.exe 
Files > Preferences > verbose compilation
Zde je pro představu dekompilace celé funkce digitalWrite() (no vlastně není celá, protože v některých případech volá ještě další pod-funkce):
void digitalWrite(uint8_t pin, uint8_t val)
{
     99e:	0f 93       	push	r16
     9a0:	1f 93       	push	r17
     9a2:	cf 93       	push	r28
     9a4:	df 93       	push	r29
     9a6:	1f 92       	push	r1
     9a8:	cd b7       	in	r28, 0x3d	; 61
     9aa:	de b7       	in	r29, 0x3e	; 62
	uint8_t timer = digitalPinToTimer(pin);
     9ac:	28 2f       	mov	r18, r24
     9ae:	30 e0       	ldi	r19, 0x00	; 0
     9b0:	f9 01       	movw	r30, r18
     9b2:	e8 59       	subi	r30, 0x98	; 152
     9b4:	ff 4f       	sbci	r31, 0xFF	; 255
     9b6:	84 91       	lpm	r24, Z
	uint8_t bit = digitalPinToBitMask(pin);
     9b8:	f9 01       	movw	r30, r18
     9ba:	e4 58       	subi	r30, 0x84	; 132
     9bc:	ff 4f       	sbci	r31, 0xFF	; 255
     9be:	14 91       	lpm	r17, Z
	uint8_t port = digitalPinToPort(pin);
     9c0:	f9 01       	movw	r30, r18
     9c2:	e0 57       	subi	r30, 0x70	; 112
     9c4:	ff 4f       	sbci	r31, 0xFF	; 255
     9c6:	04 91       	lpm	r16, Z
	volatile uint8_t *out;

	if (port == NOT_A_PIN) return;
     9c8:	00 23       	and	r16, r16
     9ca:	c9 f0       	breq	.+50     	; 0x9fe 

	// If the pin that support PWM output, we need to turn it off
	// before doing a digital write.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);
     9cc:	88 23       	and	r24, r24
     9ce:	21 f0       	breq	.+8      	; 0x9d8 
     9d0:	69 83       	std	Y+1, r22	; 0x01
     9d2:	0e 94 6d 04 	call	0x8da	; 0x8da 
     9d6:	69 81       	ldd	r22, Y+1	; 0x01

	out = portOutputRegister(port);
     9d8:	e0 2f       	mov	r30, r16
     9da:	f0 e0       	ldi	r31, 0x00	; 0
     9dc:	ee 0f       	add	r30, r30
     9de:	ff 1f       	adc	r31, r31
     9e0:	ec 55       	subi	r30, 0x5C	; 92
     9e2:	ff 4f       	sbci	r31, 0xFF	; 255
     9e4:	a5 91       	lpm	r26, Z+
     9e6:	b4 91       	lpm	r27, Z

	uint8_t oldSREG = SREG;
     9e8:	9f b7       	in	r25, 0x3f	; 63
	cli();
     9ea:	f8 94       	cli

	if (val == LOW) {
		*out &= ~bit;
     9ec:	8c 91       	ld	r24, X
	out = portOutputRegister(port);

	uint8_t oldSREG = SREG;
	cli();

	if (val == LOW) {
     9ee:	61 11       	cpse	r22, r1
     9f0:	03 c0       	rjmp	.+6      	; 0x9f8 
		*out &= ~bit;
     9f2:	10 95       	com	r17
     9f4:	81 23       	and	r24, r17
     9f6:	01 c0       	rjmp	.+2      	; 0x9fa 
	} else {
		*out |= bit;
     9f8:	81 2b       	or	r24, r17
     9fa:	8c 93       	st	X, r24
	}

	SREG = oldSREG;
     9fc:	9f bf       	out	0x3f, r25	; 63
}
     9fe:	0f 90       	pop	r0
     a00:	df 91       	pop	r29
     a02:	cf 91       	pop	r28
     a04:	1f 91       	pop	r17
     a06:	0f 91       	pop	r16
     a08:	08 95       	ret

00000a0a <_ZN6StringD1Ev>:
	*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
Pokud ale potřebujete rychlou verzi (tak, jak jsem to naivně předpokládal), tak je třeba použít přímo bitovou konstrukci:
ARDUINO_LED_PORT |= ARDUINO_LED_BIT; // faster digitalWrite(ARDUINO_LED, HIGH);
ARDUINO_LED_PORT &= ~ARDUINO_LED_BIT; // faster digitalWrite(ARDUINO_LED, LOW);


Blog / Marina 2.0

15. srpen 2018 — Marina 2.0

Šimi to nechce vzdát. Je to marný, je to marný, je to marný. Jsem teď na krátké dovolené u Lipenského jezera a to je přeci ideální příležitost pro testovaní nové konfigurace, není-liž pravda? No asi jsem to měl spíše nechat doma …
Včera jsem chtěl psát, proč to musí být vždy tak komplikované — proč nestačí to jenom pustit a jede to? Je tu dost omezený internet a lodička není přepnuta do Access Point režimu. Ale mám adaptér na micro SD kartu a dokonce jednu náhradní … takže nějaká cesta existuje, snad.
První pozorování bylo, že když jsem SD kartu dal do Win7 počítače, tak jednak ji chtěl hned opravovat (to jsem mu raději zakázal), ale pak jediné co bylo čitelné byl nějaký boot disk. Karta má 2GB, ale ten boot disk má pouze 42MB?? Sebral jsem manželce USB kabel na telefon a postupoval podle článku Connect To A Raspberry Pi Zero With A USB Cable And SSH (současně jsem koukal do desertbot.io SSH into Pi Zero over USB). Vlastně to vypadá jednoduše:
  • USB kabel zapojit do „prostředního” konektoru s názvem USB (tj. ignorovat krajní POW konektor)
  • na micro SD kartě vytvořit prázdný soubor se jménem ssh
  • editovat config.txt a přidat na konec souboru řádek dtoverlay=dwc2
  • editovat cmdline.txt a rozšířit příkazový řádek o modules-load=dwc2,g_ether (má tam být pouze jedna oddělující mezera a vše je na jediném příkazovém řádku)
Pak jsem USB zapojil do počítače a po chvíli se objevilo:
RNDIS/Ethernet Gadget - No driver found
RNDIS/Ethernet Gadget - No driver found
V článku How to install Microsoft RNDIS driver for Windows 7 to bylo celkem přímočaré a až na poslední varování, že driver nebude možná fungovat to prošlo.
V ipconfigu jsem viděl další vytvořené spojení:
Ethernet adapter Local Area Connection 3:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::24c1:bf2:aee1:ce8e%67
   Autoconfiguration IPv4 Address. . : 169.254.206.142
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
   Default Gateway . . . . . . . . . :
ale jakou adresu má RasPi? Ono to vlastně píšou i v tom článku … stačí se připojit pomocí ssh pi@raspberrypi.local:
RasPi konzole přes USB
RasPi konzole přes USB
Ještě jsem nezmínil jeden detail: po připojení ke zdroji napájení (přes USB) se objevila WiFi lodičky?! Po překonfigurování na Access Point WiFi od RasPi zmizela a další experimenty jsem odložil až na další reboot. No a to se pomalu dostáváme ke dnešku … zase vidím WiFi (možná je ze starého ESP8266, který je tam stále někde schovaný??) a zase se nemůžu připojit. Horší je, že se nemůžu připojit ani přes to USB :-(. Napadá mne pouze restart Windows …
Dnes asi žádné spojení nebude ...
Dnes asi žádné spojení nebude ...

16. srpen 2018 — WARNING Socket timeout!

Tak reboot Windows nepomohl ani reinstalace RNDIS/Ethernet Gadget driveru. Sigh. A tak jsem udělal další krok, za který bych ostatní v lepším případě peskoval v horším rovnou fackoval. Modifikoval jsem záložní micro SD kartu. A to stejným způsobem, co jsem popisoval včera, tj. zpřístupnění ssh konzole přes USB. Tentokrát jsem se ale vyvaroval jakýchkoliv zásahů do /etc/network/interfaces, který mohl být zdrojem problému …
Také psal Šimi: Na SD kartě jsou 2 partitions /boot je FAT32 (50MB) a velká /rootfs je ext4. Ale filesystem ext4 zase není vidět ve Win7 :-( Našel jsem, že snad číst i zapisovat Ext4 ve Win by měl umět http://www.ext2fsd.com/
Ne, ESP tam není, je tam nainstalováno hostapd, které se má starat o vytvoření AP, ale myslel jsem, ze je vypnuté, nebo se to chová tak, že když v módu station nenajde v nějakém timeoutu známý AP, tak zapne hostapd? Mě se to do AP nikdy nepřepnulo, ale zkoušel jsem to jen v dosahu našeho AP. Jestli se ti objeví v seznamu WiFi, tak to tak musí být. Dá se na něj přihlásit? Mělo by to být bez hesla. Jestli to nepůjde, tak ještě musí být správně nastavené /etc/dnsmasq.conf? Ale teď, když se do Rasp nedostaneš ani kabelem kvůli driveru, ani wifi kvůli nastavení, ani na SD kvůli filesystemu... Ani tady to bez boje nepůjde :-/ Držím palce.
Stav je teď ale trošku jiný. S druhou kartou se mi opět podařilo přihlásit přes USB kabel. Stejně tak se objevilo SSID lodičky. Znova jsem koukal do originálního /etc/network/interfaces a vše je tam zakomentované. Cvičně jsem i zkoušel přímo pustit kód python3 marina.py, který selhal na
pi@raspberrypi:~/marina $ python3 marina.py
Socket created
Traceback (most recent call last):
  File "marina.py", line 33, in __init__
    self.skt.bind((self.UDP_IP, self.UDP_PORT))
OSError: [Errno 98] Address already in use

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "marina.py", line 207, in 
    main()
  File "marina.py", line 183, in main
    net = Net(ppm_switcher)
  File "marina.py", line 34, in __init__
    except (socket.error, msg):
NameError: name 'msg' is not defined
… a on důvod byl, že marina.py se pouští automaticky. Z ps axuf vybírám:
pi@raspberrypi:~/marina $ ps axuf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
…
avahi      252  0.0  0.7   6388  3132 ?        Ss   21:35   0:00
 avahi-daemon: running [raspberrypi.local]
avahi      261  0.0  0.3   6388  1608 ?        S    21:35   0:00
  \_ avahi-daemon: chroot helper
root       320  0.1  0.6  10128  3060 ?        Ss   21:35   0:01
 wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwla
root       414  0.0  0.4   2920  1812 ?        Ss   21:35   0:00
 /sbin/dhcpcd -q -w
root       425 30.7  1.9  55412  8832 ?        Sl   21:35   5:30
 python3 /home/pi/marina/marina.py
root       474  0.0  0.5   6160  2624 ?        Ss   21:35   0:00
 /usr/sbin/hostapd -B -P /run/hostapd.pid /etc/hostapd/hostapd.con
dnsmasq    477  0.1  0.5   9068  2424 ?        S    21:35   0:01
 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -r /run/
…
Systém jsem shodil a znova zapnul, tentokrát ale přes PWR konektor a šlo se připojit na WiFi! … ano IP adresu jsem si pamatoval z minulého běhu. I to zalogovalo několik sekund „jízdy” všech senzorů. OK
Krabičku s RasPi, senzory a Arduinem jsem tedy zase sešrouboval, přepojil napájecí konektor z lodi a do třetice zapnul. Připojit přes WiFi šlo, ping fungoval, ale … řízení lodičky stávkuje. Nekomunikuje a jenom to hází:
m:\git\robolod\robolod\python>c:\Python27\python.exe boat.py 169.254.88.144 9999
 "WiFi only …  boat battery"
METALOG: logs/meta_180816_125343.log
Arduino RESET None
2018-08-16 12:53:45,012 WARNING Socket timeout!
ERR: 1
2018-08-16 12:53:47,026 WARNING Socket timeout!
2018-08-16 12:53:49,026 WARNING Socket timeout!
2018-08-16 12:53:51,028 WARNING Socket timeout!
2018-08-16 12:53:53,029 WARNING Socket timeout!
2018-08-16 12:53:55,030 WARNING Socket timeout!
…
a žádná data nechodí (ani po několika opakováních). Divné … asi mne čeká další rozborka-sborka''.

17. srpen 2018 — Test Ext2Fsd-0.69 + problém s napájením?

Zatím žádná sláva. Sice jsem si vytvořil teorii, čeho by mohl být WARNING Socket timeout! důsledkem: marina.py je náš kód, který nemusí být úplně na vše připravený a pokud nějak částečně spadne, tak může být následně problém se připojit. No jo, ale ono se to postupně ještě zhoršovalo. V první fázi jsem mohl Marinu přes WiFi spojení stále pingnout, ale již se k ní nešlo připojit pomocí ssh:
problém s ssh připojením
problém s ssh připojením
Nezbylo mi, než „roboloď” natvrdo vypnout. Ale po zapnutí už se neobjevila ani její WiFina :-(.
Dnes jsem řídicí krabičku zase rozdělal, odpojil napájení z lodi a zkusil nejprve připojeni (a napájení) přes USB a v druhém kroku pouze napájení přes POW konektor. Oboje fungovalo bez problémů! Tak mne jenom napadá, jestli loď nemá nějaký problém s napájením??

Ext2Fsd-0.69

Druhá řada neúspěchů byla na poli micro SD karty a Ext4 partition. Po přečtení úvodní stránky Ext2Fsd projektu, kde autor píše: Don't use Ext2Fsd 0.68 or earlier versions with latest Ubuntu or Debian systems. Ext2Fsd 0.68 cannot process EXT4 with 64-BIT mode enabled, then it could corrupt your data. Very sorry for this disaster issue, I'm working on an improvement. jsem byl „lehce” nervozní to vůbec instalovat, ale … moc času nezbývá, sigh.
Po vložení SD karty a puštění Ext2 Volume Manageru to vypadalo celkem dobře:
Ext2 Volume Manager
Ext2 Volume Manager
tj. u DISK 2 je vidět Linux Partition type. No jo, ale když jsem ji chtěl připojit nebo přiřadit disk, tak celý program spadnul :-(
Mount Linux partition
Mount Linux partition
Pád programu
Pád programu
No nic. Je možné, že ta karta je opravdu nějak poničená a proto si ji nemůžu přečíst, ale … vlastně i s tou druhou kartou lodička naběhla a byla vidět její WiFina (jen jsem ji přestal používat, protože se nedalo připojit přes USB a vlastně asi ani přes tu WiFi).
Zbývá necelých 24h … začínám být pomalu pesimistický …

19. srpen 2018 — Prohra - skóre 0:1

Tak Marina 2.0 nakonec opouštěla Lipno, aniž by si zaplavala :(. Ještě jsem ověřoval variantu, že to není „duchařina”, tj. jestli náhodou neruší RC vysílačka WiFinu, ale vypadá to, že ne — po odpojení konektoru a napájení přes POW konektor s běžící lodičkou vše fungovalo OK. Předpokládám rušení na napájení??
Šimi teď přepnul na prioritní dlouhodobý projekt „Ondra”, ale snad do září připraví mini-revizi HW. Ještě info: Marina.py se spouští v souboru /etc/rc.local., ale jestli se výstupy automaticky logují netuším??
raspi-gpio set 14 ip
python3 /home/pi/marina/marina.py &
iptables-restore < /etc/iptables.ipv4.nat
Předpokládám, že první řádek je „tajuplný hack” s GPS I/O, ale jistý si nejsem. Stejně tak proč je třeba nastavovat iptables??
Nasucho si asi můžu hrát s upgrade řídícího boat.py a testovat logování UDP packetů na OSGARovi.

25. srpen 2018 — OSGAR UDP Logging

To byla zase jednou výživná lekce :-(. Dnes jsem se pokoušel rozchodit logování UDP pomocí OSGAR knihovny. A nefungovalo to. Raspberry mám připojené přes USB, tj. spojení přes kabel. Starý boat.py kód funguje, nový logsocket.py nikoliv. Prošel jsem snad všechny rozdíly v kódu a nic …
A pak mi došel jeden detail — starý kód pouštím pod Python2, ale nový již běží v Python3, že by tam něco změnili?! Upravil jsem si marina.py na lodičce, aby vypisovala debug hlášky a v obou případech přijde první paket a to ze stejné IP adresy a portu. Následně lodička začne chrlit tuny zpráv o stavu senzoru. Pak už jsem to nevydržel a minimalistickou verzi zkusil rovnou v konzoli:
M:\git\osgar>python
Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> soc.bind(('', 9999))
>>> soc.recvfrom(1024)
^C


m:\git\robolod\robolod\python>c:\Python27\python.exe
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> soc.bind(('', 9999))
>>> soc.recvfrom(1024)
('93191615643CHV,1000,1001,1000,1000,A*31\r\n', ('169.254.91.207', 9999))
>>> soc.recvfrom(1024)
('93201615731MAG,-139,-364,481,252.00*67\r\n', ('169.254.91.207', 9999))
A důvod, proč Python 2.7 jede, ale Python 3.6 nikoliv? No jak se dostanete až na dno problému, tak je to vlastně jednoduché …
Přes Firewall for dummies jsem se dostal k obrázku výše a potvrdil konečnou domněnku — pro starší Python jsem měl porty otevřené, ale pro nový nikoliv. Sigh.
M:\git\osgar>python
Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> soc.bind(('', 9999))
>>> soc.recvfrom(1024)
(b'40511175634CHV,1000,1022,1000,1000,A*30\r\n', ('169.254.198.186', 9999))
>>> soc.recvfrom(1024)
(b'40521175718MAG,-221,-458,425,248.00*60\r\n', ('169.254.198.186', 9999))
Tak teď už jenom uklidit ty hromady hnoje, co jsem tam tam přidal, abych zjistil rozdíl, a připravit pull request na logování UDP. Ano, teď už to jede … viz boat-180825_182317.log.

30. srpen 2018 — Powerbanka

Dobrá zpráva — lodička jede a lze se na ní připojit. Provizorní řešení je trošku obskurní, ale … účel světí prostředky. Vypojil jsem 5V napájecí pin a půjčil si od Pavla z Short Circuits Prague powerbanku (resp. půjčil mi rovnou dvě ).
První pokus se sice WiFi lodičky objevilo, ale lodička neposílala žádná data. Proč? Program vůbec neběžel a když jsem svoji debug verzi pustil ručně, tak
pi@raspberrypi:~/marina $ python3 md_marina.py
Socket created
Traceback (most recent call last):
  File "md_marina.py", line 208, in 
    main()
  File "md_marina.py", line 188, in main
    sensors = Sensors()
  File "md_marina.py", line 115, in __init__
    self.hmc5883l = i2c_hmc5883l.i2c_hmc5883l(1)
  File "/home/pi/marina/i2c_hmc5883l.py", line 36, in __init__
    self.setScale(gauss)
  File "/home/pi/marina/i2c_hmc5883l.py", line 85, in setScale
    self.setOption(self.ConfigurationRegisterB, self.scale_reg)
  File "/home/pi/marina/i2c_hmc5883l.py", line 96, in setOption
    self.bus.write_byte_data(self.addr, register, options)
OSError: [Errno 121] Remote I/O error
… tj. kompas neodpovídá a položilo to hlavní program. A neodpovídá, protože nebyl napájený … zatím OK. Zapnul jsem tedy napájení lodičky a vše jede, jen data z GPS zatím moc zajímavá nejsou:
$GPRMC,,V,,,,,,,,,,N*53
$GPRMC,192355.80,V,,,,,,,300818,,,N*7E
ale jsem pod střechou a signál je tu slabý (i když se mi tu dříve GPS již mnohokrát chytla). No jestli jsem to nezakřikl — teď mi jede jenom GPS, která je připojena přes RS232. Ostatní senzory a Arduino na I2C se odmlčely.
p.s. vlastně i ta teorie s Remote I/O error je možná pochybná! Nepustil jsem to jako sudo, ale možná to není nutné??
Další chybka:
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.5/threading.py", line 1180, in run
    self.function(*self.args, self.kwargs)
  File "/home/pi/marina/scheduler.py", line 16, in _run
    self.function(*self.args, self.kwargs)
  File "md_marina.py", line 191, in states_update
    sensors.update()
  File "md_marina.py", line 143, in update
    self.mag = self.hmc5883l.getAxes() + self.hmc5883l.getHeading()
  File "/home/pi/marina/i2c_hmc5883l.py", line 123, in getHeading
    headingRad = math.atan2(scaled_y, scaled_x)
TypeError: a float is required
Přidal jsem si další debug výpis pro (scaled_x, scaled_y, scaled_z) a -235.52 None -197.8 nevypadá dobře … TODO (tady by se opravdu hodilo logování I2C rovnou na lodičce).

3. září 2018 — SMBus a I2CLibraries

Koukám teď na chybu, co jsem reportoval posledně, a ona je vlastně v původní knihovně. Šimi nakonec volil SMBus, což není sado-maso, ale The System Management Bus postavený na I2C. V čem se liší zatím nevím (mám offline stránku i2c-bus.org, kde mluví o kontrole paketů (PEC), timeout pro přenosy, standardizované typy, max. bitrate 100kHz/s … ale to bych jenom papouškoval, aniž bych tomu zatím moc rozuměl).
I2CLibraries je pak už Python knihovna pro práci s akcelerometrem i2c_adxl345.py, gyrem i2c_itg3205.py a kompasem i2c_hmc5883l.py. Je zvláštní, že původní autor přešel od SMBusu k I2C knihovně, ale zase neznám detaily. My jdeme opačným směrem.
Tak zpět k té chybě — z None se špatně počítá úhel, OK. Knihovna nahrazuje hodnotu -4096 za None, proč? V HMC5883L.pdf píší: In the event the ADC reading overflows or underflows for the given channel, or if there is a math overflow during the bias measurement, this data register will contain the value -4096. This register value will clear when after the next valid measurement is made. OK, takže -4096 reprezentuje nevalidní hodnotu přetečení nebo podtečení …
… pak by i heading měl být nevalidní. Ale také všichni uživatelé musí být připravení, že možná dostanou None. Uvidíme. Teď nastává ten okamžik, kdy plně využívám přehrávání z logu 1:1 … stejný kód pustím na notebooku, stejně mi spadne, opravím, spadne to nejspíše o patro výše nebo na nějakém překlepu atd. dokud to neprojde. Ale chyba lávky — tady logování aktivity na SMBusu nemám :-(. Unittesty pro rozšíření také nejsou (teď se odvolávám na zdrojovou knihovnu I2CLibraries). Zbyněk by určitě řekl, tak si ten test napiš sám a měl by pravdu …
Hmm, třída pojmenovaná i2c_hmc5883l, nevěřil bych, že mne PEP8 tak nakazí. No zatím si mne ten wrapper pro kompas moc nezískal. Když jsem udělal MagicMock na všechno možné:
class HMC5883lTest(unittest.TestCase):
    def test_overflow(self):
        with patch('i2c_hmc5883l.smbus') as mock:
            hmc = i2c_hmc5883l(port=1)
            hmc.bus = MagicMock()
            hmc.bus.read_i2c_block_data = MagicMock(return_value=b'001122')
            hmc.getHeading()
tak to padlo na
M:\git\robolod\robolod\raspberry\python>python -m unittest
E
==============
ERROR: test_overflow (test_i2c_hmc5883l.HMC5883lTest)
--------------
Traceback (most recent call last):
  File "M:\git\robolod\robolod\raspberry\python\test_i2c_hmc5883l.py", line 15,
in test_overflow
    hmc.getHeading()
  File "M:\git\robolod\robolod\raspberry\python\i2c_hmc5883l.py", line 124, in g
etHeading
    headingRad += self.declination
AttributeError: 'i2c_hmc5883l' object has no attribute 'declination'

--------------
Ran 1 test in 0.018s

FAILED (errors=1)
… přemýšlím, kdo ty moje „nářky” bude číst. Možná Šimi, ale nejspíše tím „zahluším” původní článek :-( … komentáře vítány. Prostě je třeba nejprve zavolat setDeclination() než použiji getHeading(). Sigh. Nedivím se, že se do unittestů netestovaných tříd nikdo nehrne …
Tak ještě jednou jsem si „natloukl” aneb testy mají smysl v okamžiku, když očekáváte jiný výsledek. Tento test by měl přeci již selhat, není-liž pravda?!
data = struct.pack('>3h', 10, -4096, 30)
hmc.bus.read_i2c_block_data = MagicMock(return_value=data)
hmc.getHeading()
Neselhal. Proč? Někdo tam, určitě v dobré víře, prohodil Y a Z osu:
(magno_x, magno_z, magno_y) = struct.unpack('>3h', bytes(b))
(toto je již střelba do vlastních řad)
==============
ERROR: test_overflow (test_i2c_hmc5883l.HMC5883lTest)
--------------
Traceback (most recent call last):
  File "M:\git\robolod\robolod\raspberry\python\test_i2c_hmc5883l.py", line 24,
in test_overflow
    hmc.getHeading()
  File "M:\git\robolod\robolod\raspberry\python\i2c_hmc5883l.py", line 124, in g
etHeading
    headingRad = math.atan2(scaled_y, scaled_x)
TypeError: must be real number, not NoneType

--------------
Ran 1 test in 0.003s

FAILED (errors=1)
Konečně! Asi je na čase to opravit a zatočit s lodičkou, jak je to tedy s těma osama … pokračování příště.

4. září 2018 — SMBus review

V rámci včerejšího syncu jsem si stáhl System Management Bus (SMBus) Specification 3.0. Jednak bych se o tom rád dozvěděl více než „štěky” typu The System Management Bus (SMBus) is more or less a derivative of the I2C bus. (src) nebo SMBus (System Management Bus) is a subset from the I2C protocol (src) a pak jsem zvědavý.
Jako programátor se až tak netrápím rozdíly elektrické specifikace (zatím), ale zajímá mne hlavně komunikační protokol a rozdíly vůči I2C, čemuž je věnován Appendix B. U I2C není žádná „minimální komunikační rychlost” — pokud master nebo slave uzemní SCL linku, tak je vše blokované. U SMBusu je vyžadována minimální frekvence 10kHz s různými časovými limity. Ty předpokládám všechny tři senzory (kompas, gyro a akcelerometr) splňují a Arduino s HW řešením I2C je snad také OK, ale raději časem ověřím.
Co se mi nelíbilo, ale teď se to vyjasnilo, bylo naše použití read_i2c_block_data() se specifikací počtu bajtů ke čtení. SMBus specifikace totiž popisuje Block Read, kde slave v prvním bajtu odpovědi říká, kolik bajtů má master přečíst. Ale třeba u kompasu HMC5883 píšou:
To minimize the communication between the master and this device, the address pointer updated automatically without master intervention. The register pointer will be incremented by 1 automatically after the current register has been read successfully.
To move the address pointer to a random register location, first issue a “write” to that register location with no data byte following the commend. For example, to move the address pointer to register 10, send 0x3C 0x0A.
Tj. žádné extra bajty o délce bloku! A rozuzlení? Ono i v tom SMBus Python bindingu je ještě druhá funkce read_block_data(), která délku jako parametr nedostává, takže snad OK.
Rozmyslel jsem si i svůj další plán. Lodička musí být nakonec plně autonomní, tj. chci logovat vše co se tam děje. Plánuji tedy udělat OSGAR I2C wrapper, takže se mi budete moci smát v přímém přenosu … . A OSGAR tak začne podporovat i levné senzory na I2C, což se může časem hodit.
p.s. Šimi píše, že Y a Z jsou prohozené jak v původních I2CLibraries, tak v HMC5883L specifikaci … ve zkratce je to tak správně, i když za mne ne úplně očekávané a jen to dokazuje, že neumím číst … možná je to kompatibilita 2D a 3D kompasů?

5. září 2018 — OSGAR Marina 2.1

Nemůžu spát. Měl jsem přeci jenom poznámky sepsat ještě včera večer a nenechávat to až na ráno, no nic …
Začal jsem do feature/marina2-i2c větve rozcházet Marinu s OSGAR knihovnou. A vlastně to byla po delší době zase radost — neřeším jen lodičku, ale potenciálně i X dalších robotů. Klasicky to nebylo bez problémů. Na lodičce není git, ale čert to vem, kopíruji to tam jak za starých časů. Není tam ani screen, což mne v případě vypadávajícího spojení bude na vodě určitě mrzet. A pak tam nejsou Pythonovské balíčky jako numpy nebo msgpack, který používáme pro serializaci zpráv u OSGARa. Podle všeho tam ale není ani pip (píp) a setuptools … a v té chvíli jsem to chtěl vzdát.
Vyhození numpy bylo celkem jednoduché — byl to nějaký zbytkový kód v gps.py, který si zaslouží promazat. Horší to bylo s msgpack. Dobrá zpráva je, že se používá jenom na jednom místě a to serialize.py. Hack byl brutální, ale není čas „řešit třísky” …
Další dílek do skládanky byly specifikace jednotlivých senzorů a návrh „protokolu”. Naštěstí to mají všechny tři senzory stejné a podobně je i přiohnutý Arduino kód. Pro zápis je první bajt číslo registru a následují data pro zapsání. U čtení se nejprve pošle číslo registru ve write režimu a pak master přepne do read a čte kolik chce bajtů. V kódu to nevypadá 2x krásně, ale tento výsek specifikuje čtení kompasu, gyra i akcelerometru:
self.i2c_loop = [
                [0x1E, 'R', 0x03, 6],  # compass HMC5883L
                [0x68, 'R', 0x1B, 8],  # gyro ITG-3205
                [0x53, 'R', 0x32, 6],  # acc ADXL345
         ]
Je to tedy I2C adresa, 'R'/'W', číslo registru a počet bajtu k přečtení resp. pole bajtu pro zápis. Až mne zaskočilo, když to hned na poprvé fungovalo! Čtení všech senzorů na I2C vycházel na 40Hz update, což je pěkné (a po pravdě si nejsem teď jistý, jaká rychlost I2C je momentálně nastavená).
Přidal jsem vyčítání jednotlivých RC kanálů z Arduina a pak už se to začalo chovat „podle očekávání”:
pi@raspberrypi:~/osgar $ python3 osgar/boat.py run config/test-i2c.json
–note "serialized by str(), test new OSGAR marina.py, read Arduino channel 0"
Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/home/pi/osgar/osgar/drivers/logi2c.py", line 39, in run
    received = self.com.read_i2c_block_data(addr, reg, len_or_data)
OSError: [Errno 121] Remote I/O error
Pak ještě asi 4x a ve všech případech (s logy je to celkem triviální záležitost) to bylo čtení gyra:
0:00:06.504589 1 [30, 'R', 3, 6]
0:00:06.505638 1 [104, 'R', 27, 8]
0:00:06.506624 1 [83, 'R', 50, 6]
0:00:06.507602 1 [8, 'R', 0, 2]
0:00:06.513724 3 [30, 3, [2, 249, 4, 251, 251, 219]]
0:00:07.148480 2 [30, 'R', 3, 6]
0:00:08.150481 2 [30, 'R', 3, 6]
0:00:09.152503 2 [30, 'R', 3, 6]
0:00:10.154548 2 [30, 'R', 3, 6]
… toto je výpis z logu, kde první je čas v sekundách, číslo uloženého kanálu (zde je 1 zápis na I2C, 2 je aplikační cyklus a 3 je čtení z I2C) a na 104 už nedošlo. Předpokládám, že je to tím, že z gyra čtu nejvíce bajtů v jednom bloku (8), tak to asi zkusím rozdělit na dva dotazy: teplota a jednotlivé osy. Nebo to spíše použiji jako testovací příklad na zotavení z chyby. A také bych ověřil tu rychlost a nastavil ji na 100kbps, pokud není.
Jak jsem říkal, že teď už neřeším jenom lodičku, ale i jiné roboty, tak jsem např. narazil na chybu, resp. neočekávané chování, kdy pořadí kanálů se měnilo mezi jednotlivými běhy:
0:00:00.018215 0 b"{'names': ['app.raw', 'boat.cmd', 'i2c.i2c']}"
vs.
0:00:00.018767 0 b"{'names': ['i2c.i2c', 'app.raw', 'boat.cmd']}"
Správně bych k nim měl přistupovat přes jména, ale myslel jsem, že se to nejprve vše nakonfiguruje a až v druhém kroku nastartuje, ale … další TODO k ověření.
Ještě pozorování, že po vyčtení všech šesti Arduino kanálů (opravdu má ta vysílačka tolik kanálů??) a stavu, zda je či není vysílačka zapnutá klesla frekvence na 16.6Hz … ale zatím bych to neřešil. Chybí ještě posílání příkazů na motory, tj. ještě se to o něco zpomalí.
Konečně jsem přilepil i konfiguraci GPS driveru (pár řádek v konfiguraci), ale pod střechou se mi nechytá (to dobou venku celkem lilo, takže test jsem odložil).
0:00:00.028329 0 b"{'names': ['i2c.i2c', 'boat.cmd', 'gps_serial.raw', 
                    'gps.position', 'gps.rel_position', 'app.raw']}"
…
0:00:00.330516 3 b'GPGGA,,,,,,0,00,99.'
…
0:00:00.356955 3 b'99,,,,,,*48\r\n'
0:00:00.359239 4 [None, None]
Kostelní hodiny odbily 4am … možná vhodný okamžik na změnu modu …
p.s. teď jsem ve fázi sběru dat a funkční autonomní celek je ještě daleko. Uvědomuji si, co práce musel Šimi investovat na rozchození celé lodičky „postaru”. Pokud to vyjde, tak vyrazím na vodu nasbírat alespoň nějaká data v manuálním režimu.

6. září 2018 — Vodní test

Dnes večer jsem vyrazil k vodě. Zbývá poslední den možná dva a tak mi lehce „teče do bot”. Naštěstí ale neteče do lodičky. To bylo první pozorování, kdy zadek se dost naklonil a voda se dostala do komory pro krmení. Ale v prostorách s elektronikou naštěstí zůstalo sucho.
Udělal jsem tři testy a všechny dopadly stejně: lodička se zuřivě točila 10s vlevo a hlásila, že je cca 20 metrů od cíle. Tipoval bych to na problém s kalibrací kompasu, ale to uvidíme …
Pokud se podíváte na poslední commity, tak uvidíte celkem nepěkné „zářezy”, se kterými bych se moc nechlubil. Zkopíroval jsem experimentální verzi ze starého captain.py, upravil print pro Python3, line.py a route.py přesunul do osgar/lib a rozšířil konfiguraci, aby řídící aplikace dostávala pozici od GPS a směr lodi z kompasu.
Výpadky I2C při komunikaci s gyrem jsem zatím ošetřil jedním odchycením výjimky OSError, zapsání do logu a jedno zopakovaní čtení. V logu to pak vypadá:
0:00:04.042995 5 [30, 3, [2, 149, 5, 117, 249, 173]]
0:00:04.050689 0 b'[Errno 121] Remote I/O error'
0:00:04.058167 5 [104, 27, [198, 96, 255, 250, 255, 164, 255, 253]]
0:00:04.063915 5 [83, 50, [247, 255, 1, 0, 225, 255]]
takže snad dobré.
Z nasbíraných dat zatím dekóduji pouze kompas. Kalibraci jsem udělal první na stole (zatím hardcoded) a po dvou otáčkách lodičky ve vzduchu vypadala celkem rozumně:
Upravil jsem také řízení motorů — zatím i horní vrstva používá šířky pulzu, kde 1000 odpovídá středové poloze. Původní kód bral v úvahu aktuální úhlovou rychlost lodičky, což momentálně chybí.
Z nepříjemností zatím vadí posílání float hodnoty (podle očekávání).
M:\git\osgar>python osgar\replay.py d:\md\osgar\logs\boat\boat-180906_174543.log
 –module boat
…
Exception in thread Thread-1:
Traceback (most recent call last):
  File "D:\WinPython-64bit-3.6.2.0Qt5\python-3.6.2.amd64\lib\threading.py", line
 916, in _bootstrap_inner
    self.run()
  File "M:\git\osgar\osgar\drivers\marina.py", line 53, in run
    self.bus.publish('heading', heading)
  File "M:\git\osgar\osgar\bus.py", line 72, in publish
    assert data == ref_data, (data, ref_data)
AssertionError: (0.42291981964892333, 0.4229198196489234)
Tj. pokud ten float zapíšete pomocí str() a pak přečtete, tak přijdete o rozlišení (zde zcela zbytečné). Byl bych zvědav, zda msgpack by toto řešil?? V plánu bylo v OSGARovi používat obecně zlomková čísla, co jsou běžná např. v autech na CAN sběrnici.
Tak proč Marina na vodě tak tancovala? Výpis proměnné heading pro poslední log naznačuje, že to není dobré :-( … co obrázek? A jaj, jaj … výpis x, y, z:
422	-4096	1350
422	-4096	1355
424	-4096	1354
418	-4096	1359
425	-4096	1349
422	-4096	1354
…
Je vám to nějaké povědomé? … tak jo, je třeba snížit citlivost kompasu.

7. září 2018 — Vodní test 2

Včera večer mne pobavil Šimi: Takhle to vypadá, jako bys hrál hru "kdybyste si mohli vzít na pustý ostrov tři pythonovské knihovny, které by to byly?" … tak alespoň jednoho čtenáře mám
Před dnešním testem jsem opravil (snad) orientaci kompasu, posílám si úhlovou rychlost (i když se ještě nepoužívá), ale na vodě to vypadalo dost jako včera :-(. Lodička občas pár metrů popojela, ale skončila snad pokaždé v zuřivém točení na místě. Důvod? Podle všeho manuální kalibrace kompasu, viz obrázky níže (uznávám, že by bylo vhodnější je dát přes sebe … prostě střed cx, cy = 900, -1300 z prvního obrázku je na druhém mimo „mísu” resp. elipsu).
Ve staré verzi bylo vždy nutné nejprve provést kalibraci na vodě … je na čase to tam zase zapojit, sigh.
Znova koukám na ty obrázky a ten střed je mimo jak v x-ové tak y-ové souřadnici … možná to tam ručně (červené kolečko) dokreslím, ať je to zřejmé.
Jinak asi nic extra zajímavého. Powerbanka svítí už pouze třema LEDkama — na to kolik hodin byla lodička zapnutá (zatím jedu celý týden bez nabíjení) myslím dost dobré … jsem zvědavý, jak moc je pokles lineární, ale asi bych to neměl riskovat.
p.s. přemýšlím nad dalším krokem (teď je tma, navíc venku prší, tj. další test až zítra ráno), který by nebyl těžký a snad vedl ke kýženému výsledku. Přidal jsem do JSON konfigurace offsety kompasu — tam to patří, s každým logem se to ukládá a na tom základě pak loď naviguje. Nemělo by se mi tedy již stávat:
M:\git\osgar>python osgar\replay.py –module boat d:\md\osgar\logs\boat\boat-180
907_110047.log
…
Exception in thread Thread-1:
Traceback (most recent call last):
  File "D:\WinPython-64bit-3.6.2.0Qt5\python-3.6.2.amd64\lib\threading.py", line
 916, in _bootstrap_inner
    self.run()
  File "M:\git\osgar\osgar\drivers\marina.py", line 65, in run
    self.bus.publish('heading', heading)
  File "M:\git\osgar\osgar\bus.py", line 72, in publish
    assert data == ref_data, (data, ref_data)
AssertionError: (-2625, 9893)
… po změně kalibrace, když chci přehrát starší log (pokud chci, tak i na starší log můžu vynutit jinou konfiguraci). Mimochodem změnil jsem ty float na setiny stupně v int — bez 100% jistoty přehrávání logu jsem úplně ztracený!
OK, tak jsem přidal ještě jednoduchý nástroj na parsování I2C logů s výpisem doporučené kalibrace (střed (min+max)/2).
M:\git\osgar>python osgar\compass_calib.py d:\md\osgar\logs\boat\boat-180907_110047.log
(924, -1263, [731, 1117, -1483, -1042])

M:\git\osgar>python osgar\compass_calib.py d:\md\osgar\logs\boat\boat-180907_115152.log
(658, -1591, [444, 872, -1823, -1358])

8. září 2018 — Vodní test 3

Uznávám, že vymýšlením titulků jsem se poslední dny moc nevěnoval … no prostě dnes byl třetí a nejspíš i poslední test na jezeře. Přidal jsem si parametr duration a tak z příkazové řádky mohu říci jak dlouho se má lodička snažit něco dělat. Nastavil jsem to na 10s, přepnul vysílačku do manuálního režimu, páčkou uvedl lodičku do zuřivého otáčení na místě a nasbíraná data rovnou „na lodi” analyzoval:
pi@raspberrypi:~/osgar $ python3 osgar/compass_calib.py boat-180908_093323.log
(659, -1571, [443, 875, -1811, -1331])
Pak jsem pustil lodičku v normálním režimu a ona jela na břeh. Další pokus znova atd. Kód jsem upravil, aby vedle konfigurace kompasu a gyra šly snadno prohazovat kontrolní body (waypoints), vybral zase nějaké 2 roky (?) staré a jelo to pěkně. Dnes mírně fouká západní vítr, takže na datech z gyra asi budou v 3D vidět i vlnky … možná.
V závěrečné fázi jsem ještě zakomentoval korekce s úhlovou rychlostí a kupodivu to nebylo tak zlé. Zase 3x opakování, kde cílový waypoint je na „útesech” … nejspíše je hladina Lipenského jezera o dost níž než v roce 2016??
A co dál? No, žena začala balit věci, tak bych se asi měl věnovat jiným záležitostem :-(. Chtěl jsem ještě párkrát lodičkou objet přístavní molo, nasbírat GPS data a definovat nové testovací body, ale … možná až příště, bude-li.
Skóre druhého výletu bych asi označil remízou 1:1 — lodička nakonec sama autonomně jela, ale vlastně stejným kódem jako před lety (a se stejnými problémy). Nově má plnou autonomii a vyrovná se s výpadky WiFi sítě. Data ze senzorů sbírá rychleji a pravidelněji, takže je otevřen prostor pro lepší navigaci!