czwartek, 7 lipca 2011

Zaległości

Już jakiś czas temu wypowiedziałem się publicznie, na forum warsztatu, że podjęcie pracy na Politechnice uważam za jeden z największych błędów w życiu. Na szczęście jest to błąd w pełni odwracalny. No i stało się, zmieniłem pracę. Od maja pracuję dla łódzkiego oddziału Mobica Ltd.

Ze zmiany jestem bardzo zadowolony. Niestety firma musi chronić swoich klientów i jestem zobowiązany do tajemnicy. Także źródło ciekawostek do notatek mi się skończyło.

Końcówka pracy dla uczelni to był prawdziwy sajgon. Robot musiał zostać przygotowany do "prób poligonowych". Próby te odbyły się na przełomie kwietnia i maja na poligonie wojskowym z kompletnie świeżym robotem. W zasadzie wszystko nam się udało przygotować, na miejscu robot został jedynie złożony do kupy.
Robot dostał świeżutkie akumulatory i jeździł jak nigdy wcześniej. Wytrzymał jakąś godzinę, a potem spaliła mu się część silników napędowych!
Generalnie cała "operacja poligon" została uznana za sukces. Osobiście jestem szczególnie zadowolony ze sterowania robotem za pomocą systemu GPS. Całe oprogramowanie zostało przygotowane na ostatnią chwilę. Nie było nawet okazji przetestować czy działa. Zadziałało i to jak. System GPS jest bardzo precyzyjny. Robot bez problemów był w stanie gonić człowieka z nadajnikiem. Nawet przez moment się nie zgubił.

Koniec pracy dla uczelni oznaczał również konieczność przygotowania "raportu końcowego". Co to znaczy? Przygotowanie dokumentacji i raportów do wszystkich zadań z całego półtorarocznego okresu pracy dla Politechniki. Nagle im się k...a przypomniało, że będą potrzebować dokumentacji! Przez półtora roku nawet się nie zająknęli. Przygotowanie tego raportu zajęło mi mniej więcej półtora miesiąca. Jakimś cudem udało mi się odtworzyć historię zadań, także raport w sumie wyszedł mi całkiem rzetelny.

Kilka słów na temat pobytu na terenie poligonu. Super! Prześliczny teren. Sporo zwierzaków. Sprzęt wojskowy :)



Wciąż pracuję nad własnym silnikiem. Jak tylko mam wolną chwilę staram się coś napisać. Niestety z czasem kiepsko ... żona.

środa, 16 lutego 2011

EasyCap 4 Channel Usb DVR

Aktualnie pracuję nad tym jak zrobić PIP z kilku kamer. EasyCap 4 Channel Usb DVR to frame grabber, którego używamy do przechwytywania obrazu z kamer analogowych. Ma tą ważną cechę, że dzięki sterownikowi EasyCAP DC60 Driver działa z systemem Linux, co chyba nie jest takie oczywiste w tej kategorii sprzętu. Testowaliśmy jedno konkurencyjne urządzenie i nie udało się go uruchomić.
Frame grabber ma 4 kanały video in. Możliwe jest jednak zbieranie tylko z jednego kanału równocześnie. Zmianę aktualnego kanału można dokonać za pomocą biblioteki Video for Linux 2 - v4l2. Przełączenie kanału jest operacją dość czasochłonną i powoduje, że obraz pobrany tuż po niej zawiera śmieci.

Jak zrobić Pip z takim sprzętem? Użyć kilka frame grabber'ów ;)



Obrazy pobieramy biblioteką OpenCV, funkcje:
cvCaptureFromCAM,
cvQueryFrame.

Zmiana kanału jest możliwa poprzez v4l2, funkcja:
ioctl(file, VIDIOC_S_INPUT, &index),
gdzie file to plik urządzenia otwarty funkcją open i flagą O_RDONLY | O_NONBLOCK,
a indeks to wartość typu int z numerem kanału. 0 i 1 to kanał pierwszy.

wtorek, 1 lutego 2011

3 miesiące przerwy, podsumowanie

Od poprzedniego posta upłynęło już bardzo dużo czasu. W pracy projekt się kończy w trybie przyśpieszonym (czytać: nie ma kasy, więc robimy na szybko ile się da). Przedłużająca się niezbyt przyjemna sytuacja w pracy daje mi się we znaki (uczucie senności, zmęczenie itp). Krótko, nie bardzo mi się chciało pisać.

Z pracy:
- linux frame buffer - bardzo ciekawa bestia,
- obsługa wielokanałowego frame grabbera sterownikiem EasyCAP DC60 Driver,
- fuse - o tym już pisałem,
- wizyta w warszawie,
- przechwytywanie obrazu z kamer ip dedykowaną biblioteką pylon.

