Когда-то давно передо мной стояла интересная задача. Необходимо было реализовать управление PTZ-камерой PTD-208z по протоколу PELCO-D причем сделать это надо было именно на Arduino. Коротко зачем все это нужно: камера крепится на специального робота, который катается внутри большой трубы и рассматривает дефекты, пишется все видео на DVR и плюс есть оператор, который управляет роботом (управление роботом, механика и драйверы двигателей тоже были сделаны, но это тема для другой статьи). С этого проекта, который я закончил буквально на днях и началось мое знакомство с Arduino.
Небольшое описание самого протокола и зачем он нужен.
Pelco-D очень популярный PTZ (Pan/Tilt/Zoom) протокол? используемый в CCTV индустрии. Собственно он используется для управления поворотными камерами, т.е. поворотов и зуммирования. В качестве среды передачи для протокола наиболее часто выступает 485-й интерфейс, т.к. в основном его поддерживают все производимые аналоговые камеры.
Формат Pelco-D посылки выглядит следующим образом:
Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
Sync | Camera Address | Command 1 | Command 2 | Data 1 | Data 2 | Checksum |
- Byte 1 (Sync) — байт синхронизации, по умолчанию имеет значение 0xFF, не изменяем;
- Byte 2 (Camera Address) — логический адрес управляемой камеры (например камера 1 имеет адрес 0x01);
- Byte 3 и 4 (Command 1 и 2) — описаны ниже;
- Byte 5 (Data 1) — байт задает скорость поворота камеры влево/вправо, 0x00 — скорость равна 0, 0x3F — максимально возможная скорость поворота. Некоторые камеры поддерживают «турбо» скорость 0xFF;
- Byte 6 (Data 2) — байт задает скорость движения камеры вниз/вверх, со скоростями все аналогично байту 5;
- Byte 7 (Checksum) — байт контрольной суммы, байт синхронизации не учитывается в контрольной сумме.
Разберем байты 3 и 4 (Command 1 и 2)
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | |
Command 1 | Sense | Reserved | Reserved | Auto / Manual Scan | Camera On/Off | Iris Close | Iris Open | Focus Near |
Command 2 | Focus Far | Zoom Wide | Zoom Tele | Tilt Down | Tilt Up | Pan Left | Pan Right | Fixed to 0 |
НАПРИМЕР:
Pan Left (поворот налево) — 0b00000100 или 0x04 в шестнадцатиричном представлении записываем в байт 4 (Command 2).
Так с протоколом разобрались, едем дальше.
Как же все это сделано на Arduino.
Для начала мы определились с органом управления, он достался нам от другой аналогичной машинки и управляет не только камерой, но и движением робота, а также светом:
Джойстики на пульте управления подключены к плате Arduino UNO к аналоговым входам от A0 до A2. Далее для передачи посылки камере реализовано программное последовательное соединение с помощью библиотеки SoftwareSerial.h, это единственная библиотека, которая используется в этом проекте. Использование ее обусловлено тем, что необходимо иметь связь с ПК и одновременно передавать данные камере.
Для реализации интерфейса RS-485 использовалась микросхема SP485EE. Накидаю небольшую схемку ее расключения:
SP485EE
Далее приведу листинг программы:
/*Программа для управления камерой*/ #include <SoftwareSerial.h> int sensorPan = A0; // канал вправо/влево int sensorTilt = A1; // канал вверх/вниз int sensorFocus = A2; // канал фокус int xVal = 0; // переменная для хранения значения с потенциометра вправо/влево int yVal = 0; // переменная для хранения значения с потенциометра вниз/вверх int focus = 0; // переменная для хранения значения с потенциометра фокуса камеры const byte tiltDown = 0x10; const byte tiltUp = 0x08; const byte panRight = 0x02; const byte panLeft = 0x04; const byte focusNull = 0x00; // Зума нет const byte focusNear = 0x20; // Зум + 0x01 const byte focusFar = 0x40; // Зум - 0x80 SoftwareSerial newSerial = SoftwareSerial(7, 8); void setup() { pinMode(7, INPUT); pinMode(8, OUTPUT); newSerial.begin(2400); // включаем uart } // отправка данных камере // структура посылки Pelco-D /* | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | ---------- ------------ ---------- ---------- -------- -------- -------- |Sync 0xFF | Cam Adress | Command1 | Command2 | Data 1 | Data 2 |Checksum| */ void sendData(byte camNum, byte command_1, byte command_2, byte panSpeed, byte tiltSpeed) { int modSum = 0; // начальное нулевое значение контрольной суммы byte dataVal[6] = {0xFF, camNum, command_1, command_2, panSpeed, tiltSpeed}; // вектор формата комманды Pelco-D for (int i=0; i<6; i++) // цикл отправки посылки камере { newSerial.write(dataVal[i]); // отправить байт в UART if (i > 0) // контрольная сумма начинает считаться со второго байта, поэтому первый 0xFF пропускаем modSum += dataVal[i]; // суммируем байты контрольной суммы } modSum %= 100; // контрольная сумма делиться по модуля на 256 в dec или по другому на 100 в hex newSerial.write(modSum); // отправить контрольную сумму } void halt() { byte dataVal[7] = {0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01}; for (int i=0; i<7; i++) { newSerial.write(dataVal[i]); // отправить байт в UART } } void loop() { xVal = analogRead(sensorPan); // считывание значения потенциометра вправо/влево в соответствующую переменную yVal = analogRead(sensorTilt); // считывание значения потенциометра вниз/вверх в соответствующую переменную focus = analogRead(sensorFocus); // считывание значения потенциометра фокуса камеры в соответствующую переменную if (xVal > 600) { sendData(0x01, focusNull, panRight, 0x3F, 0x00); // вызывается функция отправки байта while (xVal > 600) { xVal = analogRead(sensorPan); } } if (xVal < 450) { sendData(0x01, focusNull, panLeft, 0x3F, 0x00); // вызывается функция отправки байта while (xVal < 450) { xVal = analogRead(sensorPan); } } if (yVal > 600) { sendData(0x01, focusNull, tiltUp, 0x00, 0x3F); // вызывается функция отправки байта while (yVal > 600) { yVal = analogRead(sensorTilt); } } if (yVal < 450) { sendData(0x01, focusNull, tiltDown, 0x00, 0x3F); // вызывается функция отправки байта while (yVal < 450) { yVal = analogRead(sensorTilt); } } if (focus > 600) { sendData(0x01, focusNull, focusNear, 0x00, 0x00); // вызывается функция отправки байта while (focus > 600) { focus = analogRead(sensorFocus); } } if (focus < 450) { sendData(0x01, focusNull, focusFar, 0x00, 0x00); // вызывается функция отправки байта while (focus < 450) { focus = analogRead(sensorFocus); } } halt(); }
Программа максимально проста и не вызовет затруднений даже у новичков. В функции loop() происходит опрос состояний джойстиков, а также вызов функции sendData() с аргументами вида байтов протокола. Функция sendData() посылает посылку камере на лету подсчитывая контрольную сумму. Функция halt() срабатывает когда нету реакции ни одного из джойстиков. Вот и все.
Испытания системы управления камерой прошли успешно) Полностью робот в сборе уже бегает по трубам и снимает камерой все дефекты)))
Очень интересно. Пока что нихрена не понятно, но интересно)) Хочу тоже разобраться с похожим проектом. Нужно сделать ptz камеру на базе arduino. По всей видимости, это будет мой курсач). Спасибо за информацию. Будемс разбираться.
Привет, обсуждение на форуме здесь http://radiotech.kz/threads/realizacija-protokola-pelco-d-na-arduino.74/ и здесь http://radiotech.kz/threads/realizacija-pelco-d-na-mikrokontrollere-atmega8-max485-adm485.696/
у меня не заработало. подключил ардуинку через max к камере, джостики подключил.
вроде переключил на самой камере все переключатели. камера на как не реагирует на мои команды. может кто подскажет что делать? в какое положение переключатели на камере поставить? может я что напортачил?
Привет, для начала вопрос, что за камера? О каких переключателях речь?
ACUMEN ai-sd27 купольная поворотная pzt камера
Не могу по ней найти никакой технической информации, снята с производства и сайт производителя молчит.
на микросхему SP485EE точно 5 вольт на 2 и 3 ножку?
аналогичная камера :
http://acumen.su/instruction%20menu_RU/Ai-SD28.pdf
Точно, это высокий уровень для разрешения на передачу данных через интерфейс RS485.
Адрес установлен 0x01? Совпадает с адресом в программе? 4-секционный переключатель должен быть в положении off off on off для режима Pelco-D со скоростью 2400 бит/с.
Здравствуйте, я немного не понял, Вы вот используете библиотеку SoftwareSerial.h для того, чтобы одновременно передавать сигнал и видеокамере и получать сигнал с пк. С передачей сигнала камере, вроде, понятно, а вот с пк не очень. 8 пин Вы в программе определили как передатчик и сделали его выходом, затем этот выход пошел на микросхему для преобразования UART в RS-485. Затем этот сигнал пошел к камере и это все позволит нам ей управлять. 7 пин у Вас сделан приемником, но он нигде не используется. Так и надо? И как эту систему можно подружить с ПК? И вообще, нужна ли была библиотека, которая дублирует UART на цифровые пины, если UART есть на 0 и 1 пине, а мы используем только два пина? (7 и 8).. Предостерегу себя от фейспалмов и скажу, что я полный новичек в этом всем, и если то, что я написал — полнейший бред, то не серчайте. Просто мне нужно в этом разобраться. Спасибо)
Привет, согласен, в статье не совсем очевидно, что и как работает. SoftwareSerial используется для связи с камерой, 8 пин для передачи команд камере, 7 пин по идее для приема, но от камеры мы ничего не получаем, в данном случае протокол однонаправленный, поэтому не используется, но класс SoftwareSerial требует инициализации обоих пинов. Аппаратный Serial интерфейс может использоваться для отладки в данном случае и он так и использовался, потом из кода отладочные строки были убраны мною, а по идее компьютер здесь особо не нужен и можно не заводить SoftwareSerial, а использовать Serial класс для передачи команд. Код старый и я уже не помню всех тонкостей почему именно используется у меня SoftwareSerial. Удачи, если будут вопросы пишите, постараюсь помочь.
ЗДРАВСТВУЙТЕ, достал схему SP485EEN все подключил как вы, скопировал прогу, в установках камеры задал протокол Pelco-D, скорость 2400, данные 8 бит, четности нет, стоп бит -1, но не работает ;/ (камера Wisenet SNZ6320)
Не могу точно сказать, что в вашем случае не работает. Эта камера не PTZ, в ней только зум моторизированный. Попробуйте на другой скорости протестировать отправку данных. Схема подключения есть у вас?
https://ibb.co/4pqRWV5 . Мне и нужна трансфокация, вместо max использую SP
Добрый день, чтобы отправлять команды на предпозиции, а не просто ездить по сторонам с помощью джойстика, из описания протокола так и не понял какие команды надо передавать в камеру.
Добрый день. Чтобы формировать предпозиции используются, как мне кажется, те же самые команды, только в момент инициализации необходимо выбрать точку начального отсчета координат для движения камеры и уже от этой точки формировать пресеты путем передачи команд для сдвига на соответствующее количество шагов от начала координат.
как я понял предустановки хранятся в камере, а устройство просто посылает номер и все. Из описания протокола расширенные команды (https://umnyjdomik.ru/wp-content/uploads/PELCO-D-Version-2-Revision-1-2003.pdf страница 7 извиняюсь за сторонние ссылки) посылка должна выглядеть так 0xFF, 0x01, 0x00, 0x07, 0x00, 0x01, 0x09 — переход на первую предпозицию поправьте если я что-то не так понял.
Судя по коду, управление не предполагает одновременного перемещения по X,Y и управления фокусировкой.
Что ж так упрощенно? Все камеры и пульты это позволяют, хватило бы рук.
А не подскажите что означает в скетче:
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#else
#error «Camera model not selected»
#endif
#elif директива условной компиляции. Если у вас где то прописан define CAMERA_MODEL_AI_THINKER, то код между #elif и #else в вашем случае компилируется. Конкретно у вас прописаны определения для GPIO, название и соответствующий номер.