Accessing pypot REST API through HTTP requests

Introduction

In this example, we will illustrate how to start an HTTP server permitting the remote access of any Poppy creature through the pypot REST API. More precisely, we will show how we can retrieve motor position and set new ones.

For this, we will use the simulated version of the Poppy humanoid. Thus, anyone even without having a "real" poppy creature on its table, will be able to try. Adapting this tutorial to a "real" robot should be straightforward.

The client side of the example - meaning the HTTP requests - will be realised using curl.

Note: For this tutorial you will need:

Create the Robot

First, we will instantiate a robot using V-REP (see this post for details on how this can be done).

Launch V-REP

First launch a V-REP instance. We will assume below that the remoteApi is bind to '127.0.0.1' using the port 19997 (its default configuration).

Connect pypot to the V-REP scene


In [1]:
import os
import json

import pypot
import poppytools

from pypot.vrep import from_vrep

# Load the robot configuration
configfile = os.path.join(os.path.dirname(poppytools.__file__),
                          'configuration', 'poppy_config.json')

with open(configfile) as f:
    robot_config = json.load(f)
    
# Load a V-REP scene with poppy sitting
scene = os.path.join(os.path.dirname(pypot.__file__), 
                     '..', 'samples', 'notebooks', 'poppy-sitting.ttt')

# Connect to the simulated robot
robot = from_vrep(robot_config, '127.0.0.1', 19997, scene,
                  tracked_objects=['head_visual'])

Create the HTTPServer

We will now starts the HTTP server providing the remote access of our robot.

It will run on http://127.0.0.1:8080/


In [2]:
from pypot.server.httpserver import HTTPRobotServer

server = HTTPRobotServer(robot, host='127.0.0.1', port=8080)

By default, the bottle server is launch using tornado - for effiency puposes. Yet, as IPython notebook also uses the tornado server, it seems to cause trouble. Here, we use the wsgiref server instead.


In [3]:
from threading import Thread

Thread(target=lambda: server.run(quiet=True, server='wsgiref')).start()

As the run method of the server is blocking we launch it inside a thread. Thus, we can use the same notebook to send requests.

Sending requests

Now that the server is launched, you should be able to directly access the robot using the REST API.

Getting values

For instance, to retrieve the list of all motors name:


In [4]:
!curl http://127.0.0.1:8080/motor/list.json


{"motors": ["l_elbow_y", "r_elbow_y", "r_knee_y", "head_y", "head_z", "r_arm_z", "r_ankle_y", "r_shoulder_x", "r_shoulder_y", "r_hip_z", "r_hip_x", "r_hip_y", "l_arm_z", "l_hip_x", "l_hip_y", "l_hip_z", "abs_x", "abs_y", "abs_z", "l_ankle_y", "bust_y", "bust_x", "l_knee_y", "l_shoulder_x", "l_shoulder_y"]}

Or to access a specific register of a motor:


In [5]:
!curl http://127.0.0.1:8080/motor/head_z/register/present_position


{"present_position": 0.2}

Similarly, you can retrieve the list of sensors and their respective registers:


In [6]:
!curl http://127.0.0.1:8080/sensor/list.json


{"sensors": ["head_visual"]}

In [7]:
!curl http://127.0.0.1:8080/sensor/head_visual/register/list.json


{"registers": ["position", "orientation"]}

In [8]:
!curl http://127.0.0.1:8080/sensor/head_visual/register/position


{"position": [-0.011824678629636765, -0.029312146827578545, 0.53340655565261841]}

Sending values

The following requests should make the head of the robot moves.


In [9]:
!curl -X POST -d "100" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \
        --header "Content-Type:application/json"


{}

In [10]:
!curl -X POST -d "-100" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \
        --header "Content-Type:application/json"


{}

Using the requests module instead

Similar behaviors can be achieved directly in Python, for instance using the request module.


In [11]:
import requests

To retrieve a position:


In [12]:
r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')
r.json()


Out[12]:
{u'present_position': -100.3}

To set a new position:


In [13]:
import time

pos = 50

for _ in range(3):
    requests.post('http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json', 
                  data=json.dumps(pos), 
                  headers={'content-type': 'application/json'})
    
    pos *= -1
    time.sleep(1.)

Just as a very basic benchmark:


In [14]:
%%timeit

r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')


100 loops, best of 3: 3.37 ms per loop

In [ ]: