Skip to content

Commit

Permalink
Added TURN tutorial & removed symlinks
Browse files Browse the repository at this point in the history
The symlinks led to confusion for people using the examples without first compiling the js.

Closes #5
  • Loading branch information
dkumor committed Aug 30, 2019
1 parent c5cb072 commit 4372dae
Show file tree
Hide file tree
Showing 17 changed files with 108 additions and 37 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dist: js phony
publish: dist phony
cd js; npm publish
twine upload dist/*
anaconda upload dist/*.tar.gz

test: phony
pytest --cov=rtcbot --timeout=20
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![npm](https://img.shields.io/npm/v/rtcbot.svg?style=flat-square)](https://www.npmjs.com/package/rtcbot)
[![Documentation Status](https://readthedocs.org/projects/rtcbot/badge/?version=latest&style=flat-square)](https://rtcbot.readthedocs.io/en/latest/?badge=latest)
[![Join the chat at https://gitter.im/rtcbot/community](https://img.shields.io/gitter/room/dkumor/rtcbot.svg?style=flat-square)](https://gitter.im/rtcbot/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Tests](https://github.com/dkumor/rtcbot/workflows/tests/badge.svg)](https://github.com/dkumor/rtcbot/actions)
![Tests](https://github.com/dkumor/rtcbot/workflows/tests/badge.svg)

RTCBot's purpose is to provide a set of simple modules that help in developing remote-controlled robots in Python, with a focus on the Raspberry Pi.

Expand Down
41 changes: 27 additions & 14 deletions docs/installing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,53 @@ Installing RTCBot
RTCBot uses some very powerful libraries, which can make it a bit difficult to install on some systems.


Raspberry Pi
Raspbian
++++++++++++++

The raspberry pi does not have OpenCV available for python3,
meaning that RTCBot's CVCamera and CVDisplay will not be available unless you manually compile them.
Thankfully, you can still use the official camera module, by installing picamera::
RTCBot requires several dependencies which are best installed using apt-get::

sudo apt-get install python3-numpy
sudo apt-get install python3-numpy python3-cffi python3-aiohttp \
libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev \
libswscale-dev libswresample-dev libavfilter-dev libopus-dev \
libvpx-dev pkg-config libsrtp2-dev python3-opencv pulseaudio

Then, you can install rtcbot with pip::

sudo pip3 install picamera rtcbot

The installation will take a long time, since many of RTCBot's dependencies need to be compiled.
.. warning::
These instructions were made with reference to Raspbian Buster on the Raspberry Pi 4.
Some things might work differently on older versions of raspbian.

Linux
Ubuntu
+++++++++++

Before starting, you will want to install OpenCV, numpy and ffmpeg::
RTCbot requires several dependencies which are best installed using apt-get::

sudo apt-get install python3-numpy python3-opencv ffmpeg
sudo apt-get install python3-numpy python3-cffi python3-aiohttp \
libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev \
libswscale-dev libswresample-dev libavfilter-dev libopus-dev \
libvpx-dev pkg-config libsrtp2-dev python3-opencv pulseaudio

Then, you can install rtcbot using pip::
Then, you can install rtcbot with pip::

pip3 install rtcbot
sudo pip3 install picamera rtcbot

Windows
+++++++++++

To install on Windows, you will need to use Anaconda. With anaconda, install opencv, numpy, ...
To install on Windows, you will want to use Anaconda `Anaconda <https://www.anaconda.com/distribution/#download-section>`_.

Then ...
.. note::
These instructions are incomplete. If you succeed in installing rtcbot
on windows, please open a pull request with instructions!

Mac
+++++++++++

On mac, follow the windows instructions - the library will only work through anaconda.
To install on Mac, you will want to use Anaconda `Anaconda <https://www.anaconda.com/distribution/#download-section>`_.


.. note::
These instructions are incomplete. If you succeed in installing rtcbot
on mac, please open a pull request with instructions!
1 change: 0 additions & 1 deletion examples/basics/rtcbot

This file was deleted.

69 changes: 64 additions & 5 deletions examples/mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ Rather than connecting to the robot, we will have two separate Python programs.
```

In a previous tutorial, we developed a connection that streamed video to the browser. This tutorial will implement exactly the same functionality,
but with a robot on a remote connection.
but with the robot on a remote connection.

The browser-side code will remain unchanged - all of the work here will be in Python.

## Server Code

Most of the server code is identical. The only difference is that we set up a listener at `/ws`, which will establish a websocket connection with the robot:
Most of the server code is unchanged. The only difference is that we set up a listener at `/ws`, which will establish a websocket connection with the robot:

```python
ws = None # Websocket connection to the robot
Expand Down Expand Up @@ -140,7 +140,7 @@ web.run_app(app)

## Remote Code

In this tutorial, we will just run both server and robot on the local machine. The same code will work over the internet simply by setting the right IP for the robot to connect to. The robot connects to the server with a websocket, and waits for the message that will allow it to initialize its WebRTC connection.
In this tutorial, we will just run both server and robot on the local machine. The robot connects to the server with a websocket, and waits for the message that will allow it to initialize its WebRTC connection.

```python
import asyncio
Expand Down Expand Up @@ -171,12 +171,71 @@ finally:

With these two pieces of code, you first start the server, then start the robot, and finally open `http://localhost:8080` in the browser to view a video stream coming directly from the robot, even if the robot has an unknown IP.

## If it doesn't work over 4G

The above example should work for most people. However, some mobile network operators perform routing that disallows creating a direct WebRTC connection to a mobile device over 4G. If this is your situation, you need to use what is called a TURN server, which will forward data between the browser and robot.

```eval_rst
.. note::
You can check if your mobile operator allows such connections by using your phone to create a wifi hotspot, to which you can connect your robot. If video streaming works with the code above, you can ignore this section!
```

```eval_rst
.. warning::
Because a TURN server essentially serves as a proxy through which an entire WebRTC connection is routed, it can send and receive quite a bit of data - make sure that you don't
exceed your download and upload limits!
```

While installing and configuring [coTURN](https://github.com/coturn/coturn) on linux is recommended for permanent setups,
for simplicity we will run the [Pion TURN server](https://github.com/pion/turn) on the same computer that is running our server code.

The Pion server is easy to set up on Windows,Mac and Linux - all you need to do is [download the executable](https://github.com/pion/turn/releases/tag/1.0.3), and run it from the command line as shown.

**Linux/Mac**:
```bash
chmod +x ./simple-turn # allow executing the downloaded file
export USERS='myusername=mypassword'
export REALM=my.server.ip
export UDP_PORT=3478
./simple-turn-linux-amd64 # simple-turn-darwin-amd64 if on Mac
```

**Windows**: You can run the following from powershell:
```powershell
$env:USERS = "myusername=mypassword"
$env:REALM = "my.server.ip"
$env:UDP_PORT = 3478
./simple-turn-windows-amd64.exe
```

With the server running, you will need to let both Python and Javascript know about it when creating your `RTCConnection`:
```python
from aiortc import RTCConfiguration, RTCIceServer

myConnection = RTCConnection(rtcConfiguration=RTCConfiguration([
RTCIceServer(urls="stun:stun.l.google.com:19302"),
RTCIceServer(urls="turn:my.server.ip:3478",
username="myusername",credential="mypassword")
]))
```

```javascript
var conn = new rtcbot.RTCConnection(rtcConfiguration=[
{ urls: ["stun:stun.l.google.com:19302"] },
{ urls: ["turn:my.server.ip:3478?transport=udp",
username: "myusername", credential: "mypassword"], },
]);
```

With the above code, you should be able to stream video to your browser using 4G, even if your mobile operator disallows direct connections.


## Summary

This tutorial split up the server and robot code into distinct pieces. Also introduced was rtcbot's websocket wrapper, allowing you to easily establish a data-only connection.
This tutorial split up the server and robot code into distinct pieces. Also introduced was rtcbot's websocket wrapper, allowing you to easily establish a data-only connection. Finally, TURN servers were introduced, and instructions were given on how to set one up if direct connections fail.

## Extra Notes

Be aware that throughout these tutorials, all error handling and robustness was left out in the interest of
clarity of the fundamental program flow. In reality, you will probably want to make sure that the connection
clarity in the fundamental program flow. In reality, you will probably want to make sure that the connection
did not have an error, and add the ability to connect and disconnect multiple times.
1 change: 0 additions & 1 deletion examples/mobile/rtcbot

This file was deleted.

4 changes: 2 additions & 2 deletions examples/offloading/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The second part of the tutorial adds a neural network into the mix, which tries

```eval_rst
.. note::
While with a weak SBC like the Raspberry Pi there might be a non-negligible delay between sending a video frame and getting back a command, this is not a limitation of the approach, since it is possible to stream `video games with barely-noticeable lag <https://arstechnica.com/gaming/2019/03/googles-multiyear-quest-to-overcome-ids-stadia-streaming-skepticism/>`_. In particular, rtcbot currently cannot take advantage of the Pi's hardware acceleration, meaning that all video encoding is done in software, which ends up adding to video delay.
While with a Raspberry Pi there might be a non-negligible delay between sending a video frame and getting back a command, this is not a limitation of the approach, since it is possible to stream `video games with barely-noticeable lag <https://arstechnica.com/gaming/2019/03/googles-multiyear-quest-to-overcome-ids-stadia-streaming-skepticism/>`_. In particular, rtcbot currently cannot take advantage of the Pi's hardware acceleration, meaning that all video encoding is done in software, which ends up adding to video delay.
```

## Python to Python Streaming
Expand Down Expand Up @@ -137,6 +137,6 @@ This portion of the tutorial focuses on modifying the desktop code from the prev
a neural network which is trained exploiting the computational power available on a desktop PC.

This is an _advanced_ topic, requiring a working knowledge of machine learning basics. It is also assumed
that you are using linux on your desktop, and that you have an nvidia graphics card compatible with [pytorch](https://pytorch.org). If you choose to attempt this part on a windows desktop or on a mac, be aware that you might run into some problems that might need a bit of googling skills to solve.
that you are using linux on your desktop, and that you have an nvidia graphics card compatible with [pytorch](https://pytorch.org). If you choose to attempt this part on a windows desktop or on a mac, be aware that you might run into some problems that might require some googling skills to solve.

### Under Construction...
1 change: 0 additions & 1 deletion examples/offloading/rtcbot

This file was deleted.

1 change: 0 additions & 1 deletion examples/remotecontrol/rtcbot

This file was deleted.

1 change: 0 additions & 1 deletion examples/streaming/rtcbot

This file was deleted.

8 changes: 4 additions & 4 deletions examples/webrtc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,27 +232,27 @@ To understand what is happening in the above code, it is important to understand
WebRTC's core goal is fast peer-to-peer communication between clients. An example of this is video chat. Suppose you and your friend
both connect to a web server to talk with each other. The video from your friend's webcam first travels to the server, and then is forwarded from the server to you. This is not ideal - if the server is in another country, your video connection would have a lot of latency, since it needs to travel a large distance - even if you and your friend are connected to the same wifi!

WebRTC fixes this by establishing a direct connection between you and your friend - with WebRTC, the video signal would never even leave your local network, giving high quality and very low latency communication. The remote server is only used to help create the connection. Furthermore, the protocol includes mechanisms for passing connections through firewalls, and other complex network configurations.
WebRTC fixes this by trying to establish a direct connection between you and your friend - with WebRTC, the video signal would never even leave your local network, giving high quality and very low latency communication. The remote server is only used to help create the connection. Furthermore, the protocol includes mechanisms for passing connections through firewalls, and other complex network configurations.

The above technology is unimportant to us at the moment, since we will connect directly to the server anyways (no intermediate hops), but will become relevant once we try controlling the robot over a 4G connection, where the server and peer become decoupled.

Even without using the above benefits, WebRTC is a better fit than something like a websocket for controlling a robot, since it is designed from the ground up for very low latency and high throughput communication. Furthermore, it natively supports video, with video stream quality adjusting for network speed. This results in a robust and fast connection.

### Connection Setup

Due to all of the above benefits, establishing a WebRTC connection between a local device (such as your browser) and the remote device (robot) can be a bit involved. Three things need to happen:
Unfortunately, establishing a WebRTC connection between a local device (such as your browser) and the remote device (robot) can be a bit involved. Three things need to happen:

1. The local device prepares the type of data it needs to be able to send or accept (raw data, video, audio, etc)
2. The local device needs to gather information about how others can connect to it, such that this data can be sent efficiently. For example, things on your local network could possibly talk with each other using local addresses, like `192.168.1.153`. Other times, they must go over the internet, where you have a different IP. The device does some setup, and gathers all the ways that the peer could connect to it. These candidate connection methods are called [ICE Candidates](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Connectivity#ICE_candidates).
3. The resulting information needs to be sent to the remote device (robot)
4. The remote device (robot) needs to do the same thing, sending back its own information.
5. Finally, the two sides use this information to create a direct connection

Steps 3 and 4 involve a "Signaling Server", which sends this info from one device to the other. For now, we won't separate out the signaling server from our python code. In this tutorial, we will set up a data channel between the server code and the browser's javascript.
Steps 3 and 4 involve a "Signaling Server", which sends this info from one device to the other. Right now, we don't separate out the signaling server from our python code. That will come in a later tutorial.

## Sending JSON to Python and Back

In the above example, we sent a string, one way: from the browser to Python. In the interest of completeness, we can modify the
In the previous example, we sent a string, one way: from the browser to Python. In the interest of completeness, we can modify the
example given above to both send and receive JSON on button press:

The first modification we make is subscribing to incoming messages in javascript,
Expand Down
1 change: 0 additions & 1 deletion examples/webrtc/rtcbot

This file was deleted.

2 changes: 1 addition & 1 deletion js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![npm](https://img.shields.io/npm/v/rtcbot.svg?style=flat-square)](https://www.npmjs.com/package/rtcbot)
[![Documentation Status](https://readthedocs.org/projects/rtcbot/badge/?version=latest&style=flat-square)](https://rtcbot.readthedocs.io/en/latest/?badge=latest)
[![Join the chat at https://gitter.im/rtcbot/community](https://img.shields.io/gitter/room/dkumor/rtcbot.svg?style=flat-square)](https://gitter.im/rtcbot/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Tests](https://github.com/dkumor/rtcbot/workflows/tests/badge.svg)](https://github.com/dkumor/rtcbot/actions)
![Tests](https://github.com/dkumor/rtcbot/workflows/tests/badge.svg)

RTCBot's purpose is to provide a set of tutorials and simple modules that help in developing remote-controlled robots in Python, with a focus on the Raspberry Pi.

Expand Down
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rtcbot",
"version": "0.0.8",
"version": "0.0.9",
"description": "",
"main": "dist/rtcbot.cjs.js",
"module": "dist/rtcbot.esm.js",
Expand Down
3 changes: 3 additions & 0 deletions rtcbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
from .subscriptions import *
from .javascript import getRTCBotJS
from .devices import *


__version__="0.0.9"
5 changes: 3 additions & 2 deletions rtcbot/connection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from aiortc import RTCPeerConnection, RTCSessionDescription
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCConfiguration, RTCIceServer
import asyncio
import logging
import json
Expand Down Expand Up @@ -320,7 +320,8 @@ def subscribe(self, subscription=None):
class RTCConnection(SubscriptionProducerConsumer):
_log = logging.getLogger("rtcbot.RTCConnection")

def __init__(self, defaultChannelOrdered=True, loop=None, rtcConfiguration=None):
def __init__(self, defaultChannelOrdered=True, loop=None,
rtcConfiguration=RTCConfiguration([RTCIceServer(urls="stun:stun.l.google.com:19302")])):
super().__init__(
directPutSubscriptionType=asyncio.Queue,
defaultSubscriptionType=asyncio.Queue,
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# This call to setup() does all the work
setuptools.setup(
name="rtcbot",
version="0.0.8",
version="0.0.9",
description="An asyncio-focused library for webrtc robot control",
long_description=README,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit 4372dae

Please sign in to comment.