This is a simple, portable robot simulator developed by Student Robotics. Some of the arenas and the exercises have been modified for the Research Track I course.
The simulator requires a Python 2.7 installation, the pygame library, PyPyBox2D, and PyYAML.
Once the dependencies are installed, simply run the test.py
script to test out the simulator.
You can run the program with:
$ python run.py ex_roam.py
The objective of the assignment is to make the robot wander in the circuit, avoiding obstacles (golden tokens) and moving behind itself the silver tokens found along its way.
To achieve this goal, five functions were developed:
searchRoad()
findRoad(dist_scan)
scanSectors(token_list)
searchSilver()
moveSilver()
The robot can divide the surrounding space into sectors, and find the nearest obstacle in each one. The number of sectors is specified in the variable nsect (default value = 12); changing this parameter can compromise the operation. Sectors are numbered as shown:
Meaning that sector 0 is always in front of the robot, while right-side sectors have negative numbers and left-side sectors positive ones.
The main code has the following flowchart:
Let us focus on the function searchRoad
. This function aims to search for a good orientation for the robot, choosing whether to turn it or not, depending on the distance from the obstacles in each sector.
Choices made:
- If an obstacle is found to be near in both sectors +1 and -1, i.e., the road is too narrow, it calls the function
findRoad
. - Otherwise, provided that there are no obstacles in sector 0, it moves the robot further.
- Furthermore, to avoid the robot's side getting too close to an obstacle,
searchRoad
checks if sectors +1 or -1 are obstacle-free. If one of them is not, then it turns a little bit. - If there is an obstacle in sector 0, the robot must turn to find a better way, so the
findRoad
function is called.
The searchRoad
function has the following flowchart:
The function findRoad
aims at finding an obstacle-free road to orient the robot that way. To do so, it looks both right-side (negative numbers) and left-side (positive numbers), symmetrically and sequentially, and stops when it finds the nearest obstacle-free sector.
Specifically, for each pair of sectors (e.g., +1 and -1), the function selects the one with the further obstacle and checks whether the obstacle's distance is acceptable. If so, the sector is considered obstacle-free. Otherwise, the search continues.
The scanSector
function is used to search for the closest gold token in each sector. Its argument is the list of tokens provided by the R.see
method, and it returns a float array in which the j-th element is the smallest distance from a golden token detected in the j-th sector.
The function searchSilver
aims to search for the closest silver token to make the robot collect it. First, it turns the robot, then it drives the robot near the silver token to finally grab it. Lastly, it calls the moveSilver
function.
The moveSilver
function can move the grabbed silver token behind the robot, looking around for obstacles to decide if it's better to turn left or right. Thanks to this, it avoids the robot hurting obstacles during the turning operation.
In conclusion, this is the robot's behaviour (high-speed video sequence):
robot_behaviour.mp4
The API for controlling a simulated robot is designed to be as similar as possible to the SR API.
The simulated robot has two motors configured for skid steering, connected to a two-output Motor Board. The left motor is connected to output 0
and the right motor to output 1
.
The Motor Board API is identical to that of the SR API, except that motor boards cannot be addressed by serial number. So, to turn on the spot at one quarter of full power, one might write the following:
R.motors[0].m0.power = 25
R.motors[0].m1.power = -25
The robot is equipped with a grabber, capable of picking up a token which is in front of the robot and within 0.4 metres of the robot's centre. To pick up a token, call the R.grab
method:
success = R.grab()
The R.grab
function returns True
if a token was successfully picked up, or False
otherwise. If the robot is already holding a token, it will throw an AlreadyHoldingSomethingException
.
To drop the token, call the R.release
method.
Cable-tie flails are not implemented.
To help the robot find tokens and navigate, each token has markers stuck to it, as does each wall. The R.see
method returns a list of all the markers the robot can see, as Marker
objects. The robot can only see markers which it is facing towards.
Each Marker
object has the following attributes:
info
: aMarkerInfo
object describing the marker itself. Has the following attributes:code
: the numeric code of the marker.marker_type
: the type of object the marker is attached to (eitherMARKER_TOKEN_GOLD
,MARKER_TOKEN_SILVER
orMARKER_ARENA
).offset
: offset of the numeric code of the marker from the lowest numbered marker of its type. For example, token number 3 has the code 43, but offset 3.size
: the size that the marker would be in the real game, for compatibility with the SR API.
centre
: the location of the marker in polar coordinates, as aPolarCoord
object. Has the following attributes:length
: the distance from the centre of the robot to the object (in metres).rot_y
: rotation about the Y axis in degrees.
dist
: an alias forcentre.length
res
: the value of theres
parameter ofR.see
, for compatibility with the SR API.rot_y
: an alias forcentre.rot_y
timestamp
: the time at which the marker was seen (whenR.see
was called).
For example, the following code lists all of the markers the robot can see:
markers = R.see()
print "I can see", len(markers), "markers:"
for m in markers:
if m.info.marker_type in (MARKER_TOKEN_GOLD, MARKER_TOKEN_SILVER):
print " - Token {0} is {1} metres away".format( m.info.offset, m.dist )
elif m.info.marker_type == MARKER_ARENA:
print " - Arena marker {0} is {1} metres away".format( m.info.offset, m.dist )
- Make the robot move more fluently.
- Make the robot turn exactly in the correct sector.
- Optimize the trajectory of the robot.