{ "metadata": { "name": "", "signature": "sha256:7a1296f1470068a26e12a6e9eae79f37b84cd8aa3b3b14d6a900280ca6850afd" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Accessing pypot REST API through HTTP requests" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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](https://github.com/poppy-project/pypot/blob/REST-API-2.0/REST-APIs.md). More precisely, we will show how we can retrieve motor position and set new ones." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The client side of the example - meaning the HTTP requests - will be realised using *curl*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Note: For this tutorial you will need:*\n", "* [V-REP](http://www.coppeliarobotics.com)\n", "* [pypot](https://github.com/poppy-project/pypot) >= 2.x\n", "* [poppytools](https://github.com/poppy-project/poppy-software)\n", "* [bottle](http://bottlepy.org/docs/dev/index.html)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Create the Robot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we will instantiate a robot using V-REP (see [this post](https://forum.poppy-project.org/t/howto-connect-pypot-to-your-simulated-version-of-poppy-humanoid-in-v-rep/332/) for details on how this can be done)." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Launch V-REP" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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)." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Connect pypot to the V-REP scene" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import os\n", "import json\n", "\n", "import pypot\n", "import poppytools\n", "\n", "from pypot.vrep import from_vrep\n", "\n", "# Load the robot configuration\n", "configfile = os.path.join(os.path.dirname(poppytools.__file__),\n", " 'configuration', 'poppy_config.json')\n", "\n", "with open(configfile) as f:\n", " robot_config = json.load(f)\n", " \n", "# Load a V-REP scene with poppy sitting\n", "scene = os.path.join(os.path.dirname(pypot.__file__), \n", " '..', 'samples', 'notebooks', 'poppy-sitting.ttt')\n", "\n", "# Connect to the simulated robot\n", "robot = from_vrep(robot_config, '127.0.0.1', 19997, scene,\n", " tracked_objects=['head_visual'])" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Create the HTTPServer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will now starts the HTTP server providing the remote access of our robot." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It will run on *http://127.0.0.1:8080/*" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from pypot.server.httpserver import HTTPRobotServer\n", "\n", "server = HTTPRobotServer(robot, host='127.0.0.1', port=8080)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, the bottle server is launch using [tornado](http://www.tornadoweb.org/en/stable/) - for effiency puposes. Yet, as [IPython notebook](http://ipython.org/notebook.html) also uses the tornado server, it seems to cause trouble. Here, we use the [wsgiref](https://docs.python.org/2/library/wsgiref.html) server instead." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from threading import Thread\n", "\n", "Thread(target=lambda: server.run(quiet=True, server='wsgiref')).start()" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "As the [run method](http://poppy-project.github.io/pypot/pypot.server.html#pypot.server.httpserver.HTTPServer.run) of the server is blocking we launch it inside a thread. Thus, we can use the same notebook to send requests." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Sending requests" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the server is launched, you should be able to directly access the robot using the [REST API](https://github.com/poppy-project/pypot/blob/REST-API-2.0/REST-APIs.md)." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Getting values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For instance, to retrieve the list of all motors name:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "!curl http://127.0.0.1:8080/motor/list.json" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{\"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\"]}" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or to access a specific register of a motor:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "!curl http://127.0.0.1:8080/motor/head_z/register/present_position" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{\"present_position\": 0.2}" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, you can retrieve the list of sensors and their respective registers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "!curl http://127.0.0.1:8080/sensor/list.json" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{\"sensors\": [\"head_visual\"]}" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "!curl http://127.0.0.1:8080/sensor/head_visual/register/list.json" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{\"registers\": [\"position\", \"orientation\"]}" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "!curl http://127.0.0.1:8080/sensor/head_visual/register/position" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{\"position\": [-0.011824678629636765, -0.029312146827578545, 0.53340655565261841]}" ] } ], "prompt_number": 8 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Sending values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following requests should make the head of the robot moves." ] }, { "cell_type": "code", "collapsed": false, "input": [ "!curl -X POST -d \"100\" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \\\n", " --header \"Content-Type:application/json\"" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{}" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "!curl -X POST -d \"-100\" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \\\n", " --header \"Content-Type:application/json\"" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{}" ] } ], "prompt_number": 10 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Using the [requests](http://docs.python-requests.org/en/latest/) module instead" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similar behaviors can be achieved directly in Python, for instance using the [request](http://docs.python-requests.org/en/latest/) module." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import requests" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To retrieve a position:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')\n", "r.json()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 12, "text": [ "{u'present_position': -100.3}" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To set a new position:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import time\n", "\n", "pos = 50\n", "\n", "for _ in range(3):\n", " requests.post('http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json', \n", " data=json.dumps(pos), \n", " headers={'content-type': 'application/json'})\n", " \n", " pos *= -1\n", " time.sleep(1.)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just as a very basic benchmark:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%%timeit\n", "\n", "r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "100 loops, best of 3: 3.37 ms per loop\n" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }