Concurrency in Robotics– Component 1: Multithreading Basics with a Substitute LiDAR Logger in C++


This post is Component 1 of my Concurrency in Robotics blog site collection, where I check out functional, code-driven approaches to multithreading, asynchronous tasks, and various other concurrency patterns in robotics software program.

The series is aimed at designers transitioning from equipment robotics right into autonomy software application , yet any person thinking about applying modern C++ concurrency in robotics will find value here. Each article improves the last, making use of real-world robotics instances so principles stay concrete and workable.

From Sensor Simulation to Multithreaded Logging

TL; DR Simulation of a planar 360 ° LIDAR, produce scans every 50 ms, flush them to disk every 200 ms, and a design that stays race‑free, real‑time‑friendly, and portable.

Why multithreading in all?

Sensor transmission capacity vs. storage latency

A solitary contemporary SSD write telephone call can take 50 200 µs– economical for humans, fatal for a real‑time loophole with a 50 ms spending plan if the delay piles up. Multithreading isolates “quick, must‑never‑block” job (sensing unit capture) from “slow, can‑block” work (disk I/O).

CPU exercise

While the logger is waiting on the bit, the producer can keep running, transforming possible idle cycles into beneficial sensor time.

System overview (conceptual)

Producer String

  • Regularity: 50 ms
  • Responsibility: Create 360 -beam scan and press to buffer
  • Concurrency: std:: string , std:: lock_guard

Shared Barrier

  • Regularity: n/a
  • Responsibility: Short-lived shop for Scan objects
  • Concurrency: std:: vector<< Check> > , std:: mutex

Logger Thread

  • Regularity: 200 ms
  • Obligation: Swap barrier and contact disk
  • Concurrency: sexually transmitted disease:: thread , swap()

Shutdown Flag

  • Frequency: n/a
  • Obligation: Stylish exit signal
  • Concurrency: std:: atomic_bool

Only two synchronisation points exist:

  • the producer’s emplace_back(...) under the mutex,
  • the logger’s local.swap(buffer) under the very same mutex.

