树莓派两自由度云台视频直播搭建

树莓派+motion+laravel+frp搭建直播系统
2022-02-26 22:58:03 1
本文 2235 个字,阅读需要大约 5 分钟
加载失败

文章目录 [回顶部]
树莓派实现云直播系统
硬件需要
运行原理
连接硬件
搭建系统
基础层 ---- python 控制云台转动
1. 安装 python3-rpi.gpio
2. 编写代码,实现使用 python 命令可以直接控制云台
基础层 ---- 安装 motion ,实现本地访问媒体流
基础层 ---- 搭建 web
1. 安装 nginx 环境
2. 安装 php 环境
3. 安装 laravel 框架
应用层 ---- 多服务联调
1. 编写 web 页面,可以查看当前媒体流,并控制云台
2. nginx 配置
3. 内网穿透
搞定!
[--评论--]

树莓派实现云直播系统

原创文章

硬件需要

  • 树莓派
  • 舵机
  • 摄像头
  • 拥有公网ip的服务器

运行原理

服务通过 linux + php + nginx + python 环境实现,整体服务搭建在内网,通过拥有公网 ip 的服务器做 frp 内网映射,将 web 公布在公网上。通过 nginx 的 base auth 做简单认证。流媒体使用 motion 软件呈现。web 页面使用 Linux + php ,云台的控制使用 web 接口,通过接口,将数据发送到 web 端,web 端发送请求到本地的 python 脚本,从而控制云台转动。

连接硬件

(略)

搭建系统

基础层 ---- python 控制云台转动

1. 安装 python3-rpi.gpio
$ sudo apt-get update
$ sudo apt-get install python3-rpi.gpio
2. 编写代码,实现使用 python 命令可以直接控制云台
#!/usr/bin/env python3
#-- coding: utf-8 --
import RPi.GPIO as GPIO
import time
import sys


#设置转动角度
def angle_to_percent_h (angle) :
    if angle > 170 or angle < 10 :
        return False

    start = 4
    end = 12.5
    ratio = (end - start)/180 #计算角度

    angle_as_percent = angle * ratio

    print(angle_as_percent)
    return start + angle_as_percent

def angle_to_percent_s (angle) :
    if angle > 170 or angle < 10 :
        return False

    start = 2
    end = 12.5
    ratio = (end - start)/180 #Calcul ratio from angle to percent

    angle_as_percent = angle * ratio
    print(angle_as_percent)
    return start + angle_as_percent

GPIO.setmode(GPIO.BOARD) #使用 Board 模式
GPIO.setwarnings(False) #禁止掉烦人的警告

# 使用 40 作为水平方向的舵机信号口
pwm_gpio_x = 40
# 使用 38 作为竖直方向的舵机信号口
pwm_gpio_y = 38
frequence = 50
GPIO.setup(pwm_gpio_x, GPIO.OUT)
GPIO.setup(pwm_gpio_y, GPIO.OUT)
h = GPIO.PWM(pwm_gpio_x, frequence)
s = GPIO.PWM(pwm_gpio_y, frequence)

#  接收参数,指定目标角度
x = int(sys.argv[1])
y = int(sys.argv[2])
# x 方向或者 y 方向没有变化,则不调用,防止抖动
if x != -1:
  h.start(angle_to_percent_s(x))
if y != -1:
  s.start(angle_to_percent_h(y))
# 增加睡眠时间,等待舵机转动完成
time.sleep(.5)

# 关闭并清空 gpio 状态
h.stop()
s.stop()
GPIO.cleanup()

预期目标:直接使用命令行的方式可以控制舵机转动到指定位置。以下是转动到 水平 150°竖直 60°的位置 的命令

$ python main.py 150 60

基础层 ---- 安装 motion ,实现本地访问媒体流

motion是Linux下一款开源的摄像头监控软件,用命名行运行,只保存有运动物体的图像。

$ sudo apt update
$ sudo apt install motion

安装完成后最主要的就是配置。主要修改以下配置,重要配置已做备注

$ sudo vim /etc/motion/motion.conf
  • 'daemon' 设置为 on 。 这个是守护进程,当服务挂掉的时候尝试重启,这个很重要一定要开。
  • 'framerate' 设置为 1000 到 1500 中间的一个数值。
  • 'Stream_port' 设置为 8081,这个是本地访问流媒体时候的http端口,本地查看的时候,在浏览器打开 http://127.0.0.1:8081 即可。
  • 'Stream_quality' 设置为 100。流媒体质量。
  • 'Stream_localhost' 设置为 off。这个是 仅本地浏览 的配置,为 off 的时候,局域网或者外网可以访问,on 的时候仅本地(127)可以访问
  • 'webcontrol_localhost' 设置为 off。仅本地可以控制,同上
  • 'quality' 设置为 100。
  • 设置宽高:'width' 、 'height' 分别设置为 640 、 480.
  • 设置'post_capture' to 5.
  • stream_maxrate 设置为 10。即 fps 。** 这个很重要,这个是控制视频流的帧率,越高画面越看起来越流畅,但是消耗的流量更多 **

