понедельник, 24 сентября 2012 г.

NXT и Python: опрашиваем датчики

Базовые способы работы с моторами в Python были рассмотрены в прошлой заметке - теперь настало время осветить, какие API (applications programming interface) доступны для работы со стандартными LEGO NXT датчиками.
Начнем с того, что посмотрим на модуль nxt.sensor, который предоставляет классы по работе с датчиками. Для этого в Python Shell запросим справку по модулю:
import nxt.sensor
help(nxt.sensor)
Небольшая информационная страничка расскажет, что данный модуль предоставляет доступ к следующим под-модулям:
  • analog
  • common
  • digital
  • generic
  • hitechnic
  • mindsensors
Т.е. видно, что помимо стандартных датчиков, библиотека nxt-python поддерживает работу и с датчиками известных производителей Mindsensors и HiTechnic.

Сразу можно раскрыть нюансы использования модуля nxt.sensor - в большинстве случаев не нужно импортировать каждый отдельный под-модуль, чтобы получить доступ к классам, отвечающим за работу того или иного датчика, - эти классы уже импортированы в пространство имен этого модуля. Но для того, чтобы посмотреть каждый отдельный класс можно, тем не менее, воспользоваться справкой по каждому отдельному под-модулю. Например, чтобы получить информацию по API, работающим со стандартными NXT датчиками, нужно выполнить help(nxt.sensor.generic).

Теперь, глядя на полученную таким образом документацию, приступаем к изучению механизмов работы.

Датчик касания
Доступ к данному датчику осуществляется через класс Touch:
from nxt.sensor import Touch, PORT_1
SensorTouch = Touch(brick, PORT_1)
У данного класса полезен только один метод: is_pressed(), который возвращает True, если датчик в соответствующем порту нажат, иначе False. Метод может использоваться, например, вот так:
engine.run(90)
while(not SensorTouch.is_pressed()):
    pass
engine.brake()

Датчик расстояния
За работу с ультрасоник датчиком отвечает класс Ultrasonic.
from nxt.sensor import Ultrasonic, PORT_4
USSensor = Ultrasonic(brick, PORT_4)
Судя по документации, в классе заявляется поддержка работы со всеми пятью режимами (о режимах в частности и о работе US Senor вообще можно почитать на английском языке вот здесь), которые поддерживает датчик расстояния NXT: OFF, SINGLE_SHOT, CONTINUOUS_MEASUREMENT, EVENT_CAPTURE, REQUEST_WARM_RESET. На самом деле, скорее всего все зависит от того, поддерживает ли firmware блока эти команды в полном объеме.

В реальной жизни, скорее всего, будет использоваться стандартный (CONTINUOUS_MEASUREMENT?) режим. Который включается по умолчанию. Поэтому расстояние до ближайшего предмета можно получить с помощью метода get_distance(). Возвращаемое значение метода будет принимать числовое значение от 0 до 255 - расстояние в сантиметрах (фактически от 7 до 255).
engine.reset_position(False)
engine.run(50)
for i in range(0, 100):
    print "Angle:", engine.get_tacho().rotation_count,
    print "Distance:", USSensor.get_distance()
engine.brake()

Датчик освещенности
Класс Light предоставляет средства для получения замеров отраженного или окружающего света.
from nxt.sensor import Light, PORT_3
eye = Light(brick, PORT_3)
При инициализации класса подсветка светодиодом включена не будет. Т.е. замеры освещенности будут производиться для окружающего света. Позднее, подсветка может быть как включена, так и выключена (при необходимости) с использованием метода set_illuminated(), который в качестве параметра принимает логическое значение, обозначающее включить или выключить светодиод.
FREQ_D = 587
FREQ_E = 659
brick.play_tone_and_wait(FREQ_E, 500)
eye.set_illuminated(False)
brick.play_tone_and_wait(FREQ_D, 500)
eye.set_illuminated(True)
Замер освещенности происходит в режиме считывания сырых данных, т.е. результатом будет число из диапазона от 0 до 1023. За это отвечает метод get_lightness():
TRIGGER_POINT=510
while True:
    if eye.get_lightness() < TRIGGER_POINT:
        engineLeft.run(100)
        engineRight.run(10)
    else:
        engineLeft.run(10)
        engineRight.run(100)

Датчик цвета
Инициализация класса Color20 - датчика цвета практически не отличается от остальных датчиков.
import nxt.sensor
camera = Color20(brick, PORT_2)
При этом автоматически включается режим, когда горят все три светодиода: красный, зеленый и синий - датчик в этом режиме готов к определению цвета через вызов метода get_color():
view = camera.get_color()
if view == 6:
    print "White"
elif view == 1:
    print "Black"
else
    print "Too colorful!"
Метод возвращает число, характеризующее цвет под датчиком:
1Черный
2Синий
3Зеленый
4Желтый
5Красный
6Белый

Из программы на языке Python датчик также можно использовать в режиме определения освещенности, как для отраженного цвета, так и для окружающего . Для этого можно воспользоваться методом get_reflected_light(). Метод в качестве параметра принимает значение, каким светодиодом производить подсветку (Type.COLORRED, Type.COLORGREEN, Type.COLORBLUE) или вообще не производить (Type.COLORNONE):
TARGET_POINT = 510
while True:
    err = TARGET_POINT - camera.get_reflected_light(Type.COLORRED)
    P = err * 0.5
    engineLeft.run(50 + P)
    engineRight.run(50 - P)
Существует также способ отдельно манипулировать светодиодами посредством метода set_light_color(). Например, робот может помигать своими светодиодами и в конце выключить их вообще:
from time import sleep
for color in (Type.COLORRED, Type.COLORGREEN, Type.COLORBLUE):
    camera.set_light_color(color)
    sleep(0.5)
camera.set_light_color(Type.COLORNONE)
В заключение, можно также сказать, что опрос цветового датчика в режиме освещенности не выглядит оптимальным, если взглянуть на исходный код модуля gereric.py:
def get_reflected_light(self, color):
    self.set_light_color(color)
    return self.get_input_values().scaled_value
 
Видно, что перед каждым опросом метод тратит время на вызов метода, включающего светодиод. А ведь в большинстве случаев он уже включен. Поэтому использовать get_reflected_light(), например, в цикле не выглядит оптимальным. Вместо этого, нужно воспользоваться знанием того, что помимо родных методов класса Color20, можно вызывать унаследованные методы. В данном случае полезным будет метод get_input_values(), возвращающий кортеж различных данных о датчике, в том числе и величину отраженного света. Теперь если светодиод включить только один раз перед циклом, то программа будет выглядеть следующим образом:
TARGET_POINT = 510
camera.set_light_color(Type.COLORRED)
while True:
    err = TARGET_POINT - camera.get_input_values().scaled_value
    P = err * 0.5
    engineLeft.run(50 + P)
    engineRight.run(50 - P)

1 комментарий:

Related Posts Plugin for WordPress, Blogger...