RTCBot Basics¶
This tutorial will teach you the fundamentals of using RTCBot for your projects. RTCBot is a Python 3 asyncio library, meaning that it is meant to run in an event loop.
Asyncio Basics¶
The most basic asyncio program is the following:
import asyncio
# Run the event loop
asyncio.get_event_loop().run_forever()
You can exit the program with CTRL+C
. Right now, the program does nothing, just runs in a loop. Let’s fix that:
import asyncio
async def myfunction():
while True:
await asyncio.sleep(1)
print("1 second passed")
asyncio.ensure_future(myfunction())
asyncio.get_event_loop().run_forever()
This will print “1 second passed” each second.
Notice that myfunction
is run in an infinite loop.
The utility of an event loop is that you can run many functions concurrently, which behaves as if your program was running with many threads at once:
import asyncio
async def myfunction1():
while True:
await asyncio.sleep(1)
print("1 second passed")
async def myfunction2():
while True:
await asyncio.sleep(2)
print("2 seconds passed")
asyncio.ensure_future(myfunction1())
asyncio.ensure_future(myfunction2())
asyncio.get_event_loop().run_forever()
The key here is the await
keyword, used in an async
function (called a coroutine). The await asyncio.sleep(1)
command pauses execution of the function until one second has passed,
allowing the event loop to spend time running the other function.
This means that the event loop is a good way to program where multiple things need to happen in response to events, such as incoming data, or timers, which is precisely the situation in a robot.
RTCBot is a set of tools allowing you to easily use an asyncio event loop to pass information between parts of your robot.
To learn more about asyncio, it is recommended that you look at a more in-depth tutorial here.
View a Video Feed¶
To introduce you to the basic concepts of RTCBot, we will start with the simplest task, viewing a webcam video feed:
import asyncio
from rtcbot import CVCamera, CVDisplay
camera = CVCamera()
display = CVDisplay()
@camera.subscribe
def onFrame(frame):
print("got video frame")
display.put_nowait(frame)
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
The camera might take several seconds to initialize, but after it finishes, a window with a live feed of your webcam will pop up.
The CVCamera
and CVDisplay
objects use OpenCV in the background to process frames.
The camera.subscribe
function allows you to subscribe to video frames incoming from the webcam,
firing the onFrame
function 30 times a second with numpy arrays containing BGR images captured by the camera.
The put_nowait
function is then used to send the frame to the window where the image is displayed.
These two functions are part of RTCBot’s core abilities. Every producer of data (like CVCamera
) has a subscribe()
method, and every consumer of data (like CVDisplay
) has a put_nowait
method to insert data.
Note
If you are using the official Raspberry Pi camera, you should replace CVCamera with PiCamera.
Warning
CVDisplay does not work on Mac due to issues with threading in the display toolkit - if using a Mac, you’ll have to wait for the video streaming tutorial to view the video feed!
Subscriptions¶
Using a callback function with the subscribe
method is not the only way to
get data out of a data-producing object. The subscribe
method is also able
to create what is called a subscription
.
To understand subscriptions, let’s take a quick detour to python Queues:
import asyncio
# An asyncio Queue has put_nowait and get coroutine
q = asyncio.Queue()
# Sends data each second
async def sender():
while True:
await asyncio.sleep(1)
q.put_nowait("hi!")
# Receives the data
async def receiver():
while True:
data = await q.get()
print("Received:", data)
asyncio.ensure_future(sender())
asyncio.ensure_future(receiver())
asyncio.get_event_loop().run_forever()
Here, the sender
function sends data, and the receiver
awaits for incoming data, and prints it. Notice how the queue had a get
coroutine from which data could be awaited.
We can use the subscribe
method in a similar way to the above code snippet. When run without an argument, subscribe
actually returns a subscription, which CVCamera
automatically keeps updated with new video frames as they come in:
import asyncio
from rtcbot import CVCamera, CVDisplay
camera = CVCamera()
display = CVDisplay()
frameSubscription = camera.subscribe()
async def receiver():
while True:
frame = await frameSubscription.get()
display.put_nowait(frame)
asyncio.ensure_future(receiver())
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
This program displays a live video feed, just like the previous version.
The receiver
function is just running put_nowait
on each frame received from the subscription. This can be done automatically using the putSubscription
method, making this a shorthand for the above program:
import asyncio
from rtcbot import CVCamera, CVDisplay
camera = CVCamera()
display = CVDisplay()
frameSubscription = camera.subscribe()
display.putSubscription(frameSubscription)
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
Finally, the camera
object has a get
coroutine, meaning that it can be passed into putSubscription
directly:
import asyncio
from rtcbot import CVCamera, CVDisplay
camera = CVCamera()
display = CVDisplay()
display.putSubscription(camera)
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
Generalizing to Audio¶
The above code examples all created a video stream, and displayed it in a window. RTCBot uses exactly the same API for everything. This means that we can trivially add audio to the previous example:
import asyncio
from rtcbot import CVCamera, CVDisplay, Microphone, Speaker
camera = CVCamera()
display = CVDisplay()
microphone = Microphone()
speaker = Speaker()
display.putSubscription(camera)
speaker.putSubscription(microphone)
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
microphone.close()
speaker.close()
Here, a video stream should be displayed in a window, and all microphone input should be playing in your headphones (or speakers).
Summary¶
This tutorial introduced the basics of RTCBot, with a focus on the fundamentals:
Every data producer has the
subscribe
method andget
coroutineEvery data consumer has a
putSubscription
method and aput_nowait
methodputSubscription
takes any object with aget
coroutineSubscribe can also be used for direct callbacks, or with custom subscriptions.
Extra Notes¶
Each producer can have multiple subscriptions active at the same time. This code shows two different windows with the same video feed:
import asyncio
from rtcbot import CVCamera, CVDisplay
camera = CVCamera()
display = CVDisplay()
display2 = CVDisplay()
display.putSubscription(camera)
subscription2 = camera.subscribe()
display2.putSubscription(subscription2)
try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()
display2.close()
The get
coroutine of camera behaves as a single default subscription, so it can only be used by one display (it returns each frame once). The subscribe
function allows creating an arbitrary number of independent subscriptions/callbacks.