Intro to Robotics Programming: Part 2
Posted: March 21, 2016
Getting Setup
Meet Rob.
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:
- Rigid Bodies: A rigid body holds the physical properties of an object. (mass, position, rotation, velocity, etc.) It does not have a shape until you attach one or more collision shapes to it. If you’ve done physics with particles before, rigid bodies differ in that they are able to rotate. Rigid bodies generally tend to have a 1:1 correlation to sprites in a game. You should structure your game so that you use the position and rotation of the rigid body for drawing your sprite.
- Collision Shapes: By attaching shapes to bodies, you can define the a body’s shape. You can attach as many shapes to a single body as you need to in order to define a complex shape. Shapes contain the surface properties of an object such as how much friction or elasticity it has.
- Constraints/Joints: Constraints and joints describe how bodies are attached to each other.
- Spaces: Spaces are containers for simulating objects in Chipmunk. You add bodies, shapes and joints to a space and then update the space as a whole. They control how all the rigid bodies, shapes, and constraints interact together.
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.