вторник, 2 октября 2012 г.

NXC: датчики, енкодеры, кнопки - взаимодействуем с внешним миром

Программирование робота, в большинстве случаев, - изменение поведения робота как реакция на информацию, пришедшую к управляющему устройству с датчиков и сенсоров. Эта заметка рассматривает примеры опроса датчиков в языке Not eXactly C, а также некоторые особенности, которые необходимо учитывать при работе с ними.
Пример 1. Датчик расстояния
Для датчика расстояния (ультразвуковой) используются специальные функции для его инициализации и опроса.
task main() {
    /*
    Робот ждет пока перед ним не окажется предмет (ближе чем 20 см.), после
    чего отодвигается от предмета на 50 см.
    */

    //Говорим, что медленный (ультразвуковой) сенсор установлен в первый
    //порт.
    SetSensorLowspeed(S1);
    Wait(100);

    while (true) {
        //Опрашиваем порт первый, как ультразвуковой датчик
        //Ждем, пока расстояние до предмета не станет меньше 20 см.
        until (SensorUS(S1) < 20);

        //Отодвигаемся назад до тех пор, пока расстояние до предмета не
        //станет больше 50 см.
        OnRev(OUT_AB, 50);
        until (SensorUS(S1) > 50);
        //Явно останавливаем двигатели
        Off(OUT_AB);
    }
}

Пример 2. Измерение отраженного света.
Работа датчика в режиме измерения отраженного света (пространство перед датчиком дополнительно освещается светодиодом) позволяет повысить его чувствительность. Без использования этого режима, решение такой задачи как распознавание цвета или градаций серого была бы довольно трудно выполнима – обычно окружающий свет не имеет достаточной интенсивности, поэтому соседние градации цветов сливались бы один цвет при измерении. К тому же, измерения при неравномерном освещении давали бы разный результат для одного и того же предмета. Поэтому использование дополнительной подсветки – наиболее частый вариант при управлении роботом с помощью датчика освещенности.
task main() {
    /*
    Робот двигается пока поверхность под световым сенсором не потемнеет.
    */

    //Говорим, что cветовой сенсор установлен в третий порт.
    SetSensorLight(S3);
    Wait(500);

    //Двигаемся вперед, пока значение сенсора в третьем порту не станет 
    //меньше 55 процентов.
    OnFwd(OUT_AB, 50);
    until(Sensor(S3) < 55);

    Off(OUT_AB);
}

Пример 3. Измерение окружающего света
В некоторых задачах, дополнительная подсветка только мешает. Например, определить какое место в комнате самое темное. Для этого нужно измерять именно окружающее освещение.
task main() {
    /*
    Робот отображает на экране текущую освещенность.
    */

    //Говорим, что cветовой сенсор установлен в третий порт
    //лампа подсветки - неактивна
    SetSensorType(S3, SENSOR_TYPE_LIGHT_INACTIVE);
    //Сенсор, устанавливаемый через SetSensorType по-умолчанию показывает
    //ненормализованные данные (не путать с RAW). Переводим, его в режим для
    //считывания процентов.
    SetSensorMode(S3, SENSOR_MODE_PERCENT);
    Wait(500);

    while(true) {
       //Выводим на эркан текущие показатели сенсора, перед выводом -
       //очищаем экран (4-ый параметр - true)
       NumOut(0, LCD_LINE1, Sensor(S3), true);

       Wait(50);
    }
}

Пример 4. Использование необработанных данных
По умолчанию, датчики выдают измерения в процентах. В то время как NXC предоставляет возможность использовать необработанные (RAW) данные. Преимущество – очевидно – поскольку необработанные данные могут быть в диапазоне от 0 до 1023, измерения будут в 10 раз чувствительнее.
task main() {
    /*
    Робот отображает на экране показания отраженного света.
    
    Темная поверхность:
        1-ая строка (проценты): значения маленькие (<50%)
        2-ая строка (raw): значения большие (>600)

    Светлая поверхность:
        1-ая строка (проценты): значения маленькие (>50%)
        2-ая строка (raw): значения большие (<400)
    */
    
    //Говорим, что cветовой сенсор установлен в третий порт
    SetSensorLight(S3);
    Wait(500);

    while(true) {
       //Выводим на эркан в первой строке текущие показатели сенсора в
       //процентах, перед выводом - очищаем эркан (4-ый параметр - true)
       NumOut(0, LCD_LINE1, Sensor(S3), true);
       //Выводим на эркан во второй строке текущие необработанные показатели
       //сенсора, экран не очищается (4-ый параметр не указывается - берется
       //значение по-умолчанию false)
       NumOut(0, LCD_LINE2, SensorRaw(S3));

       //Чтобы избежать мерцания цифр на экране, делаем небольшую паузу.
       Wait(50);
    }
}

