Intro to Robotics Programming: Part 2

Posted: March 21, 2016


Getting Setup

Meet Rob.

Rob the robot

Rob is a member of the mobile robot family. In the two dimensional world we will create, he is free to move in any direction. Rob won't be the only robot we meet on our journey, but he'll be our first. Let's help him get moving.

Python

If you're on Linux, you already have Python and so are just about ready to go. I highly recommend the use of pip and virtualenvwrapper to manage package installation, but choose any work-flow that works for you.

You can install all the required Python packages you'll need by downloading this requirements file and running:

mkvirtualenv robotics_intro_env
pip install -r requirements.txt

And that's it, we're ready to get coding!

Hello Rob(ot)

Let's get Rob up and running. Throughout this series we'll use the pymunk library, a wrapper for Chipmunk for our world and object representation, as well as collision detection and joint modeling. There are several components to most robot models. Chipmunk sums it up nicely:

-- Chipmunk Documentation

All robot models contain the same basic components: physical properties, shape, and joints (generically called constraints). And we must usually put them into some container. We'll come back to joints later, but Rob doesn't really need one, so let's start with just a body and shape. Take note that although I refer to physical units (meters and kilograms), Chipmunk actually doesn't care what units we use. As long as you are consistent, you will get consistent and correct results that you can treat however you like. This allows us to define a meters-to-pixels conversion, and use it directly in our robot body, again simplifying our display code.

import pymunk

# constant for converting physical units to pixels
M_TO_PIXELS = 200

# build Rob the robot
rob_mass = 1  # 1 kg
rob_radius = 0.1 * M_TO_PIXELS  # 0.1 meter radius, converted to pixels for later display
rob_I = pymunk.moment_for_circle(rob_mass, 0, rob_radius)  # moment of inertia for disk
rob_body = pymunk.Body(rob_mass, rob_I)
rob_body.position = 300, 300  # (x, y)
rob_shape = pymunk.Circle(rob_body, rob_radius)

That's it! Not very exciting, since we still can't see him yet. The second library we'll make great use of is pyglet, a fantastic Python wrapper for OpenGL. OpenGL is an absolute nightmare to use directly, but pyglet makes it much easier, and there is even a convenient pymunk.pyglet_util library as well! However, we still have some setting up to do.

# setup context
window = pyglet.window.Window(width=600, height=600, visible=False)

# define visualization
@window.event
def on_draw():
    # always clear and redraw for graphics programming
    window.clear()
    pymunk.pyglet_util.draw(rob_shape)  # this is gold right here

We first create a window object which creates the OpenGL context for us, but set it to visible=False so it doesn't display before we finish defining our application. Next, we create a pyglet event handler by using the event annotation on window, and the function name on_draw indicates to run this function when, you guessed it, drawing. As with any graphics programming, we want to clear the display buffer first, and then do all of our drawing at once. We're going to get a lot of mileage out of pymunk.pyglet_util.draw(), as it saves us from implementing polygon drawing routines ourselves.

Two more lines of setup and we're ready to run our robot.

rob_shape.color = 255, 0, 0  # make Rob red, RGB value
window.set_visible(True)
pyglet.app.run()  # start application loop

We make our window visible and start our application loop. If you get stuck, ESC exits the window.

Interaction

Now, although Rob is a masterpiece of robot design, and should probably be put in a contemporary art museum somewhere, you're probably wondering if you could make him do... something.... Anything really. Let's do just that.

Before running your application, at the top add an import from pyglet.window import key, and add the following above window.set_visible(True):

# get access to system keyboard
keyboard = key.KeyStateHandler()
window.push_handlers(keyboard)

# use keyboard to control Rob
def process_user_input(dt):
    speed = 0.6  # m/s
    distance = speed * dt * M_TO_PIXELS  # m/s * s -> pixels

    # direction
    if keyboard[key.RIGHT]:
        rob_body.position += distance, 0
    if keyboard[key.LEFT]:
        rob_body.position -= distance, 0
    if keyboard[key.UP]:
        rob_body.position += 0, distance
    if keyboard[key.DOWN]:
        rob_body.position -= 0, distance

# process input at regular interval
pyglet.clock.schedule_interval(process_user_input, 1.0/60)  # 60Hz

This allows us to control Rob with the arrow keys on our keyboard. Just like we would with any hardware, we read and process its signal at a regular, fixed interval. Because pyglet cannot guarantee exact timing because of operating system restrictions, we take in a Δtime to know exactly how much time has passed since the last update.

And there you have it. Running again gives you a real (simulated) robot (colored blob) that you can control!

The complete code for this article can be found in the https://github.com/clintliddick/robotics_intro repository, and you can run the application with:

cd robotics_intro
workon robotics_intro_env
python -m roboticsintro.hellorobot.simple

Wrapping Up

We've met our first robot, and learned a little about its composition in simulation space. In the next article, we'll consider rotation, and introduce the concept of configuration space.

Next article: part 3