Controlling a robot with pypot

In this notebook, we will show you the very basic way of controlling your robot using pypot. More precisely, we will see how to:

  • instantiate a pypot Robot (e.g. using a configuration file)
  • reading sensor values
  • sending motor commands

All examples we will be based on the robot Poppy, yet as detailed below you don't actually need the real robot as you can use a simulated version instead.

While you have access to other control layers (e.g. dynamixel low-level or defining your own controller), the method presented in this notebook should be the best one for most use cases.

Finally, in this notebook we will also show how you can seamlessly switch from controlling a real robot to controlling the simulated version in V-REP.

Instantiating a robot in pypot

The Robot class in pypot provides different factories that can be used to easily instantiate it:

Robot in pypot are built using a configuration dictionary. This configuration defines which port to use, which motors are connected to each buses...

You can find Poppy's configuration file directly in the poppytools package:


In [1]:
import os
import poppytools

config_path = os.path.join(os.path.dirname(poppytools.__file__), 'configuration', 'poppy_config.json')

For details on what's inside those configuration files, please directly refer to pypot's documentation.

You can then easily instantiate your robot using the from_json factory (you should have plugged your Poppy to your computer at this point):


In [2]:
from pypot.robot import from_json

poppy = from_json(config_path)

If everything went well you should now be connected to the robot.

Using a simulated version instead

If you don't have a real robot, you can still play with the simulated version. You can connect pypot to the V-REP robotic simulator.

Please refer to this post for information on how to do this.

Once you have started your V-REP, instead of using the from_json factory you can use from_vrep to instantiate your robot:


In [2]:
from pypot.vrep import from_vrep

import json

with open(config_path) as f:
    config = json.load(f)

poppy = from_vrep(config=config, vrep_host='127.0.0.1', vrep_port=19997, vrep_scene='poppy-sitting.ttt')

Seamlessy switch to the simulation to the real world

It's important to note that the rest of the notebook will work exactly the same whether if you use the real robot or the simulated version.

This is very important is it will allow you to run the same code in both cases. For instance, you could use the simulator to debug your code before running it on the real robot. In our team, we also use it to run an compare the same experiment run in a simulation or in the real world.

The simulated version of the robot add the method reset_simulation which is a very convenient way to run the eact same experimentmultiple time. Unfortunately, you will have to do that with your own hands in the real world :-)

Starting the synchronization

The next step is to start the synchronization between hardware motors/sensors to their software equivalent:


In [3]:
poppy.start_sync()

Now, all the motor registers should reflect their real values - actually the last retrieved one.

You have to note the sync frequency may vary from one sensor to another: e.g. the present_position is refreshed at 50Hz while the present_temperature only at 1Hz.

Access motor values

You can access the list of the motors connected to your robot using the motors property:


In [4]:
poppy.motors


Out[4]:
[<DxlMotor name=abs_y id=31 pos=-9.01>,
 <DxlMotor name=abs_x id=32 pos=-12.79>,
 <DxlMotor name=abs_z id=33 pos=3.47>,
 <DxlMotor name=bust_y id=34 pos=-3.47>,
 <DxlMotor name=bust_x id=35 pos=-24.4>,
 <DxlMotor name=head_z id=36 pos=14.22>,
 <DxlMotor name=head_y id=37 pos=44.11>,
 <DxlMotor name=l_shoulder_y id=41 pos=-141.03>,
 <DxlMotor name=l_shoulder_x id=42 pos=131.19>,
 <DxlMotor name=l_arm_z id=43 pos=83.82>,
 <DxlMotor name=l_elbow_y id=44 pos=-5.23>,
 <DxlMotor name=r_shoulder_y id=51 pos=24.15>,
 <DxlMotor name=r_shoulder_x id=52 pos=13.25>,
 <DxlMotor name=r_arm_z id=53 pos=-3.82>,
 <DxlMotor name=r_elbow_y id=54 pos=-17.98>,
 <DxlMotor name=l_hip_x id=11 pos=-7.34>,
 <DxlMotor name=l_hip_z id=12 pos=-1.45>,
 <DxlMotor name=l_hip_y id=13 pos=40.24>,
 <DxlMotor name=l_knee_y id=14 pos=35.74>,
 <DxlMotor name=l_ankle_y id=15 pos=49.63>,
 <DxlMotor name=r_hip_x id=21 pos=9.8>,
 <DxlMotor name=r_hip_z id=22 pos=9.89>,
 <DxlMotor name=r_hip_y id=23 pos=44.26>,
 <DxlMotor name=r_knee_y id=24 pos=36.18>,
 <DxlMotor name=r_ankle_y id=25 pos=45.41>]

