使用树莓派制作家庭监控,实现摄像头和舵机控制功能。
开发硬件
树莓派,USB免驱摄像头,舵机云台
开发环境
操作系统:raspbian
IDE:Qt creator
舵机控制:gpio
开发步骤
主要分为摄像头驱动部分,舵机驱动部分和界面显示部分,摄像头部分优先采用opencv的videoio库,实时采集摄像头数据显示,在移植opencv过程中,highgui库和videoio库和Qt冲突,方法2摄像头采用mjpg-streamer进行图像采集,方法3使用VLC直接进行图像采集。使用树莓派的GPIO接口进行舵机控制,采用PWM控制舵机旋转角度。舵机采用舵机云台,能提供左右转动和上下转动,以此调整摄像头角度。界面显示部分采用Qt设计,增加上下左右四个按钮和一个控制摄像头开关按钮。
1、界面显示
2、舵机驱动
舵机需要采用PWM控制,所以需要接入GPIO的PWM引脚,详细参考树莓派GPIO接口说明图。
3、摄像头显示
使用mjpg显示
配置mjpg,mjpg支持多种摄像头数据源,我们使用的是USB免驱摄像头,使用UVC作为数据源格式,根据命令行启动摄像头,完成shell文件。
显示效果
使用VLC显示
配置VLC采集数据源
显示效果
4、主类代码
#include "Camera.h"
#include <QPushButton>
#include <QBoxLayout>
#include <QDebug>
#include <QProcess>
#include <QMessageBox>
#include <QApplication>
#include "wiringPi.h"
#define SERVO1_PIN 1
#define SERVO2_PIN 23
#define BTNSIZE 80
#define DELTAANGLE 1
#define AUTOREPEATINTERVAL 50
Camera::Camera(QWidget *parent)
: QDialog(parent),
m_servo1_angle(40),
m_servo2_angle(120),
m_started(false)
{
this->setMinimumSize(200, 200);
this->setWindowFlag(Qt::WindowStaysOnTopHint, true);
this->setWindowIcon(QIcon(":/home-camera.ico"));
QPushButton* up_btn = new QPushButton(this);
up_btn->setFixedSize(BTNSIZE, BTNSIZE);
up_btn->setText("up");
up_btn->setAutoRepeat(true);
up_btn->setAutoRepeatInterval(AUTOREPEATINTERVAL);
connect(up_btn, &QPushButton::clicked, this, &Camera::OnUpBtnClicked);
QPushButton* left_btn = new QPushButton(this);
left_btn->setFixedSize(BTNSIZE, BTNSIZE);
left_btn->setText("left");
left_btn->setAutoRepeat(true);
left_btn->setAutoRepeatInterval(AUTOREPEATINTERVAL);
connect(left_btn, &QPushButton::clicked, this, &Camera::OnLeftBtnClicked);
QPushButton* right_btn = new QPushButton(this);
right_btn->setFixedSize(BTNSIZE, BTNSIZE);
right_btn->setText("right");
right_btn->setAutoRepeat(true);
right_btn->setAutoRepeatInterval(AUTOREPEATINTERVAL);
connect(right_btn, &QPushButton::clicked, this, &Camera::OnRightBtnClicked);
QHBoxLayout* mid_h_layout = new QHBoxLayout();
mid_h_layout->addWidget(left_btn);
mid_h_layout->addWidget(right_btn);
QPushButton* down_btn = new QPushButton(this);
down_btn->setFixedSize(BTNSIZE, BTNSIZE);
down_btn->setText("down");
down_btn->setAutoRepeat(true);
down_btn->setAutoRepeatInterval(AUTOREPEATINTERVAL);
connect(down_btn, &QPushButton::clicked, this, &Camera::OnDownBtnClicked);
QPushButton* mjpg_start_stop_btn = new QPushButton(this);
mjpg_start_stop_btn->setFixedHeight(50);
mjpg_start_stop_btn->setText("mjpg start/stop");
connect(mjpg_start_stop_btn, &QPushButton::clicked, this, &Camera::OnMjpgStartStopBtnClicked);
QPushButton* vlc_start_stop_btn = new QPushButton(this);
vlc_start_stop_btn->setFixedHeight(50);
vlc_start_stop_btn->setText("vlc start/stop");
connect(vlc_start_stop_btn, &QPushButton::clicked, this, &Camera::OnVLCStartStopBtnClicked);
QVBoxLayout* v_layout = new QVBoxLayout(this);
v_layout->addWidget(up_btn, 0, Qt::AlignCenter);
v_layout->addLayout(mid_h_layout);
v_layout->addWidget(down_btn, 0, Qt::AlignCenter);
v_layout->addWidget(mjpg_start_stop_btn);
v_layout->addWidget(vlc_start_stop_btn);
this->setLayout(v_layout);
InitWiringPi();
RotateServoAngle(e_first_servo, m_servo1_angle);
RotateServoAngle(e_second_servo, m_servo2_angle);
}
Camera::~Camera()
{
}
void Camera::InitWiringPi(
) {
wiringPiSetup();
InitServo1();
InitServo2();
}
void Camera::InitServo1(
) {
pinMode(SERVO1_PIN, PWM_OUTPUT);
pwmSetMode(PWM_MODE_MS);
pwmSetRange(2000);
pwmSetClock(192);
}
void Camera::InitServo2(
) {
pinMode(SERVO2_PIN, PWM_OUTPUT);
pwmSetMode(PWM_MODE_MS);
pwmSetRange(2000);
pwmSetClock(192);
}
void Camera::RotateServoAngle(
const ServoIndex& servo_index,
const int& angle
) {
int pin_num = -1;
if (e_first_servo == servo_index) {
pin_num = SERVO1_PIN;
} else if (e_second_servo == servo_index) {
pin_num = SERVO2_PIN;
}
int value = static_cast<int>(100 + 600.f / 180.f * angle);
qDebug() << pin_num << ":" << angle << "-" << value;
pwmWrite(pin_num, value);
}
void Camera::OnLeftBtnClicked() {
m_servo1_angle += DELTAANGLE;
if (m_servo1_angle >= 180) {
m_servo1_angle = 180;
}
RotateServoAngle(e_first_servo, m_servo1_angle);
}
void Camera::OnRightBtnClicked() {
m_servo1_angle -= DELTAANGLE;
if (m_servo1_angle <= 0) {
m_servo1_angle = 0;
}
RotateServoAngle(e_first_servo, m_servo1_angle);
}
void Camera::OnUpBtnClicked() {
m_servo2_angle -= DELTAANGLE;
if (m_servo2_angle <= 50) {
m_servo2_angle = 50;
}
RotateServoAngle(e_second_servo, m_servo2_angle);
}
void Camera::OnDownBtnClicked() {
m_servo2_angle += DELTAANGLE;
if (m_servo2_angle >= 146) {
m_servo2_angle = 146;
}
RotateServoAngle(e_second_servo, m_servo2_angle);
}
void Camera::OnMjpgStartStopBtnClicked(
) {
if (!m_started) {
QProcess* process = new QProcess(this);
process->start("bash /home/pi/study/camera/start_mjpg_monitor_uvc.sh");
m_started = true;
QMessageBox::information(this, "info", "start", QMessageBox::Ok);
} else {
QProcess* process = new QProcess(this);
process->start("sudo killall mjpg_streamer");
m_started = false;
QMessageBox::information(this, "info", "stop", QMessageBox::Ok);
}
}
void Camera::OnVLCStartStopBtnClicked(
) {
if (!m_started) {
QProcess* process = new QProcess(this);
process->startDetached("vlc-wrapper v4l2:///dev/video0");
m_started = true;
QMessageBox::information(this, "info", "start", QMessageBox::Ok);
} else {
QProcess* process = new QProcess(this);
process->start("sudo killall vlc");
m_started = false;
QMessageBox::information(this, "info", "stop", QMessageBox::Ok);
}
}