到了这一步,可以启动 motion 服务了

$ sudo service motion start 

本地浏览器打开 http://127.0.0.1:8081 即可查看当前实时画面

基础层 ---- 搭建 web

1. 安装 nginx 环境
$ sudo apt install nginx
2. 安装 php 环境

这个可以自定义安装,主要是能够实现 php-fpm 功能即可,这个项目不需要数据库和 redis ,所以有些包不是必须的。看自己。

$ sudo apt install -y -t buster php7.3-fpm php7.3-curl php7.3-gd php7.3-intl php7.3-mbstring php7.3-mysql php7.3-imap php7.3-opcache php7.3-sqlite3 php7.3-xml php7.3-xmlrpc php7.3-zip

注意: 由于需要使用 php 执行 shell 命令,所以 php 的 exec 函数需要打开,具体方法可以百度

3. 安装 laravel 框架

应用层 ---- 多服务联调

1. 编写 web 页面,可以查看当前媒体流,并控制云台

项目是用 php 的 laravel 框架,提供一个简单的 web 页面,可以显示视频即可。

  1. 安装 laravel 框架
    $ composer create-project  laravel/laravel  [项目名] --prefer-dist
    
  2. 编写相应的控制器
    ``` php x); $y = (int)$request->y; if ($x == Cache::get('gpio:x')){ $x = -1; }else{ Cache::put('gpio:x', $x); } if ($y == Cache::get('gpio:y')){ $y = -1; }else{ Cache::put('gpio:y', $y); } $command = "/usr/bin/python /home/pi/dev/python/serve/main.py $x $y"; exec($command); return back(); } }
这里主要是接收到传过来的 x、y 变量,对比本地存储的 x、y ,如果 x、y 发生变化,传递给 python 程序,转动舵机。  
3. 编写相应的前台页面
```html
        <body class="antialiased">
        <div
                class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
                @if (Route::has('login'))
                        <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
                                @auth
                                        <a href="{{ url('/home') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Home</a>
                                @else
                                        <a href="{{ route('login') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Log in</a>

                                        @if (Route::has('register'))
                                                <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 dark:text-gray-500 underline">Register</a>
                                        @endif
                                @endauth
                        </div>
                @endif
                <form action="#">
                        <input class="position" id="position_x" type="range" min="10"
                                     value="{{ 170 - \Illuminate\Support\Facades\Cache::get('gpio:x') ?? 10 }}"
                                     max="170"
                                     step="1" name="x">
                        <input class="position" id="position_y" type="range" min="10"
                                     style="transform: rotate(-90deg);position: absolute;left: -50px;top: 360px"
                                     value="{{ \Illuminate\Support\Facades\Cache::get('gpio:y') ?? 10 }}" max="170"
                                     step="1" name="y">
        {{--        <button type="submit">submit</button>--}}
                </form>
                <div style="width: 100%">
                        <iframe width="100%" height="640px" src="/stream" frameborder="0"></iframe>
                </div>
        </div>
        <script src="https://lib.sinaapp.com/js/jquery/2.0/jquery.min.js"></script>
        <script>
                $(".position").change(function(){
                        let data = {
                                x: $("#position_x").val(),
                                y: $("#position_y").val(),
                        }
                        $.ajax({
                                //请求方式
                                type : "get",
                                //请求的媒体类型
                                contentType: "application/json;charset=UTF-8",
                                //请求地址
                                url : "/change",
                                //数据,json字符串
                                data : data,
                                //请求成功
                                success : function(result) {
                                        console.log(result);
                                },
                                //请求失败,包含具体的错误信息
                                error : function(e){
                                        console.log(e.status);
                                        console.log(e.responseText);
                                }
                        });
                })
        </script>
        </body>
        ```
4. 使用 base auth 认证
```php  
        <?php

        use Illuminate\Support\Facades\Route;

        /*
        |--------------------------------------------------------------------------
        | Web Routes
        |--------------------------------------------------------------------------
        |
        | Here is where you can register web routes for your application. These
        | routes are loaded by the RouteServiceProvider within a group which
        | contains the "web" middleware group. Now create something great!
        |
        */
        Route::group(['middleware' => 'auth.basic'], function () {
                Route::get('/', function () {
                        return view('welcome');
                });
                Route::get('change','CaptureController@capture');
        });

使用 auth.basic 中间件进行 base auth 认证,前提是需要往 user model 中插入相应的用户。之后访问组内的路由的时候,就会自动弹出 base auth 认证
【base auth 认证图片】

