суббота, 23 июня 2012 г.

NXT и Python: включаем моторы

Традиционно изучение программирования Lego-роботов начинается с простых экспериментов с движением - именно эта функция в роботах используется чаще всего.
К тому же, при такого рода знакомстве, обычно сразу видно, что механизм делает и на сколько корректно.

Чтобы узнать, какие функции модуля nxt-python нужно использовать, чтобы заставить робота двигаться, можно использовать два способа. В одном случае, можно воспользоваться свойством открытости - языки с открытым исходным кодом тем и хороши, что позволяют зайти в любой модуль, любую функцию и посмотреть для чего она нужна и что она делает. Другой способ, воспользоваться встроенной в Python функцией подсказки - например, через терминал, предоставляющий доступ к командной строке в Linux:
$ python
Python 2.7.2 (default, Oct 27 2011, 01:36:46) 
[GCC 4.6.1 20111003 (Red Hat 4.6.1-10)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import nxt.motor
>>> help(nxt.motor)
Итак, что можно почерпнуть из этих источников?

Например, можно увидеть - прежде, чем осуществлять передвижение робота, загружаются внутренний модуль motor. Фактически, в коде это будет выглядеть, как
import nxt.motor
или
from nxt.motor import *
Данный модуль предоставляет несколько классов для работы с моторами и соответствующими методами.

Так для работы с одним мотором можно воспользоваться классом Motor. Тогда прежде, чем отдавать команды мотору, нужно создать экземпляр этого класса и связать его с нужным мотором.
engine = Motor(mybrick, PORT_A)
Здесь,
engineИмя переменной, которая будет ссылаться на объект связанный с мотором
mybrickПеременная указывающая на объект, отвечающий за NXT блок, к которому присоединен мотор. Как инициализировать NXT блок написано в этой заметке.
PORT_AКонстанта, определяющая к какому порту подключен мотор.

Тогда для работы с мотором доступны следующие методы:

engine.turn(80, 720)
В данном примере ось мотора повернется на 720 градусов со мощностью 80. Мощность можно задавать отрицательными числами, если необходимо вращать мотор в противоположную сторону - engine.turn(-100, 1080). Также можно указать третий параметр, который позволит не тормозить мотор при достижении нужного количества градусов, а просто отключить от него напряжение: engine.turn(50, 90, False) - тогда мотор по инерции еще будет продолжать вращаться.

engine.run(90) или engine.run(-60)
Этот метод включит данный мотор с указанной мощностью и передаст управление следующей команде в программе. Чтобы после этого, при наступлении какого-то события, остановить мотор - нужно использовать методы: engine.idle() или engine.brake(). brake - резкое торможение, а idle просто отключает подачу питания.

engine.get_tacho()
Позволяет узнать текущие показатели енкодера двигателя. Лучше всего работу этого метода и другого, но с ним связанного, - reset_position, рассмотреть на примере. После текста программы этого примера будет приведено, то, что программа выводит на экран.
#!/usr/bin/env python
 
import nxt.locator
from nxt.motor import *
from time import sleep
 
def output_tacho(motor):
  tacho=motor.get_tacho()
  print "Tacho Count:\t\t", tacho.tacho_count
  print "Block Tacho Count:\t", tacho.block_tacho_count
  print "Rotation Count:\t\t", tacho.rotation_count
 
def do_step(motor):
  motor.run()
  sleep(.5)
  motor.brake()
 
b = nxt.locator.find_one_brick()
 
back = Motor(b, PORT_A)
# Assuming that the NXT block just turned on
# and counters are zero
 
print "Before motor starts"
output_tacho(back)
 
do_step(back)
print "1st step finished"
output_tacho(back)
 
back.reset_position(False)
print "Here reset_position called with False"
 
do_step(back)
print "2nd step finished"
output_tacho(back)
 
back.reset_position(True)
print "Here reset_position called with True"
 
do_step(back)
print "3rd step finished"
output_tacho(back)
 
sleep(.5)
back.idle()
Программа трижды запускает мотор, и в последние два запуска перед включением моторов сбрасываются показатели енкодера. Какой конкретно из показателей сбрасывается определяется флагом, передаваемым в reset_position.
Before motor starts                   
Tacho Count:            0
Block Tacho Count:      0
Rotation Count:         0
1st step finished
Tacho Count:            342
Block Tacho Count:      342
Rotation Count:         342
Here reset_position called with False
2nd step finished
Tacho Count:            746
Block Tacho Count:      746
Rotation Count:         399
Here reset_position called with True
3rd step finished
Tacho Count:            1148
Block Tacho Count:      397
Rotation Count:         801
Видно, что вызов функции сброса никак не влияет на показатель Tacho Count - он показывает абсолютную позицию мотора после активации NXT блока.
Запуск функции с параметром False, скорее всего, нужно делать в начале каждой программы - это позволит отсчитывать относительную позицию двигателя Rotation Count с начала работы программы.
Ну и наконец, внутри циклов, других методов или функций имеет смысл выставлять параметр в True при использовании reset_position - при этом относительная позиция программы сохраниться, а относительная позиция внутри данного блока Block Tacho Count может быть использована для навигации.

Работа сразу с двумя моторами, синхронизированными друг с другом, несколько сложнее. За нее отвечает класс SynchronizedMotors. Дело в том, что как распределяется мощность между моторами задается при инициализации класса:
left_engine  = Motor(mybrick, PORT_B)
right_engine = Motor(mybrick, PORT_C)
 
vehicle = SynchronizedMotors(left_engine, right_engine, 0)
Иными словами, сначала нужно проинициализировать каждый мотор, а потом связать их вместе. Последний параметр "0" как раз и задает распределение мощности. В примере выше, "0" означает, что на оба мотора будет подаваться одинаковая скорость.

Если потом в программе возникнет необходимость поменять распределение, то нужно будет объект заново инициализировать:
vehicle = SynchronizedMotors(left_engine, right_engine, -70)
Результирующая программа будет выглядеть, например, так:
#!/usr/bin/env python
 
import nxt.locator
from nxt.motor import *
from time import sleep
 
mybrick = nxt.locator.find_one_brick()
 
left_engine  = Motor(mybrick, PORT_B)
right_engine = Motor(mybrick, PORT_C)
 
vehicle = SynchronizedMotors(left_engine, right_engine, 0)
 
vehicle.turn(100, 180)
sleep(.5)
vehicle.turn(-100, 180)
 
vehicle = SynchronizedMotors(left_engine, right_engine, 25)
 
vehicle.turn(100, 90)
sleep(.5)
vehicle.turn(-100, 90)
 
sleep(.5)
vehicle.idle()
Как видно, при синхронизированный моторах используются все те же самые методы, что и для одиночного мотора:
  • turn - повернуть моторы на определенное количество градусов
  • run - включить моторы с определенной базовой мощностью и передать управление следующей команде
  • brake - затормозить
  • idle - отключить питание

В завершение, хочется обратить внимание на одну важную деталь - brake/торможение все время держит моторы под нагрузкой, чтобы обеспечить их неподвижность. Это будет продолжаться даже если программа закончила свое выполнение. Именно поэтому в примерах выше перед концом каждого скрипта выполняется idle - это не только поможет руками крутить моторы, например, чтобы поставить их в какое-то начальное положение, но и будет экономить разряд батареи - энергия на моторы перестанет подаваться.

Комментариев нет:

Отправить комментарий

Related Posts Plugin for WordPress, Blogger...