Stereopi + WebRTC =在家进行远程呈现

首先,请使用youtube视频以获取灵感:


警告:视频中的项目只是一个示例,可以根据文章中有关立体视觉和“头转弯”的教程进行操作。 不包含带有xbox遥控器的坦克。

尽管很清楚,但该项目的作者在视频上的故事很短,链接的可用性也很容易,要立即弄清一切的工作原理并不容易。 如果您想以更方便的格式收集类似内容,建议阅读。

*我将立即进行预订,YouTube视频中的那个人对我来说并不陌生,我没有传输任何秘密数据,我不知道他现在的项目状态如何。
**我们不会通过xbox的远程控制来控制机器人在树莓派表面上的移动,我们可以自行处理。
***请不要扔硬皮鞋,因为该项目仍在开发中。

因此,我们对两件事感兴趣:

  • 如何在头盔上的手机上获取立体声图片;
  • 如何通过转动头来控制伺服器。

如果总结,视频中使用的概念如下所示:

  • 2树莓派通过webrtc服务将视频流从其摄像机发送到网络;
  • 手机(位于头盔中)接受手机上2个相同应用程序中的流-浮动应用程序。
  • 同时,电话控制连接到树莓派的服务器。

一切都很简单。 但是,如您所知,暗黑破坏神存在于细节和不便之处,即:

  • 您需要运行2个树莓派,监视2个摄像机的设置,电源树莓派* 2。
  • 浮动应用程序不断在手机中滑动,您必须在屏幕上对齐图片。
  • ...

因此,我们将转移到stereopi,幸运的是,它已经出现在俄罗斯的商店中(我希望它在这篇文章发布后不会消失):



Stereopi是我们同胞的发展,他现在正在积极征服市场。

它的魅力源于名称-您可以同时连接2个CSI树莓派摄像机。 同时,所有这些工作都基于一个树莓派计算模块。 不幸的是,模块本身不包含在包装中;您必须自己购买。

关于stereopi,有关于哈布雷的文章。

从中,我们将需要2个视频流并通过GPIO控制服务器。
作为stereopi的基础,我们将使用Raspberry Pi计算模块3+。

立体视准备


组装了stereopi(将计算模块插入stereopi,照相机)后,填写软件。

我们将现成的图像用于树莓派计算模块-Raspbian(拉伸)。 它可在stereopi.com上获得-Raspbian Stretch OpenCV图像,Google云端硬盘
装满覆盆子。

如果在填充方面有困难,请其他人去wiki立体声

安装webrtc。


在stereopi上安装webrtc软件。 安装材料部分取自该页面: ARM(Raspberry Pi)的安装


我们将避免上面页面中已经存在的不必要注释,而只需安装所需的所有内容。

curl http://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | sudo apt-key add - sudo nano /etc/apt/sources.list deb http://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main sudo apt-get update sudo apt-get install uv4l uv4l-raspicam sudo apt-get install uv4l-raspicam-extras sudo raspi-config  Anvanced Options  Memory Split   256   enter sudo apt-get install uv4l-server uv4l-uvc uv4l-xscreen uv4l-mjpegstream uv4l-dummy uv4l-raspidisp sudo apt-get install uv4l-webrtc sudo apt-get install uv4l-demos sudo apt-get install uv4l-xmpp-bridge sudo apt-get install uv4l-raspidisp-extras 

现在,您需要(按照说明进行操作)生成ssl密钥,因为Chrome可能无法通过http连接(仅通过https)显示视频:

 openssl genrsa -out selfsign.key 2048 && openssl req -new -x509 -key selfsign.key -out selfsign.crt -sha256 

*生成密钥时,将询问有关公司,地区等的问题。 -您可以任意回答。

生成的密钥(selfsign.key和selfsign.crt将出现在当前文件夹中)应放在以下文件夹中:

 /etc/ssl/private/ 

所有webrtc设置都存储在2个文件中:

 /etc/uv4l/uv4l-raspicam.conf /etc/uv4l/uv4l-raspidisp.conf 

为了避免累赘需要取消注释的文件中的项目,或者
要修复,请使用uv4l-raspicam.conf和uv4l-raspidisp.conf覆盖设置文件。

