Concurrent programming with Vector: Sharing Vector’s charger
Tags: Robotics, Concurrent Programming, Python

In my previous article on the theme of concurrent programming with Vector, we illustrated the concepts of preemptive multitasking, cooperative multitasking and multiprocessing using the Vector robots. In this article, we will build on those concepts and examine how a basic synchronization primitive, the lock, can be used to allow two Vectors to share a common charger. (Background: Vector is a battery powered autonomous robot. Whenever the battery voltage runs low, Vector needs to return to its charger in order to rev up its batteries. Vector is programmed to be able to automatically return to its charger when required… however, the same doesn’t work with two robots are sharing the same charger).

The Challenge

I have two Vector robots, but only one charger (I bought the second Vector as a stand alone unit on eBay). If left alone outside the charger, one of the two Vector’s would get to the charger first, and sit on it, while the other Vector dies out. So the intent of this exercise is to figure out a way by which the two Vector’s can cooperatively share the charger.

This project can be both fun as well as a great learning resource on how robots can be programmed to share resources.

While we work out the solution, you can reference the complete code on my github.

The Solution

The heart of the problem lies in that the charger must be shared by the two robots in a mutually exclusive fashion: meaning that when one Vector is given permission to charge, it must get exclusive ownership of the charger, so that the two Vectors don’t land up colliding while trying to drive up the charger at the same time. When the charging is complete, the same Vector needs to drive off the charger and release ownership of the charger. In computer science, a lock is used to define this concept of mutual exclusion and guard the resource (in this case a charger) against any possible concurrent access. Here is a code snippet illustrating how a lock can be used to provide mutual exclusion to a charger.

with await lock:

await returnToCharger(robot)

chargerOccupied = True

print ("Charger occupied by robot %d" %id)

time.sleep(120)

await driveOffCharger(robot)

chargerOccupied = False

The set of instructions that a lock is protecting is known as a critical section. In this case, the critical section comprises of instructing the Vector to return to the charger, charge itself for 120 seconds, and drive off the charger. It is important to remember that only one Vector (the one who successfully acquires the lock) can execute the instructions in the critical section. Once the lock has been acquired, it must be released after the instructions in the critical section are complete, before the lock can be acquired again by another Vector. This the critical section must be so defined to protect any access to a shared resource, in this case, the charger. Because only one entity can grab the lock at one time, equally important it is to ensure that the critical section is compact and contains no extra instructions that do not require exclusive access to the resource.

The next question is when should the lock be acquired. In our case, the answer to this is pretty simple, the lock needs to be acquired when one of the Vector’s feel the need to get charged. Here is the code.

async def worker(robot, id, lock):

"""Connect to the robots and run

"""

print ("Starting worker for robot %d" %id)

if not checkIfChargeRequired(robot):

await driveOffCharger(robot)

else:

with await lock:

await returnToCharger(robot)

chargerOccupied = True

print ("Charger occupied by robot %d" %id)

time.sleep(120)

await driveOffCharger(robot)

chargerOccupied = False

In this code, we define a worker which is responsible to check if the robot needs charging, and if so attempt to grab the lock so that it can access the critical section of moving to the charger and charging up.

The final part is firing the workers which will check on the two Vector robots, and the same can be easily done by the following code.

while True:

await asyncio.wait([worker(robot1, 1, lock),

worker(robot2, 2, lock)])

time.sleep(30)

In this piece, we fire up two workers (one for each robot) every 30 seconds. These workers check on the state of the robot and try to charge the robot if the battery voltage is running low.

Results

We monitor the battery voltage of both robots. When the robot sits on the charger, the battery voltage increases, while the voltage drains as soon as Vector drives away from the charger. The following figure shows a plot of the measured voltage for a 10 minute duration. Observe that when Vector 2 (orange curve) is charging, Vector 1 is draining. And vice-versa.

And here is an interesting video that shows you how this code plays out in practice…

Video of two Vector robots sharing the same charger

Hope you found this code fun and useful. Once again, you can access it here. It is always invaluable to have others try out this code. Also, if you have any thoughts or suggestions, please do leave it below. Claps definitely help too.

Please follow my publication: “Programming Robots” for more interesting tips. I also have an online course to teach AI with the help of Vector available at: http://robotics.thinkific.com I will feel honored to have you as a student.