Подключение нескольких сенсоров к одному входу



Есть смысл сделать небольшое предупреждение перед тем, как вы приступите к чтению этого раздела! Из-за новой структуры улучшенных датчиков платформы NXT и кабелей на 6 линий, эта возможность стала сложнее, чем с RCX-набором подключать больше чем 1 датчик на порт модуля. По моему мнению единственное имеющее смысл и более менее простое применение указанной ниже информации - сборка разветвителя датчика касания с помощью кабеля конвертера. Альтернатива которому - полноценный цифровой мультиплексор, который может работать по протоколу I2C с модулем NXT, но это однозначно не лучшее решение для начинающих.

Модуль NXT имеет 4 входя для подключения датчиков. Когда вы хотите собрать более сложного робота (и купили несколько дополнительных датчиков) этого может оказаться недостаточно для вас. К счастью есть несколько трюков, с помощью которых вы можете подключить два (или даже больше) датчиков к одному входу. Проще всего подключить два датчика касания к одному входу. Когда один (любой) из них нажат (или оба) - значение датчика будет 1, иначе 0. Вы не можете отличить какой датчик сработал, но иногда это и не нужно. Например, когда вы размещаете один датчик спереди, а второй сзади, вы можете понять каким стукнулись о препятствие по тому, куда вы сейчас ехали - вперед или назад. Еще вы можете установить режим сенсора RAW (см. выше). Теперь вы сможете получать больше информации. Если вам повезёт - значения нажатого и не нажатого сенсора будет разным для обоих сенсоров. Тогда вы сможете отличать какой из них нажат. А если нажаты сразу оба - вы получите намного меньшее значение (около 30) и тоже сможете это обнаружить.

Вы можете так же соединить датчик касания и датчик освещенности с одним входом (только для датчиков из набора RCX). Установите при этом тип сенсора "датчик света" (иначе датчик света не будет работать). Установите режим RAW. В этом случае, если нажат датчик касания - вы получите значение ниже 100. Если же он не нажат - вы получите значение датчика освещенности, которое никогда не бывает ниже 100.

Следующая программа использует этот подход. Робот должен быть снабжён датчиком освещенности, направленным вниз и датчиком касания выставленным в качестве бампера вперед. Соедините оба датчика ко входу 1. Робот будет ездить случайным образом по освещенной территории, отъезжая от препятствий. Когда датчик освещенности (значение с датчика > 750) попадает на тёмное место робот немного отъезжает назад. Когда что-то нажимает датчик касания (значение < 100) он делает то же самое. Вот текст это программы:

mutex moveMutex;

int ttt,tt2;

 

task moverandom()

{

while (true)

{

ttt = Random(500) + 40;

tt2 = Random();

Acquire(moveMutex);

if (tt2 > 0)

{ OnRev(OUT_A, 75); OnFwd(OUT_C, 75); Wait(ttt); }

else

{ OnRev(OUT_C, 75); OnFwd(OUT_A, 75); Wait(ttt); }

ttt = Random(1500) + 50;

OnFwd(OUT_AC, 75); Wait(ttt);

Release(moveMutex);

}

}

 

task submain()

{

SetSensorType(IN_1, SENSOR_TYPE_LIGHT);

SetSensorMode(IN_1, SENSOR_MODE_RAW);

while (true)

{

if ((SENSOR_1 < 100) || (SENSOR_1 > 750))

{

Acquire(moveMutex);

OnRev(OUT_AC, 75); Wait(300);

Release(moveMutex);

}

}

}

 

task main()

{

Precedes(moverandom, submain);

}

Я надеюсь программа получилась понятная. В ней есть две задачи. Задача "moverandom" заставляет робота случайным образом блуждать. Главная задача запускает "moverandom", потом переходит в своё продолжение "submain" чтобы запустилась "moverandom", настраивает сенсоры и ждет пока что-нибудь случится. Если значения сенсоров будут слишком малы (касание) или слишком велики (выехали из освещенного района) она остановит случайные блуждания, отъедет немного назад и затем снова продолжит случайные движения.

Подводим итоги