2. nginx 配置
    server {
        listen 8082 default_server;

        root /home/pi/dev/php/home/public;

        index index.html index.htm index.nginx-debian.html;

        server_name _;
        limit_rate 1k; # 最大传输速率
        limit_rate_after 20m; # 超过20m限制传输速率
        location / {
            proxy_pass http://127.0.0.1:8081;

        }

        location /stream {
            auth_basic "HTTP Basic Authentication";
            auth_basic_user_file /etc/nginx/conf.d/htpasswd/.htpasswd;
        proxy_pass http://127.0.0.1:8000;
        }

    }

这里 nginx 和 php 的关系,并没有使用传统的 nginx + php sock 的形式(虽然这种比较常见),而是直接使用 php artisan serve 启动一个 laravel 服务,然后 nginx 代理到这个端口上。
laravel 服务启动:

   $ php /path/to/your/project/artisan serve --port=8081 --host=127.0.0.1

使用 8081 端口,仅启动本地服务。
nginx 和 motion 的关系,同 php ,也是 nginx 做反向代理。

location /stream {
                auth_basic "HTTP Basic Authentication";
                auth_basic_user_file /etc/nginx/conf.d/htpasswd/.htpasswd;
                proxy_pass http://127.0.0.1:8000;
}

这里 /steam 项目使用 nginx 的 base auth 认证。认证方式如下:
首先安装 apache2-utils 工具

$ apt-get install apache2-utils

生成 base auth 认证的用户名和密码

$ htpasswd -c /etc/nginx/.htpasswd admin

输入以上命令后,会创建 admin 用户,并提示输入密码。输入完成后,按照上面 /stream 模块配置 base auth 认证即可

3. 内网穿透

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
frp的github
通过有公网 ip 的服务器,做 frps 服务器,将内网的服务穿透到公网上。

  • 搭建 frps 服务 文档地址

    1. 登录公网服务器,搭建 frps 服务
        $ wget https://github.com/fatedier/frp/releases/download/v0.39.1/frp_0.39.1_linux_amd64.tar.gz
        $ tar -xf frp_0.39.1_linux_amd64.tar.gz
      
    2. 修改 frps.ini 配置文件
        [common]
      bind_port = 7000 #  frps 进程端口,客户端连接这个端口
      token = xxxxxxxxxxxxxxxxxxxx
      # console or real logFile path like ./frps.log
      log_file = /var/log/frp/frps.log # 日志文件
      log_level = debug # 日志等级
      vhost_http_port = 7080 # web 服务端口
      
    3. 尝试启动
        $ ./frps -c ./frps.ini
      
    4. 配置系统服务,将 frps 配置为系统服务
        $ sudo cp systemd/frps.service /lib/systemd/system/frps.service
        $ sudo systemctl daemon-reload # 重载系统服务
        $ sudo service frps start # 启动 frps 服务
        $ sudo systemctl enable frps.service # 设置开机自启动
      
  • 配置 frpc 服务

    1. 同样下载并解压 frp 文件包
       $ wget https://github.com/fatedier/frp/releases/download/v0.39.1/frp_0.39.1_linux_amd64.tar.gz
       $ tar -xf frp_0.39.1_linux_amd64.tar.gz
      
    2. 修改 frpc.ini 配置文件
      [common]
      # frps 的服务器地址
      server_addr = 1.1.1.1 
      # frps 服务器端口
      server_port = 7000 
      # frps 设置的token
      token = xxxxxxxxxxx 
      # 配置一个 ssh 服务
      [pi_ssh] 
      type = tcp
      local_ip = 127.0.0.1
      local_port = 22
      remote_port = 7122
      # 配置一个静态文件服务
      [pi_static_file]
      type = tcp
      remote_port = 7121
      plugin = static_file
      # 要对外暴露的文件目录
      plugin_local_path = /home/pi/Pictures/cron
      # 用户访问 URL 中会被去除的前缀,保留的内容即为要访问的文件路径
      plugin_strip_prefix = static
      plugin_http_user = xxxxx
      plugin_http_passwd = xxxxx
      # 配置 vnc 服务
      [pi_vnc]
      type = tcp
      local_ip = 127.0.0.1
      local_port = 5900
      remote_port = 7159
      # 重点!配置一个 web 服务,将本系统映射到公网上
      [pi_camera]
      type = http
      # 本地端口
      local_port = 8082
      #  域名
      custom_domains = xxxx.xxx.xxx
      
    3. 尝试启动
        $ ./frpc -c ./frpc.ini
      
    4. 配置系统服务,将 frpc 配置为系统服务
        $ sudo cp systemd/frpc.service /lib/systemd/system/frpc.service
        $ sudo systemctl daemon-reload # 重载系统服务
        $ sudo service frpc start # 启动 frps 服务
        $ sudo systemctl enable frpc.service # 设置开机自启动
      

搞定!

原创文章,转载请注明出处~ 以上就是本文的全部内容啦,有什么疑问欢迎在下方评论区留言嗷,收到通知会及时回复~
文章浏览总量:872 (非即时 )
Comments
Nothing...
推荐文章 使用余弦向量算法进行推荐(分数)
跳转到顶部