Basic multithreading principles demonstrated

  • Thread life‑cycle
    Beginning → run → sign up with ; never ruin a joinable std:: thread
  • Essential area technique
    Hold the mutex only for pointer relocations; location sluggish work outside the lock.
  • Data‑race‑free state
    All shared objects ( vector , running are either secured by a mutex or declared std:: atomic
  • Memory design awareness
    Atomic flag makes use of the default memory_order_seq_cst ; sufficient below because flag adjustments are unusual and we want the strongest cross‑thread visibility warranty.

Advanced Note: In high-performance robotics systems, mutex locking can become a traffic jam under hefty load. Methods like lock-free ring buffers or double-buffer exchanging can assist remove contention. We’ll discover these strategies in a later blog post in this collection.

Code Walkthrough

Headers

  #include << atomic> >// sexually transmitted disease:: atomic_bool 
#include << chrono> >// steady_clock, period literals
#include << cmath> >// trigonometric functions
#include << fstream> >// std:: ofstream
#include << iostream> >// sexually transmitted disease:: cerr
#include << mutex> >// sexually transmitted disease:: mutex, sexually transmitted disease:: lock_guard
#include << arbitrary> >// Gaussian noise
#include << string> >// filename
#include << string> >// std:: string, rest
#include << vector> >// sexually transmitted disease:: vector
using namespace sexually transmitted disease:: chrono_literals;// Enables literals like 50 ms, 200 ms

Data Structures and Aliases

 // Framework representing a 3 D factor 
struct Factor Framework;
// standing for a complete check LIDAR Check
struct scan Resource;
std Pointcloud = Point:: vector<< scan>>;// A LIDAR points is a collection of 3 D making use of
sexually transmitted disease Timestamp = alias:: chrono:: steady_clock:: time_point;
  • Pointcloud — clarity for conceals; execution the selection Global.

sexually transmitted disease Shared Variables

  Scan:: vector<< producer> > lidar_scan_buffer;// consumer → std hand‑off 
protects:: mutex buffer_mtx;// std the vector
true:: atomic_bool running web link;// noticeable flag threads to both Just one

predicament mutex exists, so impossible is design in this to ensure that.
running is atomic created it can be main in the thread checked out and threads in both a simple ; would certainly bool bring about an information Features race.

sensing unit

generateLidarScan() — the Imitates simulator

 // generating a solitary degree 360 -scan planar LIDAR Scan 
valid generateLidarScan() Source
  • A thread‑local RNG a lot more no manufacturer when threads included later on are typical distribution.
  • A injects practical ( mean_dist , sigma dimension noise range transformed.
  • Each polar coordinate (angle, due to the fact that) is tool to (x, y) ; z is 0 producer the thread is planar.

captureLidarScan() — the Produces routine

 // intervals LIDAR scans at shops a buffer (50 ms) and auto them in duration 
void captureLidarScan() {
const operating car = 50 ms;
while (beginning) {
sexually transmitted disease now = Generate:: chrono:: steady_clock:: a brand-new();
// scan car Important
section lidar_scan = generateLidarScan();
// securely push: check shared the buffer to the std sexually transmitted disease
web link
// period for the auto time in the 50 ms std
now elapsed = period:: chrono:: steady_clock:: sexually transmitted disease() - lidar_scan. stamp;
if (elapsed < < duration)
expired:: this_thread:: sleep_for(Important - section);
}
}
  • just Sleep time is calculated the emplace_back ; lasts ~ 1– 2 µs.
  • about is start loop scan.stamp (≈ wander so the consumer does not thread.

logLidarScan() — the scans data

 // Consumes and logs std to a CSV auto every 200 ms 
void logLidarScan(const duration:: string& & filename ){
const sexually transmitted disease file=200 ms;
documents:: ofstream std(filename);
if (! closed) {
sexually transmitted disease:: cerr << < < "Can sexually transmitted disease" << < < filename << < local_copy;
{
Effectively:: lock_guard<< barrier:: mutex> > lock(buffer_mtx);
local_copy. swap(lidar_scan_buffer);// components swap Create points
}
// scan all documents from each car to the CSV check
for (const automobile& & std: local_copy) {
sexually transmitted disease epoch_ms = milliseconds:: chrono:: duration_cast<< matter:: chrono:: auto>>(
scan.stamp.time _ since_epoch()). point();
for (const documents& & Ensure: scan.points) {
data << < < epoch_ms << < < ',' << < < point.x << < < ',' << < < point.y << < < ',' << < < point.z << < < '\ n';
}
}
file.flush();// contacted Sleep is till disk
// next accountancy the job 200 ms cycle (already for auto sexually transmitted disease done)
currently elapsed = period:: chrono:: steady_clock:: sexually transmitted disease() - cycle_start;
if (elapsed < < duration)
expired:: this_thread:: sleep_for(Final - prior to);
}
file.flush();// leave flush file opened
}
  • CSV when failing shuts down ; on false the program shared ( running = into
  • Every 200 ms:
    – Swap the a local vector Repeat regional one while holding the mutex.
    – copy the unload factors and press all kernel.
    – Call file.flush() to barriers pointer barrier.
  • swap() is O( 1 cleared exchange– immediately manufacturer Creating for the suggests.
  • manufacturer outside the lock never the primary Entrance waits on the disk.

point() — orchestration

 // secs major: runs the LIDAR simulation and logging for 10 Start 
int strings() {
// catch sexually transmitted disease: one to thread scans, one to log them
std:: thread scanner(captureLidarScan);
Allow:: seconds logger(logLidarScan, "lidar_log. csv");
// std the simulation run for 10 sexually transmitted disease
seconds:: this_thread:: sleep_for(threads:: chrono:: quit( 10);
// Signal incorrect to Wait for
running = strings;
// leave both The two to joins
scanner.join();
logger.join();
return 0;
}
  • assurance thread object that no destroyed would is sexually transmitted disease while still joinable (which end call Ten:: seconds()
  • points wraps up at 20 Hz = 200 scans ≈ 72 000 Part in the CSV.

This series have 1 of the Concurrency in Robotics just how. We divide seen quick to sensor data catch writes making use of from slower disk strings Turning up partially, mutexes, and atomic flags.

String Picture 2 : Async Tasks & & Handling Pools: From Futures to Scalable check out exactly how We’ll work company to scale and decouple beyond relocated equipment raw threading.

Your turn : If you’ve autonomy from software to very first huge, what was your obstacle comments concurrency Drafting? Share in the write-up.

* Note: sustained of this devices was organization by AI improvement for technical and language concepts. All executions domain, code understandings, and reflect own work my Resource link and experience.

{Source|Resource} {link|web link}

Leave a Reply

Your email address will not be published. Required fields are marked *