В этой главе вы познакомились с некоторым количеством дополнительных возможностей сенсоров. Мы увидели, как отдельно устанавливать тип и режим работы датчиков, как эти режимы могут быть использованы для получения дополнительной информации. Мы научились использовать датчик вращения и увидели, как несколько датчиков можно подключить к одному входу NXT. Все эти трюки очень полезны при сборке достаточно сложных роботов, в которых сенсоры имеют огромное значение.

Параллельные задачи

Как показывалось ранее, задачи в NXC выполняются одновременно, или как обычно говорят - параллельно. Это очень удобно. Такая возможность позволяет вам следить за датчиками в одной задаче, пока вторая задача управляет движением робота, а, скажем, третья задача играет какую-нибудь музыку. Но параллельные задачи могут создавать проблем. Одна задача может начать мешать другой.

Неправильная программа

Разберем следующую программу. В ней одна задача управляет движением робота по квадратам (как мы часто уже делали раньше), а другая задача проверяет датчик касания, когда датчик нажат, она отводит робота немного назад и делает поворот на 90 градусов.

task check_sensors(){ while (true) { if (SENSOR_1 == 1) { OnRev(OUT_AC, 75); Wait(500); OnFwd(OUT_A, 75); Wait(850); OnFwd(OUT_C, 75); } }} task submain(){ while (true) { OnFwd(OUT_AC, 75); Wait(1000); OnRev(OUT_C, 75); Wait(500); }} task main(){ SetSensor(IN_1,SENSOR_TOUCH); Precedes(check_sensors, submain);}

Вероятно программа выглядит как совершенно правильная, но если вы попробуете загрузить её в робота вы скорее всего обнаружите некоторое необычное его поведение. Попробуйте следующее, чтобы убедиться в этом: сделайте так, чтобы робот задел что-то бампером при повороте. Он начнет отъезжать назад, но немедленно снова начнет двигаться вперед, снова ударяясь о препятствие. Причина этого в том, что задачи начинают пересекаться. А происходит вот что. Робот едет в повороте и ударяется об препятствие. Он начинает отъезжать назад, но в этот момент главная задача (submain) завершает ожидание конца разворота и даёт команду ехать прямо на препятствие, игнорируя тот факт, что сейчас выполняется маневр отъезда от него. Вторая задача в это время находится в состоянии ожидания и даже не сможет обнаружить столкновение. Это точно не то поведение робота, которое мы хотели бы видеть. Проблема заключается в том, что пока вторая задача ждёт мы забыли, что первая задача продолжает работу и её действия начинают мешать работе задаче избегания препятствий.

Критические секции и "мьютекс"-переменные

Один из способов решения этой проблемы это введение механизма, гарантирующего, что только одна задача в данный момент работает с двигателями робота. Такой подход мы уже применяли в главе 6. Давайте посмотрим на эту программу еще раз.

mutex moveMutex;task move_square(){ while (true) { Acquire(moveMutex); OnFwd(OUT_AC, 75); Wait(1000); OnRev(OUT_C, 75); Wait(850); Release(moveMutex); }} task check_sensors(){ while (true) { if (SENSOR_1 == 1) { Acquire(moveMutex); OnRev(OUT_AC, 75); Wait(500); OnFwd(OUT_A, 75); Wait(850); Release(moveMutex); } }} task main(){ SetSensor(IN_1,SENSOR_TOUCH); Precedes(check_sensors, move_square);}

Загвоздка здесь в том, что обе задачи (check_sensors и move_square) контролируют моторы только если другая задача не использует их в данный момент. Это делается с помощью команд Acquire, которые ждут, чтобы "мьютекс"-переменная moveMutex стала свободной, перед тем как начать работать с моторами, после чего команда блокирует эту переменую и передаёт управление следующей команде. Команда Acquire всегда используется на пару с командой Release, которая сообщает системе, что "мьютекс"-переменная освободилась, и теперь другие задачи могут использовать соответствующий общий ресурс, в нашем случае - двигатели робота. Кусок программы внутри пары Acquire - Release называется критической областью программы, в том смысле, что используются общие ресурсы. Это гарантирует что в таких областях задачи не будут конфликтовать друг с другом по объявленным общим ресурсам.

Использование семафоров

