507 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			507 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | { | ||
|  |  "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": {} | ||
|  |   } | ||
|  |  ] | ||
|  | } |