Czytnik taśmy

Pomysł zrobienia modelu magnetofonu z Lego pojawił się około 4 miesiące przed datą ukończenia. Początkowo zakładałem możliwość drukowania i czytania danych. Z drukowaniem jednak były same problemy (brak tak dużego pisaka, niedokładność). Powstał prototyp, ale z uwagi na bardzo zły program i brak pomysłów na jego poprawę pomysł zarzuciłem. Miesiąc temu miałem już pomysł na dobry algorytm, więc jak tylko znalazłem chwilkę wolnego czasu zabrałem się za konstrukcję.

Robot zbudowany jest z kilku części strukturalnych. Po obu bokach znajdują się silniki połączone z rolkami sprzęgłami. Sprzęgło (łącznik osi) jest konieczne, aby móc odłączyć napęd od rolek. Rolki wykonane są z felg Lego owiniętych kawałkiem gumy dla zapewnienia większego tarcia. Po obu stronach rolek znajdują się wykonane z tektury koła prowadzące taśmę. Taśma jest przewijana pod czujnikiem koloru, który czyta dane.

Teraz kilka słów o taśmie i sposobie zapisu. Jest to papierowy pasek z nadrukowanymi prostokącikami w kolorze czarnym. Na samym początku ścieżki musi znajdować się sekcja inicjalizacyjna. Składa się ona z 4 bitów: CZARNY | BIAŁY | CZARNY | BIAŁY. Dzięki niej robot będzie znał długość jednego bitu na taśmie, i będzie potrafił odczytać poprawnie całą ścieżkę. Po sekcji inicjalizacyjnej następuje sekcja danych dowolnej długości. To od programu będzie zależeć jak robot interpretuje bity. Z uwagi na zastosowany algorytm na samym końcu ścieżki powinien znaleźć się bit CZARNY. Jeżeli taki bit znajduje się już w zapisanych danych, to nie jest konieczna żadna zmiana. Jeżeli natomiast dane kończą się bitem BIAŁYM, to po nim wystarczy dołożyć jeden CZARNY (nic nie znaczący).

Najważniejsza część: algorytm odczytu. Cały odczyt bazuje na przejściach CZARNY-BIAŁY i BIAŁY-CZARNY. To dlatego ścieżka musi się kończyć bitem czarnym. Jeżeli byłby to bit biały, to taśma przewijałaby się w nieskończoność po skończeniu danych, a ostatnie białe bity nigdy nie byłyby dopisane do tablicy z odczytanymi danymi. Program na bieżąco analizuje odczyt z czujnika koloru i jeżeli zauważy zmianę natężenia światła, to dopisuje odczytane bity. W analizie przejść zastosowana została histereza, aby uniknąć wpływu zakłóceń.

Podczas inicjalizacji mierzona jest długość bitu białego i czarnego. Jeżeli różnica długości jest znaczna, to następuje błąd inicjalizacji (możliwe zakłócenia od światła zewnętrznego, bity są nierówne na taśmie, itp.). Jeżeli są mniej więcej równe, to zapisana zostaje średnia wartość, ustalone poziomy przejść między bitami i inne potrzebne parametry. Następnie następuje czytanie danych. Program zapamiętuje kąt obrotu silnika dla poprzedniego przejścia i czeka na następne przejście. Następnie oblicza różnicę między wartościami kąta obrotu (długość sekwencji bitów jednego koloru), dzieli ją przez długość jednego bitu i zaokrągla do najbliższej liczby całkowitej. Tak otrzymana wartość jest liczbą bitów jednego koloru, które zostały odczytane.

Przykład: bit ma średnią długość 40 obliczoną podczas inicjalizacji. Program zanotował przejście BIAŁY-CZARNY przy kącie silnika 134, oraz przejście CZARNY-BIAŁY przy kącie 218. Różnica: 84. Dzielę przez 40 i dostaję 2.1. Zaokrąglam do 2. Zostały odczytane 2 bity czarne.

Taki sposób odczytu eliminuje całkowicie niedokładności przesuwu taśmy i pomiaru kąta obrotu silników. Wszystkie odczyty są relatywne jedynie do poprzednich, a nie do początku taśmy. Taśma może przesuwać się z ogromną prędkością zapewniając ciągle poprawne dane.

