понедельник, 21 февраля 2011 г.

Программирование: события и состояния. Часть III

Продолжение. Начало здесь и здесь.

В прошлой заметке было разработана программа для движения тележки вдоль стены на определенном расстоянии. А измениться ли программа, если вместо тележки, собранной по танковой схеме, использовать тележку с управляющими колесами?


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

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



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


Движение же тележки с управляющими колесами немного отличается тем, что, после выполнения поворота, для того чтобы начать ехать прямо, нужно изменить положение управляющих колес, так чтобы они встали в нейтральную позицию. Но как узнать в какую сторону их изменять из положение, ведь если до этого поворот был в правую сторону, изменение положения колес должно происходить влево, а если поворот – левый, колеса надо перемещать вправо?


Существует еще одно отличие. Допустим, что на очередной итерации цикла было обнаружено, что расстояние до стены меньше 14 см, т.е. необходимо выполнить поворот вправо и для этого происходит изменение положения колес. Что происходит на следующей итерации цикла? Значение с сенсора расстояния все еще меньше 14 см., но нужно ли изменять положение колес? Нет. Они уже стоят в нужном положении. Следовательно, необходимо отличать состояния, когда с сенсора приходят сигналы о повороте, но колеса стояли в нейтральном положении или уже повернуты.

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

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


От предыдущей диаграммы ее отличает
  1. Появление новой сущности – состояние. Состояние, определенное после одной итерации, используется как входные данные для определения нового действия и нового состояния.
  2. Отдельные блоки для выбора действия и его выполнения. Почему? Потому что в некоторых случаях при переходе в различное состояние, будут выполняться одни и те же действия. Например, при изменении положения управляющих колес для выполнения поворота направо и при изменении положения колес из позиции для левого поворота в нейтральную, необходимо повернуть их вправо на один и тот же градус. Поэтому неплохо было бы оптимизировать этот блок, включив в него только "уникальные" действия. Но тогда для того, чтобы передавать действие, необходимое для выполнения в этом блоке, необходимо для него тоже завести код, подобные тем, что заводились для кодирование событий.
Диаграмма переходов тоже измениться - в ней появятся обозначения для действий, выполняемых для управления тележкой:

Где,
  • Event = 0 – показания на сенсоре меньше 14 см.
  • Event = 1 – показания на сенсоре больше 14 см., но меньше 16 см.
  • Event = 2 – показания на сенсоре больше 16 см.
  • Action = 0 – положение управляющих колес не изменяется
  • Action = 1 – повернуть управляющие колеса вправо
  • Action = 2 – повернуть управляющие колеса влево
Для состояний тоже введен код, чтобы ими было проще манипулировать в программе. Может показаться, что состояния S0, S1 и S2 можно расшифровать как
  • S0 – состояние движения прямо
  • S1 – состояние выполнения поворота направо
  • S1 – состояние выполнения поворота налево
Но лучше не привязываться к таким описаниям, поскольку иногда тому или иному коду могут соответствовать несколько реальных состояний. Так например, даже в нашем случае, состояние S0 – это и движение прямо, и промежуточные состояния при ситуации когда робот из одного поворота должен сразу перейти в другой.

Измененная диаграмма переходов, теперь может быть прочитана следующим образом:
Когда робот находится в состоянии S0, при наступлении события 0, робот переходит в состояние S1 и выполняет действие 1. Когда робот находится в состоянии S2, при наступлении события 0, робот переходит в состояние S1 и выполняет действие 3.

Как видно из диаграммы переходов, у каждого состояния-вершины есть стрелка-петля, сообщающая, что при определенном событии, робот продолжает находиться в данном состоянии. При этом все еще генерируется "управляющее" действие, которая правда в нашем конкретном случае можно охарактеризовать как бездействие – не изменять положение управлющих колес: при движении прямо колеса будут оставаться прямо, при повороте, колеса будут оставаться направленными в нужную сторону.

Прежде, чем писать программу, диаграмму переходов удбно в начале представить в виде следующих таблиц:

Какое будет новое состояние в зависимости от поступившего сигнала и предыдущего состояния:Какое будет управляющее действие в зависимости от поступившего сигнала и предыдущего состояния:
Соответственно, программа для движения вдоль стены тележки с управляющими колесами будет выглядеть следующим образом:

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

Например, двумерный массив States описывал бы как новое состояние зависит от пришедшего события и старого состояния, а двумерный массив Actions содержал бы в себе логику выбора действия для соответствующего события и состояния.
Тогда определение нужного действия и нового состояния выглядела бы следующим образом:

Action = Actions[Event][OldState]
NewState = States[Event][OldState]

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

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

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

Related Posts Plugin for WordPress, Blogger...