重新启动树莓派并使用chrome登录到IP树莓派:

 https://192.168.1.100:8080 

WebRTC极力支持各种可能性,但我们将只限于一件事-我们将转到webrtc选项卡:

图片


现在,检查视频是否与stereopi兼容。

按下手机网页底部的“通话”按钮。

图片


将会出现立体摄像机的视频。

单击带有网络摄像机图像的窗口下方的“全屏”按钮:

图片


*不要在手机上重新加载页面! 如果仍然发生这种情况,则需要终止树莓派上的进程:

 sudo killall uv4l 

并重新启动它的服务:

 sudo service uv4l_raspidisp restart sudo service uv4l_raspicam restart 

然后,再次在手机浏览器的页面上点击“通话”。
**如果没有相机连接到覆盆子,则通话将无法进行。

我们将处理伺服器。


要通过手机管理树莓派上的舵机,您需要一个代码,该代码将在树莓派上运行并在手机上执行操作。

但首先,让我们决定伺服器。 YouTube视频使用直接连接到gpio树莓派的伺服器。 由于伺服器是低功率的,您可能可以在gpio树莓派上挂2个伺服器。 这些技巧很容易在sg-90服务器上执行。 它们对营养的要求不高,但对负载的要求也不是特别好。 原则上,它们应该足以容纳带有stereopi的两个摄像机的悬架。 万向节本身可以在同一速卖通上购买,以进行“平移-倾斜”搜索。 但是,这些伺服器也有一个严重的缺点-它们“恐惧震颤”。 YouTube视频的作者可以观察到这种效果。 为什么会发生这种情况以及如何处理它在这里将不予考虑。

在我们的案例中,使用了mg-996n伺服器和机器人关节,我希望他在不久的将来不再需要。

图片


* Mg-996N不“高音”。

Stereopi的gpio布局类似于树莓3上的标准。

因此,来自伺服器的信号线将进入gpio,最好不要从树莓派获取5V,而是从侧面获取5V,将GND serv与GND树莓派和外部电源的GND结合在一起。

现在最重要的是软件


在树莓上,我们需要一个恶魔,而不是莱蒙托夫的恶魔,而是piggio。 您无需采取任何特殊步骤来配置它,主要是要知道它挂在端口8888上,您必须首先运行它:

 sudo systemctl start pigpiod.service 

接下来,创建一个文件来管理服务器,并从套接字本身接收的套接字中接收数据:

datachannel_server_tele.py
 # python 3 # Taken from: # https://stackoverflow.com/questions/45364877/interpreting-keypresses-sent-to-raspberry-pi-through-uv4l-webrtc-datachannel # based on: # https://raspberrypi.stackexchange.com/questions/29480/how-to-use-pigpio-to-control-a-servo-motor-with-a-keyboard # public domain # systemctl status pigpiod.service # sudo systemctl start pigpiod.service # goto http://raspberrypi:8080/stream/webrtc and press Call! # video from raspberry pi appear # run from cmd raspberry: sudo python3 datachannel_server.py # turn on gps on phone # put V on 'send device orientation' from phone import socket import time import pigpio import os import re import json socket_path = '/tmp/uv4l.socket' try: os.unlink(socket_path) except OSError: if os.path.exists(socket_path): raise s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) ROLL_PIN = 4 #gpio PITCH_PIN = 17 #gpio ! not phisical pin YAW_PIN = 15 MIN_PW = 1000 # 0 degree MID_PW = 1500 # 90 degree MAX_PW = 2000 # 180 degree print ('socket_path: %s' % socket_path) s.bind(socket_path) s.listen(1) def cleanup(): pi.set_servo_pulsewidth(ROLL_PIN, 0) pi.set_servo_pulsewidth(PITCH_PIN, 0) pi.set_servo_pulsewidth(YAW_PIN, 0) pi.stop() while True: print ('awaiting connection...') connection, client_address = s.accept() print ('client_address %s' % client_address) try: print ('established connection with', client_address) pi = pigpio.pi() #pi = pigpio.pi('soft', 9080) rollPulsewidth = MID_PW pitchPulsewidth = MID_PW yawPulsewidth = MID_PW pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth) pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth) pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth) while True: try: data = json.loads(connection.recv(200).decode('utf-8')) # dict except ValueError: # no data return continue # data #{"do":{"alpha":0.1,"beta":-0.3,"gamma":-0.2,"absolute":false}, # "dm":{"x":0,"y":0,"z":-0.2,"gx":0,"gy":0,"gz":-9.6,"alpha":-0.1,"beta":-0.1,"gamma":0.1} #print ('received message"%s"' % data) #print ('received message"%s"' % data['dm']['x']) # coordinate x from data #print ('received message"%s"' % data['dm']['y']) # coordinate y from data time.sleep(0.01) key1 = float(data['do']['alpha']) # os x 0 to 360 degree #key2 = float(data['do']['beta']) # os y #print(key1) #print(key2) rollPW = rollPulsewidth pitchPW = pitchPulsewidth yawPW = yawPulsewidth pitchPW = key1*5+500 print ('x: '+str(pitchPW)) #if pitchPW > MAX_PW: # pitchPW = MAX_PW #elif pitchPW < MIN_PW: # pitchPW = MIN_PW #rollPW = int(key2 + 1000) #print ('y: '+ str(int(rollPW))) #if rollPW > MAX_PW: # rollPW = MAX_PW #elif rollPW < MIN_PW: # rollPW = MIN_PW if rollPW != rollPulsewidth: rollPulsewidth = rollPW pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth) if pitchPW != pitchPulsewidth: pitchPulsewidth = pitchPW pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth) if yawPW != yawPulsewidth: yawPulsewidth = yawPW pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth) #if data: #print ('echo data to client') #connection.sendall(str(data)) #else: #print ('no more data from', client_address) #break finally: # Clean up the connection cleanup() connection.close() 