W programie zostały dodane funkcje zapisujące odczytane bity oraz odległości między przejściami podzielone przez długość bitu do plików. Jest to niezwykle pomocne przy debuggingu.

Przykładowy program czytający tekst zapisany w 7-bitowym kodzie ASCII został zamieszczony poniżej.

Dostępna jest instrukcja budowy zarówno w formie filmu jak również pliku programu Lego Digital Designer tutaj. Link do LDD znajduje się na dole strony.

Konstrukcja i program są niezwykle udane. Podczas całego okresu działania i przy dużych prędkościach taśmy robot nigdy nie pomylił się w odczycie danych. Kilka razy występował błąd inicjalizacji. Początkowo testowałem go z prędkością 10% silników. Później od razu zwiększyłęm ją do 60% bez zmiany kodu, ani szerokości bitu na taśmie i wciąż otrzymałem poprawne dane. Byłem pozytywnie zaskoczony. Polecam wszystkim!

Wideo




Galeria

Galeria 3D

Obrazy w tej galerii możesz oglądać jedynie przy użyciu specjalnych czerwono-niebieskich okularów. Można je nabyć w wielu sklepach internetowych za bardzo małe pieniądze.


Kod programu

//author: Krzysztof Kapusta, All rights reserved #define LEFT_MOTOR OUT_B #define RIGHT_MOTOR OUT_C #define SENSOR S2 #define READ SensorRaw(SENSOR) #define ROT_LEFT MotorRotationCount(LEFT_MOTOR) #define ROT_RIGHT MotorRotationCount(RIGHT_MOTOR); #define MAX_SPEED 40 // increase if you want #define HIS_WIDTH_PRC 10.0 int MED; int HIS_RISING_LEVEL; int HIS_FALLING_LEVEL; int ONE_BIT_WIDTH; void init() { SetSensorType(SENSOR, SENSOR_TYPE_COLORRED); SetSensorMode(SENSOR, SENSOR_MODE_RAW); ResetSensor(SENSOR); Wait(MS_100); PosRegSetMax(LEFT_MOTOR, MAX_SPEED, 100); PosRegSetMax(RIGHT_MOTOR, MAX_SPEED, 100); Float(LEFT_MOTOR); Float(RIGHT_MOTOR); } bool initReading() { int min = 1024, max = 0, read; long start_pt, mid_pt, end_pt; int white = READ; Float(LEFT_MOTOR); OnFwdReg(RIGHT_MOTOR, -MAX_SPEED, OUT_REGMODE_SPEED); while(1) { read = READ; if (read < white-50) break; if (read > max) max = read; } // first falling edge while (1) { read = READ; if (read > min + 20) break; if (read < min) min = read; } // just before first rising edge MED = (max+min)/2; MED *= 1.05; // !!! worked best for me, modify if you constantly get Initialization Error HIS_RISING_LEVEL = MED + HIS_WIDTH_PRC/100.0*(max-MED); HIS_FALLING_LEVEL = MED - HIS_WIDTH_PRC/100.0*(MED-min); while (READ < HIS_RISING_LEVEL); // while bit is black start_pt = ROT_RIGHT; while (READ > HIS_FALLING_LEVEL); // while bit is white mid_pt = ROT_RIGHT; while (READ < HIS_RISING_LEVEL); // while bit is black Off(RIGHT_MOTOR); end_pt = ROT_RIGHT; ONE_BIT_WIDTH = abs(start_pt - end_pt)/2; if (abs(start_pt - mid_pt) - abs(mid_pt - end_pt) > 0.2 * ONE_BIT_WIDTH) return false; else return true; } #define MAX_READ_LENGTH 1000 // number of bits you suppose to read, there's no protection for writing more #define FILES_LENGTH 5000 // increase if files are incomplete mutex READING_MUTEX; bool STOP_READING = false; bool DATA[MAX_READ_LENGTH]; // data read, data[0] is always false, data[1]... is your data long DATA_POS = 0; task read() { byte bits_h, data_h; DeleteFile("bits.txt"); CreateFile("bits.txt", FILES_LENGTH, bits_h); DeleteFile("data.txt"); CreateFile("data.txt", FILES_LENGTH, data_h); bool prev_bit = true; long count, start_count = ROT_RIGHT; int val, bits, cnt; float fbits, delta; string tmp; bool stop_flag = false; Float(LEFT_MOTOR); OnFwdReg(RIGHT_MOTOR, -MAX_SPEED, OUT_REGMODE_SPEED); while (true) { if (STOP_READING) { // one more pass with instructions in 'if' Off(RIGHT_MOTOR); stop_flag = true; } val = READ; if ((prev_bit && val < HIS_FALLING_LEVEL) || (!prev_bit && val > HIS_RISING_LEVEL) || stop_flag) { count = ROT_RIGHT; fbits = 1.0*abs(start_count - count)/ONE_BIT_WIDTH; bits = fbits + 0.5; // integer cast tmp = FormatNum("%f", fbits); WriteLnString(bits_h, tmp, cnt); delta = fbits - bits; if (delta > 0.3) ONE_BIT_WIDTH++; else if (delta < -0.3) ONE_BIT_WIDTH--; Acquire(READING_MUTEX); for (long i = DATA_POS; i < DATA_POS+bits; i++) { DATA[i] = !prev_bit; tmp = FormatNum("%d", !prev_bit); WriteString(data_h, tmp, cnt); } DATA_POS += bits; Release(READING_MUTEX); prev_bit = !prev_bit; start_count = count; } if (stop_flag) break; } CloseFile(bits_h); CloseFile(data_h); } bool STOP_PRINTING = false; task printCharacters() { long prev_data_pos = 1; int print_x = 0, print_y = LCD_LINE1; char character; while (!STOP_PRINTING) { if (DATA_POS >= prev_data_pos + 7) { character = 0; for (long i = prev_data_pos; i < prev_data_pos+7; i++) { character <<= 1; character += DATA[i]; } if (character == 10) { // new line print_y += (LCD_LINE2-LCD_LINE1); print_x = 0; } else if (character == 3 || character == 4) // end of tape STOP_PRINTING = true; else { TextOut(print_x, print_y, FormatNum("%c", character)); print_x += 6; if (print_x > 93) { print_y += (LCD_LINE2-LCD_LINE1); print_x = 0; } } prev_data_pos += 7; } Wait(MS_100); } } task main() { init(); if (!initReading()) { TextOut(0, LCD_LINE4, "Init error"); while(1); } StartTask(read); StartTask(printCharacters); while(!STOP_PRINTING); STOP_READING = true; while(!ButtonPressed(BTNCENTER, false)); }