Существует возможность собрать самому альтернативу "мьютекс"-переменым, являющуюся явной реализацией команд Acquire и Release.

Стандартный способ решить эту программу - объявить переменную, которая будет указывать, какая задача контроллирует двигатели. Другие задачи не должны использовать двигатели, пока первая задача не объявит их свободными. Такая переменная часто называется семафором. Пусть sem это такой семафор (это то же самое по сути, что и "мьютекс"). Мы будем считать, что значение 0 показывает, что двигатели свободны. Теперь, если задача хочет использовать моторы - она должна выполнить следующие команды:

until (sem == 0);sem = 1; //Acquire(sem);// Чего-то делаем с моторами// (Это критическая область программы)sem = 0; //Release(sem);

Сначала мы ждем, пока моторы будут свободны, затем забираем этот ресурс себе, выставляя 1. Теперь мы управляем ими. Когда мы всё, что хотели, сделали - пишем в sem снова 0. Ниже можно увидеть программу, реализующую семафор. Когда обнаруживается нажатие датчика касания, семафор выставляется в 1 и выполняется процедура отката назад. Во время неё задача move_square обязана ждать. Когда откат выполнен - семафор выставляется снова в 0 и задача move_square может продолжить.

int sem;task move_square(){ while (true) { until (sem == 0); sem = 1; OnFwd(OUT_AC, 75); sem = 0; Wait(1000); until (sem == 0); sem = 1; OnRev(OUT_C, 75); sem = 0; Wait(850); }} task submain(){ SetSensor(IN_1, SENSOR_TOUCH); while (true) { if (SENSOR_1 == 1) { until (sem == 0); sem = 1; OnRev(OUT_AC, 75); Wait(500); OnFwd(OUT_A, 75); Wait(850); sem = 0; } }} task main(){ sem = 0; Precedes(move_square, submain);}

Вы можете сказать, что не ясно зачем в задаче move_square мы выставляем семафор в 1 и обратно в 0. Но на самом деле это полезно. Причина в том, что команда OnFwd() на самом деле состоит из двух команд (см. главу 8). Если вы не хотите, чтобы эту пару команд прервала другая задача - нужно так же блокировать здесь доступ к устройству.

Семафоры очень полезны и, когда вы пишете достаточно сложные программы с параллельными задачами, они почти всегда будут вам нужны. (Кстати, существует некоторый небольшой шанс, что такой семафор может не сработать. Попробуйте разобраться, почему.)

Подводим итоги

В этой главе мы научились решать некоторые проблемы, которые могут возникнуть при параллельной работе нескольких задач. Всегда будьте внимательны к возможным побочным эффектам при такой работе. Очень много случаев незапланированной работы программ возникает при работе с параллельными задачами. Мы увидели два возможных пути решения таких проблем. Первый заключается в том, что задачи выполняются в критической своей области по очереди. Второй подход использует семафоры для управления порядком выполнения команд. Это гарантирует что только одна задача в каждый момент времени будет находиться в своей критической области связанной с соответствующим общим ресурсом.

Коммуникации между роботами

Если у вас есть больше чем один модуль NXT, тогда эта глава для вас (хотя вообще можно устанавливать связь не только между роботами, но и робота с ПК). Роботы могут связываться друг с другом через радиоканал Bluetooth: так что вы можете иметь нескольких роботов одновременно выполняющих общую задачу (или сражающихся друг с другом), кроме того вы можете построить большого сложного робота из двух NXT, так что можно будет использовать 6 сервомоторов и 8 сенсоров. Для старого доброго RCX, это очень просто: он отправляет ИК-сообщение и все роботы вокруг принимают его.

Но в NXT реализован совершенно другой подход! Во-первых вам нужно соединить два или более NXT (или NXT и ПК) с помощью меню "Bluetooth" на модуле NXT; только тогда вы сможете отправлять сообщения к подключенным устройствам. NXT который инициирует подключение называется Мастером, и может иметь до 3 подчиненных устройств, подключенных к линиям 1,2 и 3. Подчиненный модуль всегда видит мастера на линии 0. Вы можете отправлять сообщения в 10 доступных ячеек.


Дата добавления: 2018-05-12; просмотров: 325; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!