树莓派实现云直播系统
原创文章
硬件需要
- 树莓派
- 舵机
- 摄像头
- 拥有公网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 页面,可以显示视频即可。
- 安装 laravel 框架
$ composer create-project laravel/laravel [项目名] --prefer-dist
- 编写相应的控制器
``` 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 服务 文档地址
- 登录公网服务器,搭建 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
- 修改 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 服务端口
- 尝试启动
$ ./frps -c ./frps.ini
- 配置系统服务,将 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 # 设置开机自启动
- 登录公网服务器,搭建 frps 服务
配置 frpc 服务
- 同样下载并解压 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
- 修改 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
- 尝试启动
$ ./frpc -c ./frpc.ini
- 配置系统服务,将 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 # 设置开机自启动
- 同样下载并解压 frp 文件包
Comments