Вот такое замудреное название, а на самом деле хотел поделиться опытьм создания поворотного механизма как, например, на камерах видеонаблюдения. То есть организовать вращение в вертикальной и горизонтальной плоскостях. Это позволит нам смотреть вверх, вниз и в стороны, что достаточно для подного обзора. Делать это будут 2 сервомотора. Так же будет присутствовать кнопка сброса, т.е. по ее нажатию сервомоторы вернуться в начальное положение. Всю базу мы возьмем из предыдущего поста, но несколько усовершенствуем его.
Итак, в предыдущей статье сервомотор двигался в зависимости от положения стика джойстика. Принцип похож на симулятор водителя от первого лица, но нас это не устраивает. Если подобный механизм установить на квадрокоптер, то мы можем смотреть только вперед и иногда поворачивать голову в каком-то направлении. Да, так же ведет себя пилот. Но мы же углубляемся в робототехнику! Нам надо, что бы после того, как стик джойстика вернулся в центр, то наша камера осталась в повернутом положении. Это позволит нам лететь вперет, а спимать что-то сбоку. Но это не все. Наверняка Вы замечали в компьютерных играх, чем сильнее отклоняешь стик джойстика, тем больше скорость у машины, например. Так вот, в нашем случае мы просто сделаем несколько этапов скорости, что позволит нам более точно позиционировать серву, но при этом будет возможность быстрого ее вращения.
Еще у нас запланирована кнопка возврата на начальное положение. Эта кнопка будет просто позиционировать оба сервомотора в какую-то заданную позицию. И, что бы казаться совсем профессионалами, повесим эту кнопку на прерывание Arduino, что бы она могла работать в любой момент.
Итак, в предыдущей статье сервомотор двигался в зависимости от положения стика джойстика. Принцип похож на симулятор водителя от первого лица, но нас это не устраивает. Если подобный механизм установить на квадрокоптер, то мы можем смотреть только вперед и иногда поворачивать голову в каком-то направлении. Да, так же ведет себя пилот. Но мы же углубляемся в робототехнику! Нам надо, что бы после того, как стик джойстика вернулся в центр, то наша камера осталась в повернутом положении. Это позволит нам лететь вперет, а спимать что-то сбоку. Но это не все. Наверняка Вы замечали в компьютерных играх, чем сильнее отклоняешь стик джойстика, тем больше скорость у машины, например. Так вот, в нашем случае мы просто сделаем несколько этапов скорости, что позволит нам более точно позиционировать серву, но при этом будет возможность быстрого ее вращения.
Еще у нас запланирована кнопка возврата на начальное положение. Эта кнопка будет просто позиционировать оба сервомотора в какую-то заданную позицию. И, что бы казаться совсем профессионалами, повесим эту кнопку на прерывание Arduino, что бы она могла работать в любой момент.
Тонкости
У нас все как и ранее - сервомотор (в наличии, к сожалению, пока только один) подключен к пину D9, джойстик отдает только позицию X и подключен к A0. Тонкость в подключении кнопки - она подключена к линии в 5В через подтягивающий резистор на 100 кОм (под рукой другого не было), так мы всегда получаем напряжение в 5В на кнопке. Сигнал от кнопки идет на вход инвертирующего тригера Шмитта, а уже выходной сигнал подается на пин D2, который поддерживает прерывания.Альтернатива на подумать
Кнопка - это хорошо, но зачем использовать отдельную кнопку, когда на самом джойстике есть своя кнопка? Этот вопрос я обдумываю, поскольку на данный момент эта кнопка постоянно вызывает прерывание и не дает нормально вращать серву.Фотоотчет
Не было большого триггера Шмитта, пришлось припаять провода и контакты, что бы можно было воспользоваться тем, какой есть |
Скетч
/** * Вращение сервомотора под управлением джойстика */ #include <Servo.h> #define SERVO_START_ANGLE 90 // Начальный угол сервы #define SERVO_DEFAULT_ROTATE_SPEED 1 // Скорость вращения по умолчанию #define SERVO_MAX_LIMIT 180 // Максимальный угол поворота сервы #define SERVO_MIN_LIMIT 15 // Минимальный угол поворота сервы #define SERVO_SPEED_COUNT 5 // Количество скоростей поворота сервы (итератор) Servo servo; // Сервомотор int servoPin = 9; // Пин для сервы int ledPin = 13; // Пин светодиода (здесь встроеный светодиод) //int switchPin = 2; // Пин кнопки джойстика int xPin = A0; // Пин для считывания по оси X //int yPin = A1; // Пин для считывания оси Y int resetBtnPin = 2; // Пин для кнопки сброса int valPosX = 0; // Текущее положение X int valServoAngle = 0; // Текущий угол сервы int valCenterX = 0; // Значение центра X (стик по центру) int valServoSpeed = 1; // Скорость вращения сервы int arrLimits[SERVO_SPEED_COUNT]; // Соответствие ограничений по уровню скорости void setup() { Serial.begin(9600); // Ставим соответствующие атрибуты для пинов pinMode(ledPin, OUTPUT); pinMode(xPin, INPUT); pinMode(resetBtnPin, INPUT); // Добавляем прерывание attachInterrupt(digitalPinToInterrupt(resetBtnPin), resetServoPosition, RISING); // подцепляем серву servo.attach(servoPin); // Ждем секунду delay(1000); // Ставим положение сервы на 90 градусов // т.к. джойстик по умолчанию стоит по центру // (вообще не обязательная команда) valServoAngle = SERVO_START_ANGLE; servo.write(valServoAngle); // Установим начальную скорость вращения сервы valServoSpeed = SERVO_DEFAULT_ROTATE_SPEED; // Читаем текущее положение X valCenterX = analogRead(xPin); // Подсчитываем ограничения для уровней скорости calculateLimits(); // Моргнем 4 раза, сказав, что мы закончили с первоначальными настройками // За это время серва точно доберется до положения в 90 градусов for (int i = 0; i < 4; ++i) { digitalWrite(ledPin, HIGH); delay(200); digitalWrite(ledPin, LOW); delay(200); } } /** * Бесконечный цикл */ void loop() { // Считываем положение джойстика по X int xVal = analogRead(xPin); // Убедимся, что значение в интервале от 0 до 1023 xVal = constrain(xVal, 0, 1023); // Проверяем, что разность от центра до положения больше 5 if (abs(valCenterX - xVal) > 5) { // В таком случае начинаем вращение // Вычислим скорость вращения // Возьмем абсолютную величину по X int absX = abs(valCenterX - xVal); // Устанавливаем скорость вращения valServoSpeed = getServoSpeed(absX); // Множитель. Принимает значения 1 или -1 int mult = (valCenterX - xVal > 0) ? 1 : -1; // Вычисляем значение конечной позиции valServoAngle += valServoSpeed * mult; // Проверяем, не превысили ли лимит valServoAngle = constrain(valServoAngle, SERVO_MIN_LIMIT, SERVO_MAX_LIMIT); // Поворачиваем серву servo.write(valServoAngle); // Ждем 15 милисекунд, что бы серва успела повернуться delay(15); } } /** * Вычисление огланичений для уровней скорости * * Эта функция является частью оптимизации и позволяет сократить объем используемой памяти */ void calculateLimits() { // Вычислим количество шагов в интервале наклона стика int stepCount = valCenterX / SERVO_SPEED_COUNT; // Заполняем массив ограничений for (int i = 0; i < SERVO_SPEED_COUNT; i++) { arrLimits[i] = stepCount * (i + 1); } // У последнего значения добавим 1, что бы попало значение максимального наклона стика arrLimits[SERVO_SPEED_COUNT - 1] = valCenterX + 1; } /** * Функция возвращает уровень скорости в зависимости от значения наклона стика */ int getServoSpeed(int absX) { // Цикл по всем ограничениям for (int i = 0; i < SERVO_SPEED_COUNT; i++) { // Минимальная граница int min = (i == 0) ? 0 : arrLimits[i - 1]; // Максимальная граница int max = arrLimits[i]; // Если значение попадает в этот интервал, то это наш уровень скорости if (absX >= min && absX < max) { return i + 1; } } // Если ничего не получилось, то скорость вращения по умолчанию return SERVO_DEFAULT_ROTATE_SPEED; } /** * Сброс позиции сервы на начальный угол */ void resetServoPosition() { // Вращаем серву servo.write(SERVO_START_ANGLE); }
Комментариев нет:
Отправить комментарий