Skip to content

Shared-memory communication option - #37

Open
FlavioFoxes wants to merge 24 commits into
mainfrom
shm
Open

FlavioFoxes wants to merge 24 commits into
mainfrom
shm

Conversation

@FlavioFoxes

@FlavioFoxes FlavioFoxes commented Sep 4, 2026

Copy link
Copy Markdown
Member

What this PR addresses

Questa PR introduce l'opzione di avere la comunicazione tra simulatore ed esterno tramite shared memory, che garantisce performance molto più alte rispetto alla socket.

SharedMemory classes

Ci sono due classi template (SharedMemoryReader e SharedMemoryWriter) che creano un canale di comunicazione tramite shm per un qualsiasi tipo di dato per cui vengono dichiarate. Valgono anche per tipi di dato composto (e.g. struct come nel nostro caso), ma hanno la limitazione che devono essere dati trivially-copyable, cioè la loro size deve essere conosciuta a tempo di compilazione e non devono stare nell'heap (per cui std::vector o simili non valgono); questo è dovuto al fatto che è implementato essenzialmente come una memcpy (visto che a noi serve la massima velocità di trasmissione possibile, dovrebbe essere la cosa migliore).
Per questo motivo sono state introdotte delle struct Data all'interno delle classi dei sensori, che creano l'interfaccia scrivibile su shm.
Sono state introdotte anche delle struct Meta, che servono come identificativo di quello che stiamo mandando (aiuta anche per generalizzare rispetto all'invio delle immagini)

SHM Connection

Se con la modalità di connessione tramite socket i robot si connettevano al simulatore inviando un messaggio con il proprio "nome", e la simulazione viene avviata nel momento in cui tutti i robot sono connessi, con SHM la connessione viene considerata attiva nel momento in cui viene il file _<nome_robot>commands.shm viene detectato (è il file che creato dal robot, non dal simulatore).

Simulation Loop

Il loop di simulazione in SimulationThread adesso è come segue:

  • per ogni step di simulazione ci sono N step di controllo.
  • Lo step di simulazione nel nostro caso dovrebbe essere di 20ms per essere considerato in real-time (perché il nodo di Brain gira a 50Hz = 20ms), quindi noi riceviamo un comando ogni 20ms e dobbiamo far avanzare di 20ms al simulazione
  • invece che applicare un solo step di fisica (che è troppo grande per essere integrato adeguatamente), applichiamo kDecimation steps più piccoli, dove ad ogni substep reinviamo lo stato del robot e riceviamo il nuovo comando
    subcomando da applicare
 for(int i=0; i<kControlDecimation; ++i){
     mj_step1(model_, data_);
     RobotManager::instance().applyCommands();
     mj_step2(model_, data_);
     RobotManager::instance().update();
     GameController::instance().update();

     std::memset(data_->xfrc_applied, 0, model_->nbody * 6 * sizeof(mjtNum));

     if (maxSimulationTime_ > 0 && data_->time >= maxSimulationTime_) {
         running_ = false;
         emit maxSimulationTimeReached();
         break;
     }

     // Camera frames only change once per GUI-thread render cycle, so only
     // publish them on the last substep of each control step rather than
     // re-copying them on every one of the kControlDecimation substeps.
     const bool publishImages = (i == kControlDecimation - 1);
     
     RobotManager::instance().sendStateMessages(publishImages);
     RobotManager::instance().receiveCommandMessages();                

 }

Per funzionare real-time, uno step intero (quindi la somma di tutti i substep) non deve impiegare più di 20ms. Per questo motivo ho introdotto un booleano per la funzione sendStateMessages, di modo che le immagini vengano inviate solo una volta per ciclo (riduce drasticamente il tempo, anche con la shm)

I cambiamenti del funzionamento sono scritti nel commento lasciato nel file
A questo punto dell'implementazione c'è una bozza di struttura per la shm in direzione simulatore->simbridge.
Le struttture di astrazione forse vanno aggiustate un po'. Per ora pusho per non perdere traccia
Implementazione di base completata in entrambe direzioni circus <-> simbridge per lo scambio di messaggi frequenti. Al momento la parte di connessione dei robot al simulatore avviene ancora tramite socket, è da smantellare
TODO 1: togliere connessione iniziale dei robot tramite socket
TODO 2A: modificare writer/reader delle immagini per usare la classe template
TODO 2B: integrare le immagini direttamente dentro lo stato del robot
TODO 3: refactoring (rivedi anche astrazioni delle classi)
TODO 4: mettere doppia modalità (shm/socket) tramite file di config, cosi da usare quella che si vuole (default: shm)
Unificato classe ImageSharedWriter/Reader nella classe SharedMemoryWriter/Reader. Sembra funzionare (da refactorare)
Argomento element_count era diventato fisso a 1 dopo l'aggiunta di trailing_bytes, quindi è stato rimosso come argomento delle funzioni
…endMessageSocket

Spostamento di send_all temporaneo in un utils, in modo che non ci sia l'implementazione all'interno del simulation thread.
Funzione sendMessage rinominata in packMessage perché invio tramite socket non avviene lì  dentro.
Funzione sendMessageSocket per invio tramite socket direttamente dentro Robot.h
La connessione iniziale dei robot al simulatore non funziona tramite scambio di messaggi, ma il simulatore controlla se il file in shm creato dai container dei robot esistono. Se c'è, considera quel robot connesso.
Aggiunta connection mode che può essere "shm" o "socket" (da refactorare in maniera più chiara, al momento credo ci sia qualcosa di hardcodato)
…tManager + connection choice added

Tutta la logica di comunicazione è stata spostata in RobotManager, per mantenere il thread di Simulazione più snello (vediamo la gestione della comunicazione dei robot come qualcosa riguardante il loro Manager).
Aggiunta la possibilità di scegliere la modalità di connessione tramite argomento al lancio (mancano ancora alcune cose per completare entrambe).
Adesso le due modalità shm e socket sono entrambe funzionanti. Reso alcune funzioni private per rendere codice ad alto livello più pulito
Adesso con shm riusciamo  a stare in realtime senza problemi (almeno con un robot solo). Ho separato l'invio e ricezione delle immagini dallo stato, poiché non serve inviarle ad ogni ciclo di update di decimazione, ma una sola volta per ciclo di running. Questo fa risparmiare parecchio tempo.

Va refactorato un po'
@torchipeppo

Copy link
Copy Markdown
Contributor

Ho fatto un po' di prove, funziona ed è una scheggia. Flavio sa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants