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": {}
 | |
|   }
 | |
|  ]
 | |
| } |