Or show only their names


In [5]:
[m.name for m in poppy.motors]


Out[5]:
[u'abs_y',
 u'abs_x',
 u'abs_z',
 u'bust_y',
 u'bust_x',
 u'head_z',
 u'head_y',
 u'l_shoulder_y',
 u'l_shoulder_x',
 u'l_arm_z',
 u'l_elbow_y',
 u'r_shoulder_y',
 u'r_shoulder_x',
 u'r_arm_z',
 u'r_elbow_y',
 u'l_hip_x',
 u'l_hip_z',
 u'l_hip_y',
 u'l_knee_y',
 u'l_ankle_y',
 u'r_hip_x',
 u'r_hip_z',
 u'r_hip_y',
 u'r_knee_y',
 u'r_ankle_y']

You can also access motors individually directly by their names:


In [6]:
poppy.l_hip_y


Out[6]:
<DxlMotor name=l_hip_y id=13 pos=40.24>

You can then access all register values:


In [7]:
poppy.l_hip_y.present_position


Out[7]:
40.24

In [8]:
poppy.l_hip_y.present_temperature


Out[8]:
38.0

Using list comprehension, you can easily retrieve the value of a register for each motors.

Retrieve a dictionary {motor_name: position} just by doing:


In [9]:
{m.name: m.present_position for m in poppy.motors}


Out[9]:
{u'abs_x': -12.79,
 u'abs_y': -9.01,
 u'abs_z': 3.47,
 u'bust_x': -24.4,
 u'bust_y': -3.47,
 u'head_y': 44.11,
 u'head_z': 14.22,
 u'l_ankle_y': 49.63,
 u'l_arm_z': 83.82,
 u'l_elbow_y': -5.23,
 u'l_hip_x': -7.34,
 u'l_hip_y': 40.24,
 u'l_hip_z': -1.45,
 u'l_knee_y': 35.74,
 u'l_shoulder_x': 131.19,
 u'l_shoulder_y': -141.03,
 u'r_ankle_y': 45.41,
 u'r_arm_z': -3.82,
 u'r_elbow_y': -17.98,
 u'r_hip_x': 9.8,
 u'r_hip_y': 44.26,
 u'r_hip_z': 9.89,
 u'r_knee_y': 36.18,
 u'r_shoulder_x': 13.25,
 u'r_shoulder_y': 24.069999999999993}

Sending motor commands

Sending motor commands is really similar to reading them.

Let's try to make Poppy's head move a bit.

First, we have to make the motor uncompliant (dynamixel motors are set to compliant by default).


In [10]:
poppy.head_z.compliant = False

And then we can set a new goal_position:


In [11]:
poppy.head_z.goal_position = 10.

We can of course makes it move to a relative position using its current one:


In [12]:
poppy.head_z.goal_position = poppy.head_z.present_position + 10

Similarly you can make Poppy's head follows a sinusoid with the following line of codes:

We will use numpy to compute the sinus and matplotlib to plot it.


In [13]:
%pylab inline

amp = 30 # in degrees
freq = 0.3 # in Hz 
duration = 10 # in s

# Our sinus will be send at 50Hz to match the refresh frequency.
# This is not at all mandatory, yet sending at a higher frequency will have no impact.
step = 1 / 50.
N = duration / step

t = linspace(0, duration, N)
pos = amp * sin(2 * pi * freq * t)


Populating the interactive namespace from numpy and matplotlib

Let's plot it to make sure everything is fined.


In [14]:
plot(t, pos)


Out[14]:
[<matplotlib.lines.Line2D at 0x7fd0fd6dd9d0>]

And to apply it on the real robot:


In [15]:
import time

for p in pos:
    poppy.head_z.goal_position = p
    time.sleep(step)

You should now see your Poppy turns its head :-)

Next...

BTW, You could have done this in a much simpler way using primitives but that's another story...