Pokaż/ukryj cały kod

Komentarze

Aha, to jak znajdę czas to się w to pobawię. Napotkałem też teraz jeszcze jeden problem - lewy silnik, podpięty do portu B mi nie działa. Jest on w pełni sprawny, a tu odmawia posłuszeństwa. Prawy działa dobrze, ale kręci się w odwrotnym kierunku w stosunku do filmu - również nie wiem dlaczego. Może to wina wgranego softu? - w stosunku do innego w tym dźwięk nie działa, i może ma też jakieś błędy, które pokazują się w tym programie. Jeśli wiedziałbyś jak i co poprawić, żeby ten silnik działał to proszę o pomoc.

Dodane przez: Anonymous | 2014-08-31

Zamiana bitów z taśmy na muzykę wymaga wymyślenia jakiegoś sposobu kodowania nut muzycznych. W moim programie każdy dźwięk miał zapisaną informację o jego wysokości (częstotliwości) i czasie trwania. Musisz sam wymyślić kod który najlepiej będzie tobie pasował i poeksperymentować z nim. Mi to zajęło całkiem sporo czasu :)

Dodane przez: admin | 2014-08-31

Witam Mam pytanie - co powinienem zrobic, zeby zamienic papierowy kod na muzyke?. Kiedy testowalem robota, mialem do odczytu tylko tekst.

Dodane przez: slawus1998 | 2014-08-31

Upload new firmware using instructions from BricxCC website.

Dodane przez: Admin | 2014-02-06

ok thank you :) but it says error in line 25 and 26 in the code when i want to download to the NXT what can i do.??

Dodane przez: Domi | 2014-02-06

Zobacz wszystkie

Dodaj swój komentarz