文本中留有注释,以了解代码的来源以及可以更正的其他内容。
该代码的一般含义如下:

  • 在开始时,将伺服器设置在中间位置。
  • 伺服器的信号线悬挂在3个引脚(gpio)上。 在我们的例子中,有2个引脚(暂停2个舵机)。
  • 通过施加1000至2000范围内的PWM信号来控制gpio。
  • 一条线从电话到达,由json解析(您可以做其他事情),然后从中获取值x和y。 此外,将这些值转换为PWM值以转动伺服机构。

*问题是x的取值范围是0到360(电话绕其轴旋转),例如y。 并且这些值必须绑定到PWM,PWM的值从1000到2000。代码使用公式pitchPW = key1 * 5 + 500。 500是最小PWM伺服值(尽管在代码中假设为1000)。 而乘以5是有条件的。 这一点需要改进,因为在x = 360时,PWM值比最大值高很多倍。 为了避免损坏,可以防止伺服超过最大旋转角度,但这不是很令人满意。

在raspberry终端中运行代码:

 sudo python3 datachannel_server_tele.py 

我们将打开手机上的GPS(每个手机的设置中都有一个对应的图标),然后通过ip raspberry。

 https://192.168.1.100:8080/stream/webrtc 

点击“通话”。 建立连接后,在页面上浏览器的电话上,检查“发送设备方向角alpha,beta,gamma”。

x值将随脚本一起到达终端。 而且,如果您旋转手机,它们将会改变。
Servos也将移动。

*当前其中之一(第二个已被注释掉)。

webrtc的好处还包括

  • 在电话和覆盆子之间创建一个电桥的外观(您的对话者会非常多),
  • 双向广播声音(未经测试,但在设置中已考虑在内),
  • 流式传输到3d youtube网页。
  • 从几个朋友创建电话会议(jitsi见面)。
  • 通过网络界面即时更改相机设置(为什么旋转!&?不起作用)。

现在,关于悲伤。


1.将两个不同的摄像头用鱼眼镜头对准学校标尺,将其收起不能正常工作。 事实证明,鱼有不同的眼睛。 我们需要相同类型的相机:



2.无法通过webrtc Web界面的设置从立体摄像机扩展图片。 虽然图片很窄,但是像法国人的裤子。



3. MG996N伺服器的旋转角度被限制为-180。 实际上-160。也许有人会建议360,但不会连续旋转。

4.软件需要研磨。

5.呼叫有时掉线,您必须重新连接。

应用范围:

Source: https://habr.com/ru/post/zh-CN473750/


All Articles