- Управление камерой на сервоприводе с помощью Raspberry Pi 2
Управление камерой на сервоприводах с помощью Raspberry Pi 2 и фреймворка WebIOPi
Практика для студентов. Мясищев А.А.
Необходимо создать систему удаленного управления камерой, входящей в состав мини компьютера Raspberry Pi 2 с помощью сервоприводов, вращающих камеру в двух плоскостях. Для управления сервоприводами используется фреймворк WebIOPi, видео поток с камеры на удаленный браузер будет формировать mjpg-streamer. Для ознакомления с материалом предполагается, что была прочитана статья https://sites.google.com/site/webstm32/internet_rozetka. На рисунке 1 показано устройство в сборе.
Рис. 1. Фото камеры на сервоприводах с подключенным к ней Raspberri Pi 2
На рисунке 2 показана картинка с камеры для одного из ее положений и кнопки управления сервоприводами(серые) с индикацией положения(синие). В строку ввода Input step вводится шаг поворота сервопривода в градусах. Рассмотрен самый простой случай - загружены два окна браузера Chrome. Первое окно подключено к web - серверу, который запускается при запуске mjpg-streamer(port 9000). Второе окно подключено к web - серверу фреймворка WebIOPi (port 8000).
Рис.2. Интерфейс при работе с камерой.
Этапы решения задачи:
1. Схема подключений
На рисунке 3 показана схема подключения сервоприводов камеры к Raspberry Pi 2. Целесообразно сервоприводы подключать к отдельному от компьютера источнику питания.
Рис.3. Схема подключения сервоприводов камеры к Raspberry P 2.
2. Установка фреймворка WebIOPi
Предполагается, что на Raspberry Pi 2 установлена операционная система RASPBIAN с IP адресом 192.168.1.38.
Для работы 40 выводов порта GPIO целесообразно установить фреймворк WebIOPi версии 0.7.1:
$ wget http://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz
$ tar xvzf WebIOPi-0.7.1.tar.gz
$ cd WebIOPi-0.7.1
Устанавливаем patch для работы с 40 пинами порта GPIO Raspberry Pi 2:
$wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch
$ patch -p1 -i webiopi-pi2bplus.patch
$ sudo ./setup.sh
Для автоматического старта WebIOPi после перегрузки компьютера необходимо выполнить команду (действительно для образа системы 2015-05-05-raspbian-wheezy.img):
sudo update-rc.d webiopi defaults
Для более поздних версий выполняются следующие команды:
$ cd /etc/systemd/system/
$ sudo wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi.service
$ sudo systemctl start webiopi
$ sudo systemctl enable webiopi
Редактируем конфигурационный файл config:
sudo nano /etc/webiopi/config
Настраиваем порты GPIO 23,24 как выходы с низким стартовым уровнем. В секции [GPIO] записываем:
23 = OUT 0
24 = OUT 0
В секции [SCRIPTS] указываем имя и расположение файла - скрипта на Питоне, необходимый для управления сервоприводами
myproject = /home/pi/myproject/python/script.py
В секции [HTTP] указываем имя и расположение html файла:
doc-root = /home/pi/myproject/html
В секции [REST] прописываем активные порты(только они будут работать):
gpio-export = 23,24
После этого перегружаем Raspberry Pi 2:
sudo reboot
Для входа по адресу http://192.168.1.38:8000 используется логин - "webiopi" , пароль - "raspberry"
3. Настройка фреймворка WebIOPi. Построение файлов сценария на Python и HTML файла с JavaScript
Принимается, что сервоприводы поворачивают камеру Right-Left на угол -90...+90 градусов и Up-Down на угол -90...+90 градусов соответственно. Исходное состояние при старте или рестарте WebIOPi для Right-Left 0 градусов и Up-Down также 0 градусов.
В скрипте scrypt.py используются встроенные в WebIOPi функции:
GPIO.setFunction(SERVO1, GPIO.PWM) - устанавливает режим работы вывода SERVO1, как ШИМ вывод.
GPIO.pwmWriteAngle(SERVO1, 0) - для сервопривода,подключенного к выводу SERVO1 устанавливает угол 0 градусов.
Задержка, формируемая командой webiopi.sleep(0.25) и последующее изменение режима работы вывода SERVO1 (GPIO.setFunction(SERVO1, GPIO.OUT)) необходимы для предотвращения "дрожания" сервопривода (см. принцип работы сервопривода).
Аналогично для вывода SERVO2.
Файл сценария /home/pi/myproject/scrypt.py
# Imports
import webiopi
import datetime
GPIO = webiopi.GPIO
SERVO1=23
SERVO2=24
SERRL = 0 # Angle rotation Right-Left
STE = 0 # Step angle rotation
SERUD = 0 # Angle rotation Up-Down
# setup function is automatically called at WebIOPi startup
def setup():
# set the GPIO used by the light to output
GPIO.setFunction(SERVO1, GPIO.PWM)
GPIO.setFunction(SERVO2, GPIO.PWM)
GPIO.pwmWriteAngle(SERVO1, 0)
GPIO.pwmWriteAngle(SERVO2, 0)
webiopi.sleep(0.25)
GPIO.setFunction(SERVO1, GPIO.OUT)
GPIO.setFunction(SERVO2, GPIO.OUT)
# destroy function is called at WebIOPi shutdown
def destroy():
GPIO.digitalWrite(SERVO1, GPIO.OUT)
GPIO.digitalWrite(SERVO2, GPIO.OUT)
@webiopi.macro
def steps():
global SERRL,STE
return "%d" % SERRL
@webiopi.macro
def steps1():
global SERUD,STE
return "%d" % SERUD
@webiopi.macro
def gets():
global SERRL
return "%d" % STE
@webiopi.macro
def lef(on):
global SERRL,STE
STE = int(on)
GPIO.setFunction(SERVO1, GPIO.PWM)
SERRL=SERRL+STE
if (abs(SERRL)>=90):
SERRL=90
GPIO.pwmWriteAngle(SERVO1, SERRL)
webiopi.sleep(0.25)
GPIO.setFunction(SERVO1, GPIO.OUT)
return gets()
@webiopi.macro
def ri(on):
global SERRL,STE
STE = int(on)
GPIO.setFunction(SERVO1, GPIO.PWM)
SERRL=SERRL-STE
if (abs(SERRL)>=90):
SERRL=-90
GPIO.pwmWriteAngle(SERVO1, SERRL)
webiopi.sleep(0.25)
GPIO.setFunction(SERVO1, GPIO.OUT)
return gets()
@webiopi.macro
def downs(on):
global SERUD,STE
STE = int(on)
GPIO.setFunction(SERVO2, GPIO.PWM)
SERUD=SERUD+STE
if (abs(SERUD)>=90):
SERUD=90
GPIO.pwmWriteAngle(SERVO2, SERUD)
webiopi.sleep(0.25)
GPIO.setFunction(SERVO2, GPIO.OUT)
return gets()
@webiopi.macro
def ups(on):
global SERUD,STE
STE = int(on)
GPIO.setFunction(SERVO2, GPIO.PWM)
SERUD=SERUD-STE
if (abs(SERUD)>=90):
SERUD=-90
GPIO.pwmWriteAngle(SERVO2, SERUD)
webiopi.sleep(0.25)
GPIO.setFunction(SERVO2, GPIO.OUT)
return gets()
HTML файл /home/pi/myproject/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>servo and camera</title>
<script type="text/javascript" src="/webiopi.js"></script>
<script type="text/javascript">
setInterval ("callMacro_steps()", 500);{
}
function callMacro_steps(){
webiopi().callMacro("steps", [], macro_steps_Callback);
}
function macro_steps_Callback(macro, args, data) {
$("#step").text("Right Left: "+data);
}
setInterval ("callMacro_steps1()", 500);{
}
function callMacro_steps1(){
webiopi().callMacro("steps1", [], macro_steps1_Callback);
}
function macro_steps1_Callback(macro, args, data) {
$("#step1").text("Up Down: "+data);
}
webiopi().ready(function() {
// Following function will process data received from set/gets macro.
var update = function(macro, args, response) {
var servo=response;
// Following lines use jQuery functions
$("#inputOn").val(servo);
}
// Immediately call gets macro to update the UI with current values
// "gets" refers to macro name
// [] is an empty array, because gets macro does not take any argument
// update is the callback function, defined above
webiopi().callMacro("gets", [], update);
// Create a button to call lef macro
var sendButton1 = webiopi().createButton("sendButton1", "Left", function() {
// Arguments sent to the macro
var servo = $("#inputOn").val();
// Call the macro
webiopi().callMacro("lef", servo, update);
});
// Append the button to the controls box using a jQuery function
$("#controls").append(sendButton1);
// Create a button to call ri macro
var sendButton2 = webiopi().createButton("sendButton2", "Right", function() {
// Arguments sent to the macro
var servo = $("#inputOn").val();
// Call the macro
webiopi().callMacro("ri", servo, update);
});
// Append the button to the controls box using a jQuery function
$("#controls").append(sendButton2);
//-------------
// Create a button to call downs macro
var sendButton3 = webiopi().createButton("sendButton3", "Down", function() {
// Arguments sent to the macro
var servo = $("#inputOn").val();
// Call the macro
webiopi().callMacro("downs", servo, update);
});
// Append the button to the controls box using a jQuery function
$("#controls").append(sendButton3);
// Create a button to call ups macro
var sendButton4 = webiopi().createButton("sendButton4", "Up", function() {
// Arguments sent to the macro
var servo = $("#inputOn").val();
// Call the macro
webiopi().callMacro("ups", servo, update);
});
// Append the button to the controls box using a jQuery function
$("#controls").append(sendButton4);
//-------------
// Refresh GPIO buttons
// pass true to refresh repeatedly of false to refresh once
webiopi().refreshGPIO(true);
});
</script>
<style type="text/css">
#step, #step1 {
margin: 5px 5px 5px 5px;
width: 160px;
height: 45px;
background-color: Blue;
font-size: 16pt;
font-weight: bold;
color: white;
}
</style>
<style type="text/css">
button {
display: block;
margin: 5px 5px 5px 5px;
width: 160px;
height: 45px;
font-size: 20pt;
font-weight: bold;
color: white;
}
</style>
</head>
<body>
<div align="center">
Input step:<input type="text" maxlength="2" size="2" id="inputOn" /><br/>
<div id="controls"></div>
<div id="step"></div>
<div id="step1"></div>
</div>
</body>
</html>
4. Установка и настройка MJPG-Streamer (https://miguelmota.com/blog/raspberry-pi-camera-board-video-streaming/)
Предположим, наш текущий каталог /home/pi. Создадим каталог:
sudo mkdir /opt/mjpg-streamer
Установим библиотеку libjpeg62-dev:
sudo apt-get install libjpeg62-dev
Устанавливаем cmake:
sudo apt-get install cmake
Загружаем mjpg-streamer с плагином raspicam:
git clone https://github.com/jacksonliam/mjpg-streamer.git ~/mjpg-streamer
Заходим в директорию:
cd ~/mjpg-streamer/mjpg-streamer-experimental
Выполняем компиляцию
make clean all
Перемещаем файлы и удаляем рабочую директорию:
sudo mv ~/mjpg-streamer/mjpg-streamer-experimental /opt/mjpg-streamer
sudo rm -rf ~/mjpg-streamer
Создаем файл /opt/mjpg-streamer/start_stream.sh для старта MJPG-Streamer:
#!/bin/bash if pgrep mjpg_streamer > /dev/null then echo "mjpg_streamer already running" else LD_LIBRARY_PATH=/opt/mjpg-streamer/ /opt/mjpg-streamer/mjpg_streamer -i "input_raspicam.so -fps 10 -q 50 -x 640 -y 480" -o "output_http.so -p 9000 -w /opt/mjpg-streamer/www" > /dev/null 2>&1& echo "mjpg_streamer started"
fi
Создаем файл /opt/mjpg-streamer/stop_stream.sh для останова MJPG-Streamer:
#!/bin/bash
if pgrep mjpg_streamer then kill $(pgrep mjpg_streamer) > /dev/null 2>&1 echo "mjpg_streamer stopped" else echo "mjpg_streamer not running" fi
Таким образом для запуска необходима команда:
/opt/mjpg-streamer/stop_stream.sh
Для останова:
/opt/mjpg-streamer/stop_stream.sh
Просмотреть видеопоток можно, набрав в браузере адрес:
http://192.168.1.38:9000/stream_simple.html
Получим изображение, как в левом окне рисунка 2.
Выводы
1. Сервоприводы чувствительны к броскам напряжения. Например, при включении обогревателя сервоприводы "дергаются" и меняется положение камеры.
2. Замечено "подвисание" WebIOPi при управлении камерой. После рестарта по команде /etc/init.d/webiopi restart, управление камерой возобновляется.
Написана 16.04.2016