Пример 5. Датчик вращения двигателя (енкодер)
Поскольку датчик вращения двигателя (енкодер) скрыт от глаз конструктора, часто о его существовании забывают. Тем не менее, с помощью этого датчика можно решать множество задач: для определения столкновения с препятствием, для синхронизированного движения моторов, для контроля положения двигателя в одном положении с использованием регуляторов и т.п.
task main() {
    /*
    Робот начинает двигаться, затем, в цикле дважды опрашивается датчик 
    поворота двигателя с небольшой паузой.
    Поскольку пауза происходит параллельно движению двигателя (использование
    функции OnFwd), оценивается поворот двигателя, произошедший во время
    этой паузы.
    Если поворот меньше ожидаемого, то скорее всего робот наткнулся на
    препятствие - препятствие мешает провернуться двигателю. После 
    обнаружения такой ситуации - движение прекращается.
    */

    int Start;
    int Diff;
    int Desired;
    //Переменная используется, чтобы при первой итерации цикла определить
    //поворот двигателя во время паузы и взять этот поворот за эталон
    int iter = 0;

    OnFwd(OUT_AB, 40);

    do {
        //Считываем показание датчика поворота перед паузой
        Start = MotorRotationCount(OUT_A);
        Wait(100);
        //Считываем показание датчика после паузы и определяем угол поворота
        Diff = MotorRotationCount(OUT_A) - Start;
        //Если это первая итерация цикла - принять угол поворота за эталон
        if (iter == 0) {
            Desired = Diff * 8 / 10;
            //Указать, что больше в этот 'if' заходить не надо
            iter = 1;
        }
    } while (Diff > Desired) //Если угол поворота двигателя меньше эталона

    Off(OUT_AB);
}

Еще больше деталей об использовании датчика вращения двигателя можно прочитать в одной из предыдущих заметок.

Пример 6. Скорость опроса датчика расстояния
При работе с датчиком расстояния необходимо помнить, что он является низкоскоростным устройством, т.е. опрос состояния этого датчика занимает определенное время. Это в первую очередь связано с физической природой процесса измерения расстояния: для того чтобы звуковая волна прошла путь до препятствия и вернулась обратно в датчик, определенно, необходимо какое-то время.
#define OneSecond 1000

task main() {
    /*
    В каждой итерации цикла опрашивается датчик расстояния. Количество
    итераций определяется выяснением, сколько миллисекунд прошло с начала
    работы программы. Как только количество миллисекунд становится больше
    1000 - цикл прерывается.
    Поскольку ультразвуковой сенсор является низкоскоростным устройством,
    его опрос вызывает задержку.

    Результат показывает, что ультразвуковой сенсор может быть опрошен 34
    раза за одну секунду.
    Т.е. задержка опроса сенсора составляет - 1000/34 = 30 миллисекунд.
    */

    //Указываем, что низкоскоростной сенсор (ультразвуковой) установлен в
    //первый порт
    SetSensorLowspeed(S1);
    Wait(100);

    //Считываем начальное показание внутреннего таймера
    unsigned long Start = CurrentTick();
    unsigned long Diff;
    unsigned long i = 0, value;

    do {
        //Опрашиваем сенсор, его показания не важны для данной программы,
        //важен факт его вызова
        value = SensorUS(S1);
        //Вычисляем разницу между начальным показанием таймера и текущим
        Diff = CurrentTick() - Start;
        i++;
    } while (Diff < OneSecond) //Повторять, пока разница - меньше секунды

    NumOut(0, LCD_LINE1, i);
    Wait(3000);
}

Пример 7. Определение цвета
Работа с датчиком цвета практически не отличается от работы с датчиком освещенности, следует только помнить, что в датчике цвета светодиодов три.
task main()
{
    /Инициализация датчика в первом порту - включить все три светодиода
    SetSensorColorFull(S1);

    while(1)
    {
        //Опрашиваем датчик и выводим на экран определившийся цвет
        NumOut(0, LCD_LINE1, Sensor(S1));
        Wait(1000);
    }
}

Еще несколько вариантов определения цвета с помощью этого датчика можно найти здесь и здесь (в конце заметки).

Пример 8. Опрос датчика цвета в режиме датчика освещенности
В определенных задачах датчик цвета может заменить датчик освещенности.
task main()
{
    //Инициализация датчика в первом порту - включить красный светодиод
    SetSensorColorRed(S1);

    while(1)
    {
        //Опрашиваем датчик и выводим на экран значения отраженного
        //света в процентах и "сырых" показаниях
        NumOut(0, LCD_LINE1, Sensor(S1));
        NumOut(0, LCD_LINE2, SensorRaw(S1));
        Wait(1000);
    }
}

Пример 9. Нажата ли кнопка на NXT блоке?
Этот пример уже был расписан во в этой заметке.

2 комментария:

  1. Как всегда интересно и по делу. Спасибо!
    Ждём новых заметок!

    ОтветитьУдалить
  2. Даниил Сидоров2 апреля 2014 г. в 19:02

    Пожалуйста, напишите как сделать чтобы сенсор света светил!

    ОтветитьУдалить

Related Posts Plugin for WordPress, Blogger...