Z domu:
- przeprowadzka - w listopadzie zamieszkaliśmy we "własnym" mieszkaniu (nasze nie jest, ale w końcu sami),
- silnik się rozwija. Przemyślałem sobie parę rzeczy i sporo pracy poświęciłem przygotowaniu "framework'a" leżącego u podstaw silnika.

W silniku skończony mam jak na razie system obsługi okien. Prosty w obsłudze i bardzo elastyczny ;) Na poważnie, skupiłem się na elastyczności:
- użytkownik sam może utworzyć okno (np. CreateWindowEx) i przekazać je do systemu,
- użytkownik może wybierać pomiędzy domyślną i własną funkcją obsługi komunikatów,
- nie ma ograniczeń co do ilości okien,
- można dynamicznie tworzyć i zamykać okna,
- system zapewnia dostęp do uchwytu okna (na razie tworzę z myślą o Windows),
- renderować do okna można dowolną metodą: GDI, OGL, DX,
- można renderować do kilku okien naraz,
- można nawet dodać menu ;).

Ograniczenia:
- domyślna funkcja okna korzysta z danych zapisanych za pomocą funkcji SetWindowLong w polu GWL_USERDATA.

Wygoda:
- utworzenie typowych okien wymaga jedynie obiektu menadżera i wywołania odpowiedniej funkcji tworzącej okno.


Koniec.

środa, 24 listopada 2010

Danny MacAskill - "Way Back Home"

Dziś nieco inaczej niż zwykle, link: Danny MacAskill - "Way Back Home". Jestem pod ogromnym wrażeniem.

środa, 3 listopada 2010

Remont silnika odpowiedź

Anonimowy użytkownik zapytał czemu w poście Remont silnika wyraziłem nie chęć do stosowania rozwiązań typu singleton "Czemu zero rozwiązań typu singleton? Globalny, jednolity stan w jakiejkolwiek formie (monostate, singleton, zwykła klasa statyczna), jest bardzo naturalny i nie ma sensu unikać go na siłę w imię jakichś pustycznych idei."

Odpowiedź jest dość obszerna, także umieszczę to w nowym wpisie.

Moim zdaniem największym problemem z wszelkiej maści obiektami o dostępie globalnym są zależności jakie powstają pomiędzy różnymi modułami oprogramowania.
Pisząc swój silnik graficzny byłem bardzo pozytywnie nastawiony do tego typu rozwiązań, bo "ułatwiały" mi życie. Dzięki nim mogłem zmniejszyć ilość parametrów przekazywanych do metod. Unikałem również przekazywania obiektów w głąb hierarchii wywołań. (chcemy zasób, więc przekazujemy managerowi obiekt api, ten przekazuje go fabryce, ta przekazuje go konkretnej metodzie). Kto potrzebuje singletona ten sam może go sobie wziąć. I tak właśnie powstają zależności pomiędzy jednym modułem, a drugim. Przykładowo klasa tekstury potrzebowała rozmiar okna dla którego będzie render targetem. Bach, okno jest singletonem, wszyscy maja do niego dostęp. To był chyba największy błąd projektowy jaki popełniłem.

Zależności się nie skalują. Zmiana w jednym module często pociąga ogromne zmiany w całym systemie.
Zależności się bardzo szybko rozrastają. Ja w końcu się pogubiłem w kolejności otwierania i zamykania modułów. We własnym kodzie straciłem orientację.
Zależności się nie skalują. Jak zapewnić rozsądny dostęp do globalnych obiektów w środowisku wielowątkowym?
Obiekty globalne gryzą się z raii. Obiekt globalny jakim jest std::clog jest bardzo wygodny kiedy potrzebujemy coś logować. Osobiście bardzo często z niego korzystam, w taki sposób, że podpinam pod niego plik.
W main robie sobie taki trick:
fstream log("log.log" fstream::out);
clog.rdbuf(log.rdbuf());
Potem w dowolnym miejscu kodu wystarczy zapisać do std::clog i wszystko trafi do pliku.
Informacją, którą szczególnie warto logować jest moment otwierania i zamykania poszczególnych modułów. Obiekt zarządzający modułem powstanie już po otwarciu pliku, więc log powstanie poprawnie. Jeżeli jednak pozwolimy istnieć takiemu obiektowi poza czas trwania maina to mamy problem. Ja nie raz popełniłem taki błąd, nie tylko przy projekcie tego silnika. Niby wiem, że tak jest, a i tak wciąż popełniam ten błąd.

