Part 2. Exploring the API
Python's built-in help system is a convenient way of determining what constants and functions are available within a particular scope. For example, after you type
>>> import create
you can determine everything at the top-level within create.py via
>>> dir(create)
You'll see lots of constants in all caps, as well as Create, a class that represents the type of a Create robot. You can see all of the methods and data encapsulated by that class via
>>> dir(create.Create)
and then you can get help on a particular method, such as demo(n) with
>>> help(create.Create.demo)
which will help explain how to get the most out of the next step...
Running the built-in demonstration behaviors
You should first place the robot, turned on, in a relatively open area when trying these. You can read brief descriptions of the built-in demos via
>>> help(r.demo)
and then
>>> r.demo(n)
will run Create's built-in demo number n.
You can stop a demo by calling demo with no arguments:
>>> r.demo()
Note that the programmatic demo numbers are zero-indexed, while those in the users manual are indexed starting at 1.
Controlling motors and actuators
The goal of the Create is to make it easy to program your own demos. The building blocks for your own tasks will be the actuators (motors, sounds, light) and sensors (lots).
Action
To get moving, you can specify the robot's translational and rotational velocities separately with
>>> r.go( cm_per_sec, degrees_per_sec )
The maximum translational velocity is 50 cm/sec and the max rotational velocity is about 60 deg/sec. I'd recommend a speed of 10 cm/sec for trying things out, but
you should certainly check out full speed (and be ready to stop it!)
For example, to get off the charger (be sure you're not in passive mode), you can run
>>> r.go( -10, 0 )
To stop you can run
>>> r.go()
Alternatively, r.stop() will also stop the robot. If you're teaching robotics, you might have your students exercise their kinematic skills by writing the above function from the following, more primitive one that sets each wheel's linear velocity separately (The diameter between the wheels is 25.8cm):
>>> r.setWheelVelocities( left_cm_per_sec, right_cm_per_sec )
Sound
You can do more actuation than driving, however. You have programmatic control over both the speaker (single midi notes and sequences of up to 16 notes) and the LEDs:
>>> r.playNote( note_number, note_duration )
>>> r.playSong( list_of_notes )
These fucntions use the note representation on page 12 of the interface specification. The note number spans 8 octaves from low G (31) to high G (127), and the note duration may be between 1 and 255, and is specified in 64ths of a second. Thus, r.playNote(69, 64) plays the 440 hertz, concert-tuning A for 1 second. A song may contain up to 16 notes. Rests are notes outside of the range from 31 to 127.
The input to playSong should be specified as a list of pairs of [ note_number, note_duration ] format. Thus, r.playSong([(60,8),(64,8),(67,8),(72,8)]) plays an arpeggiated C chord.
Lights
>>> r.setLEDs( power_color, power_intensity, play_status, advance_status )
The above method sets each of the three LEDs, from left to right: the power LED, the play LED, and the status LED. The power LED at the left can display colors from green (0) to red (255) and its intensity can be specified, as well. Hence, power_color and power_intensity are values from 0 to 255. The other two LED inputs should either be 0 (off) or 1 (on).
Reading sensors
None of the API commands thus far have provided a return value; they are "write-only," with data going to the robot. Here we take a look at getting data from the sensors.
One note -- you should be sure not to poll the sensors more quickly than the robot's internal update rate, which is every 15ms, or 67 hertz. If you're in a loop that might call a sensor-reading command more often than this, use time.sleep(0.015) or some other suitable value. To use this sleep function, you'll need to have to import the time module with the line import time at the top of the file.
To try out all the sensors at once (though it's a bit more time consuming to do), you can run
>>> d = r.sensors() # returns a dictionary that we name d
After this, d is a python dictionary whose keys are the various sensor names (see below). The values for each key are the stored sensor values. The full specification is summarized on pp 24-25 of the open interface.
Here is a quick summary of the sensor keys and their values:
- create.LEFT_BUMP, 0 or 1
- create.RIGHT_BUMP, 0 or 1
- create.LEFT_WHEEL_DROP, 0 or 1
- create.RIGHT_WHEEL_DROP, 0 or 1
- create.CENTER_WHEEL_DROP, 0 or 1
- create.WALL_IR_SENSOR, 0 or 1
- create.CLIFF_LEFT, 0 or 1
- create.CLIFF_FRONT_LEFT, 0 or 1
- create.CLIFF_FRONT_RIGHT, 0 or 1
- create.CLIFF_RIGHT, 0 or 1
- create.VIRTUAL_WALL, 0 or 1
- create.LEFT_WHEEL_OVERCURRENT, 0 or 1
- create.RIGHT_WHEEL_OVERCURRENT, 0 or 1
- create.INFRARED_BYTE, 0 to 255 (see table on p. 18)
- create.ADVANCE_BUTTON, 0 or 1
- create.PLAY_BUTTON, 0 or 1
- You can't sense the power button.
- create.POSE, [x, y, theta_in_degrees]
- create.CHARGING_STATE, 0 to 5 (see table on p. 19)
- create.VOLTAGE, 0 to 65535, in millivolts
- create.CURRENT, -32768 to 32767, in milliamps
- create.BATTERY_TEMP, -128 to 127, in degrees Celsius
- create.BATTERY_CHARGE, 0 to 65535, in milliamp-hours
- create.BATTERY_CAPACITY, 0 to 65535, in milliamp-hours
- create.WALL_SIGNAL, 0 to 4095, strength of side IR
- create.CLIFF_LEFT_SIGNAL, 0 to 4095, strength of IR
- create.CLIFF_FRONT_LEFT_SIGNAL, 0 to 4095, strength of IR
- create.CLIFF_FRONT_RIGHT_SIGNAL, 0 to 4095, strength of IR
- create.CLIFF_RIGHT_SIGNAL, 0 to 4095, strength of IR
- create.CHARGING_SOURCES_AVAILABLE, 0 to 3 (see table on p. 20)
- create.OI_MODE, 0 to 3 (see table on p. 20)
- create.SONG_NUMBER, 0 to 15
- create.SONG_PLAYING, 0 or 1 (is a song currently playing?)
It's worth singling out the POSE sensor: this is where the (estimated) odometry is stored as a list in the format [ x in cm, y in cm, angle in degrees ]. If the robot gets disconnected without calling close(), the odometry can get out of sync. You can call
>>> r.setPose(0,0,0)
to reset the odometry to the origin (or, by changing the inputs, to any other point you like.)
You can save a little time by asking for only the sensors that you care about, using a list of the above values:
>>> d = r.sensors( list_of_sensors_to_poll )
For example, you might update only the bump and odometry sensors via
>>> d = r.sensors( [ create.LEFT_BUMP, create.RIGHT_BUMP, create.POSE ] )
All of the values will be in the dictionary d, but only the ones requested will have been updated to reflect the most recent polling of the sensors.