树莓派制作家庭实时监控

/ 0评 / 0

使用树莓派制作家庭监控,实现摄像头和舵机控制功能。

开发硬件

树莓派,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);
    }
}