środa, 6 października 2010

Remont silnika

Dawno nic nie napisałem, nie mam czasu. Chcemy się przeprowadzić i aktualnie robimy dość poważny remont mieszkania. Oczywiście oboje z żoną jesteśmy zatrudnieni i wszystkie prace remontowe odbywają się po zakończeniu obowiązków zawodowych (długo myślałem jak usunąć 3 razy praca z tego zdania). Generalnie jest masakra :)

Pochwalę się tylko, że wznowiłem prace nad swoim silnikiem. Podczas pierwszego podejścia sporo się nauczyłem. Postanowiłem wykorzystać to doświadczenie i rozpocząć wszystko od nowa.

  • zmienię strukturę projektu. Poprzednio zrobiłem sobie straszną krzywdę i wszystko zamknąłem w jednym projekcie. Tym razem rozbije całość na sporą ilość małych projektów. Ułatwi to zarządzanie i testowanie kodu.
  • ograniczę idioto odporność na korzyść możliwości. Poprzednie rozwiązanie zakładało, że użytkownik jest głupi i broniłem dostępu do rzeczy takich jak zasoby czy directx.
  • zero automagiczności. Uważałem, że jeżeli silnik potrzebuje okna to powinien je umieć sobie otworzyć sam. Dzięki temu użytkowanie było banalne, o ile użytkownik chciał mieć dokładnie jedno okna o takich i takich właściwościach. Teraz uważam, że jeżeli użytkownik chce mieć okno to je sobie zrobi, jeżeli chce aby silnik coś w nim wyrenderował to powie o tym silnikowi.
  • mam zamiar zastosować model warstwowy. Poprzednio podzieliłem system na moduły i wyszedł mi z tego straszny galimatias.
  • zero rozwiązań w stylu singleton. Wszystkie niezbędne dane są przekazywane przez parametry wywołania.
  • wciąż trzymam się rozwiązań obiektowych.
  • DirectX 10. Im dłużej patrzę na wersję 10 tym mniej podoba mi się 9. OpenGL nigdy nie był obiektem mojego uwielbienia.

wtorek, 3 sierpnia 2010

Rysowanie map

Ostatnio pracowałem nad aplikacją która pozwoli na wyznaczenie jakiegoś obszaru na mapie. Dane mapy pobieram za pomocą biblioteki OGDI z VMAP'y, rysowanie odbywa się poprzez OpenGL, obsługa okien dzięki SDL.

To był mój pierwszy kontakt z mapami w formie elektronicznej, tzn. wiem co to googlemaps, ale nigdy wcześniej nie próbowałem wydobyć i wykorzystać takich informacji „własnoręcznie”. Nigdy też nie zastanawiałem się jak to jest robione. Także:
- VMAP to bardzo skomplikowana struktura, mapa Europy i Północnej Azji na poziomie 0, czyli największej skali i najmniejszej precyzji, to 509 megabajtów danych w blisko 4 tysiącach plików !!!
- Nie wyobrażam sobie samemu zrobić obsługę takiego molocha w żadnym sensownym czasie,
- OGDI pozwala na obsługę danych geograficznych podobnie do bazy danych, podajemy kryteria (obszar geograficzny) oraz tabelę (warstwę), w wyniku otrzymujemy zbiór rekordów spełniających zapytanie,
- Dane mogą przyjąć formę punktów (miasta), linii (drogi), obszarów (lasy), bądź tekstu. OGDI dopuszcza jeszcze parę innych, ale ja się z nimi nie spotkałem,
- Obszary to tak naprawdę zamknięte linie i żeby je narysować trzeba najpierw zamienić dane w coś co jest akceptowalne przez OpenGL. Tak, tak, na trójkąty,
- Zamiana obszaru, który nie jest wypukły i ma w sobie otwory, z formy konturów na trójkąty nie jest zadaniem trywialnym,
- Na szczęście istnieje OpenGL ze swoim zestawem funkcji do teselacji (gluBeginPolygon i spółka).


Przetworzenie danych z obszaru Polski na formę, którą można w miarę wydajnie renderować trwa dość długo (parędziesiąt sekund), a aplikacji rysujących mapy będzie jeszcze kilka i wszystkie będą używane naprzemiennie. Nie ma takiej możliwości aby pracowały równocześnie i nie ma takiej możliwości aby włączały się tak długo. Uznałem więc za sensowne opracowanie pomocniczego procesu, który przygotuje te dane raz i udostępni je w formie pamięci współdzielonej. W tym celu wykorzystałem boost::interprocess::shared_memory_object.

Screeny (przedstawiają Łódź):