Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Robotics from first principals: toy examples

Python License

Intro

In robotics, many things happen simultaneously — sensors gather data, motors respond, and controllers make decisions — and we need to process them appropriately. This is one of the main challenges in robotics programming.

The naïve idea is simple: just read all sensors one at a time in an event loop, as frequently as possible.

That approach works, but it has several drawbacks. For example, each sensor may need to be read at a different frequency. It’s enough to read a temperature sensor once a minute, but a video camera or accelerometer must be read much more often. Reading all sensors at the same frequency causes unnecessary CPU overhead and power consumption — which are critical issues for embedded systems.

Table of content

Cooperative scheduling

One way to solve this problem is cooperative scheduling.

Cooperative scheduling strategy — a way to organize concurrent tasks so that each sensor loop voluntarily gives up control (cooperates) rather than being forcibly interrupted (preempted).

A cooperative scheduler stands in contrast to preemptive scheduling.

In preemptive scheduling, you run each sensor-reading loop in a separate thread or process, and the operating system (OS) scheduler decides when a thread or process has run long enough and should pause to let another run — even if the first thread hasn’t finished its current operation.

How Cooperative Scheduling Is Organized Programmatically

Each sensor is read in a loop. After its first reading, it sends (emits) a message to the controller loop (the receiver) through a pipe, which is essentially a queue.

During each iteration, the sensor (emitter) sends not only the reading itself but also a command to the controller — usually a Sleep command, but it could also be something else (e.g., logging or control instructions).

Note that Sleep command is not an actual OS sleep — it’s an instruction interpreted by the controller to decide when to next schedule the sensor.

Overall, in each loop iteration, every sensor sends both:

  • Its reading (e.g., temperature = 22.4°C), and
  • A command, like Sleep(2), meaning “wait 2 seconds before asking me again.”

Since the sensor loop is organized as a generator, it stops at the yield Sleep(1) statement, gives control back to the controller loop, and waits until the controller resumes it for the next iteration. This means the sensor is not running on every tick (loop iteration) — avoiding unnecessary energy and CPU consumption — but only when necessary for that specific sensor.

Here is a very simple example of how cooperative scheduling is organized cooperative_scheduling_sensor_time.py

Combining Cooperative Scheduling with Multiprocessing

Now, let's try to scale this approach up a little and demonstrate how a larger system might be designed by utilizing both a cooperative loop (for controllers and non-demanding sensors) and a multiprocessing setup, where demanding sensors such as a camera run in a separate process and send data via a shared variable or a shared memory block.

In principle, the camera could also use a multiprocessing queue. However, since it sends a large amount of data every second, the overhead of using a queue would be enormous. Therefore, using shared memory is a more efficient way to handle such a high data throughput between processes.

The full example is here: v2_interprocess_shared_memory

How Data Exchange via Shared Memory Is Implemented

When we run a sensor generator (for example, a camera) in a separate process, we can use a shared queue to send its data from emitter to receiver. For interprocess communication, this would typically be a multiprocessing.Queue.

Queues are convenient and safe for passing small messages but introduce significant serialization and copying overhead for large data streams such as camera frames.

A much more efficient approach is to use a dedicated block of memory — shared memory — to exchange data directly between two processes (the sensor-emitter and the controller-receiver).

About

Core software concepts for robotics programming

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages