-
Notifications
You must be signed in to change notification settings - Fork 0
Bridge - Simulator communication #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…r (to remove in future)
|
grazie della descrizione molto dettagliata e bel lavoro. Prima che guardo il codice vero e proprio, perche vuoi mettere tutta la roba di boostermotion dentro la repo del simulatore? |
|
se non sbaglio sul robot già ci dovrebbe essere booster-motion (non so neanche se è lo stesso identico o cambia per qualche motivo), quindi questo sarebbe circoscritto solo al simulatore. Però invoco gli altri a correggere se sbagliato, si può tranquillamente mettere dentro spqrbooster in realtà, non cambia nulla. |
|
booster motion sul robot c'è Non so dove può essere meglio mettere la roba di booster motion, forse un'idea può essere anche fare una repo separata con il bridge da inserire come sub-module in circus Comunque se puoi runna il pre-commit così risolvi i problemi di clang |
|
Secondo me booster motion deve stare dentro il framework, perchè è una cosa che riguarda il framework stesso, non il simulatore. Penso che puoi metterlo direttamente li. Anche la repo esterna andrebbe bene |
|
Scusa, ma per funzionare il bridge richiede di avere ROS sulla macchina? |
nel docker credo |
|
dentro al docker già c'è ros, però vi serve anche a voi perché il bridge va compilato a parte (con cmake, non colcon). Volendo posso mettere la compilazione direttamente dentro al docker |
Però effettivamente se booster motion sta sul robot non dovrebbe essere messo nel framework |
Si ma l'immagine docker è specifica del framework. Diversi framework hanno dipendenze diverse e quindi immagini diverse. |
eh si esatto, ma la stessa cosa vale per il simulatore, non tutti i framework lo usano nello stesso modo. Diciamo che il bridge è nel mezzo, non dovrebbe sare direttamente nel framework, ma neanche nel simulatore |
Vero, forse mettere il bridge in una repo separata ed aggiungerlo come submodule è la cosa migliore |
|
per ora ho messo la compilazione del bridge dentro al docker, cosi non dovete avere ros per buildarlo. Appena spostiamo su una repo a parte la togliamo |
|
nella nuova repo simbridge ho spostato sia il bridge che stava su spqrbooster sia booster-motion. A parte la compilazione del bridge, stessi passaggi di prima per provare il tutto (quindi crea l'immagine, modifica framework_config.yaml , ...) |
src/Container.cpp
Outdated
| // Add host library paths to binds | ||
| std::vector<std::string> all_binds = binds; | ||
| all_binds.push_back("/usr/lib/x86_64-linux-gnu:/host/usr/lib/x86_64-linux-gnu:ro"); | ||
| all_binds.push_back("/lib/x86_64-linux-gnu:/host/lib/x86_64-linux-gnu:ro"); | ||
|
|
||
| payload["HostConfig"] = { | ||
| {"Binds", all_binds}, | ||
| {"IpcMode", "host"}, | ||
| {"CapAdd", {"SYS_NICE", "IPC_LOCK"}}, | ||
| {"SecurityOpt", {"seccomp=unconfined"}}, | ||
| {"Ulimits", nlohmann::json::array({ | ||
| { | ||
| {"Name", "memlock"}, | ||
| {"Soft", -1}, | ||
| {"Hard", -1} | ||
| } | ||
| })} | ||
| }; | ||
|
|
||
| payload["Env"] = { | ||
| "ROBOT_NAME=" + robot_name, | ||
| "SERVER_IP=172.17.0.1", | ||
| "CIRCUS_PORT=" + std::to_string(frameworkCommunicationPort), | ||
| "LD_LIBRARY_PATH=/app/booster_motion/lib:/app/booster_motion/lib-usr-local:/app/booster_motion/lib-x86_64-linux-gnu:/host/usr/lib/x86_64-linux-gnu:/host/lib/x86_64-linux-gnu", | ||
| "FASTRTPS_DEFAULT_PROFILES_FILE=/app/booster_motion/fastdds_profile.xml" | ||
| }; | ||
|
|
||
| payload["Env"] | ||
| = {"ROBOT_NAME=" + robot_name, "CIRCUS_PORT=" + std::to_string(frameworkCommunicationPort)}; | ||
| payload["Tty"] = true; | ||
| payload["OpenStdin"] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Queste modifiche devono essere fatte a livello di immagine, non a livello di codice dentro la simulazione, altrimenti stiamo facendo qualcosa su misura
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ha senso, però alcune cose da chiarire:
{"Binds", all_binds},
{"IpcMode", "host"},
{"CapAdd", {"SYS_NICE", "IPC_LOCK"}},
{"SecurityOpt", {"seccomp=unconfined"}},
{"Ulimits", nlohmann::json::array({
{
{"Name", "memlock"},
{"Soft", -1},
{"Hard", -1}
}
})}
non credo possano essere messe nel dockerfile perché sono cose da specificare al lancio del docker, non nel dockerfile. In caso potrebbero essere messe in un dockercompose, se può servire posso vedere come poi chiamarlo da codice perché non ne ho idea.
Gli altri binds sono riuscito a toglierli perché ho trovato le ultime librerie mancanti che servivano a booster-motion, le scarico direttamente nel dockerfile.
Questi invece:
"LD_LIBRARY_PATH=/app/booster_motion/lib:/app/booster_motion/lib-usr-local:/app/booster_motion/lib-x86_64-linux-gnu:/host/usr/lib/x86_64-linux-gnu:/host/lib/x86_64-linux-gnu",
"FASTRTPS_DEFAULT_PROFILES_FILE=/app/booster_motion/fastdds_profile.xml"
possono essere messi nel dockerfile senza problemi, però per farlo devo caricare booster-motion dentro al container direttamente nell'immagine e non come volume. Il che ha anche senso perché non è da compilare ma già pronto, però ognuno che si crea l'immagine deve andare a modificare nel dockerfile il path rispetto a dove ha messo booster-motion. Per me si può fare, però ditemi anche voi che ne pensate
| void AppWindow::startSimulation() { | ||
| if (sim && !sim->isRunning()) { | ||
| sim = std::make_unique<SimulationThread>(mujContext->model, mujContext->data); | ||
| sim->start(); | ||
| } | ||
| } | ||
|
|
||
| void AppWindow::stopSimulation() { | ||
| if (sim && sim->isRunning()) { | ||
| sim->stop(); | ||
| } | ||
| } | ||
|
|
||
| void AppWindow::stepSimulation() { | ||
| if (mujContext) { | ||
| mj_step(mujContext->model, mujContext->data); | ||
| viewport->update(); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Io toglierei i bottoni, e lascerei l'avvio della simulazione come era prima.
Lasciamolo fare ai frontendisti.
Immagino ti serva stoppare la simulazione fin quando i robot non si connettono giusto? Puoi semplicemente usare qualsiasi tecnica di thread communication per far comunicare il server e il main thread. Quando il server dice che i robot sono connessi, il main thread fa partire sim->start
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ho fatto il meccanismo per far partire la simulazione automaticamente (è l'ultima commit, ti richiedo la review cosi la vedi). Devo per forza distringuere il caso connected da ready perché l'essere connessi non implica essere pronti (causa booster-motion).
L'altra strada che si poteva seguire è questa: usare un array dentro RobotManager grande quanto l'array robots dove ogni elemento i-esimo è un booleano che dice se il robot i-esimo è pronto per essere lanciato. Questo rende le computazioni più leggere ma non associa intrinsecamente l'essere connessi e l'essere pronti al robot.
Pensavo potesse essere comodo per il futuro avere l'informazione direttamente nel robot.
|
|
||
| // Receive initial message with robot name | ||
| char buffer[MAX_MSG_SIZE]; | ||
| int n = read(client_fd, buffer, sizeof(buffer) - 1); | ||
|
|
||
| if (n <= 0) { | ||
| std::cerr << "Error reading the initial message.\n"; | ||
| // close(client_fd); | ||
| continue; | ||
| } | ||
|
|
||
| // unpack of the MsgPack message | ||
| msgpack::object_handle oh = msgpack::unpack(buffer, n); | ||
| msgpack::object obj = oh.get(); | ||
|
|
||
| // First message is the robot name as a string | ||
| if (obj.type != msgpack::type::STR) { | ||
| std::cerr << "First message must be a string. Ignore it...\n"; | ||
| continue; | ||
| } | ||
|
|
||
| std::string robotName = obj.as<std::string>(); | ||
| std::cout << "Connected Robot: " << robotName << "\n"; | ||
|
|
||
| // Send message with initial state | ||
| for (auto& r : robots_) { | ||
| if (r->name == robotName) { | ||
| std::cout << "Sending initial message to " << robotName << std::endl; | ||
| auto answ = r->sendMessage(); | ||
| msgpack::sbuffer sbuf; | ||
| msgpack::pack(sbuf, answ); | ||
| send(client_fd, sbuf.data(), sbuf.size(), 0); | ||
| break; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client_fd >= 0 significa che qualcuno si è appena connesso? Perchè ci sta tutta questa logica ripetuta se tanto ti comunica tutto nel primo messaggio che ti manda? Sembra ridondante.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
riguardo client_fd>=0 si.
La logica è ripetuta tranne che per la ricezione del messaggio (rispetto a sotto se guardi non c'è r->receiveMessage(), ma direttamente r->sendMessage()), questo perché dentro di sé receiveMessage applica le torque che riceve (cosa che nel caso ideale nel primo messaggio noi non vogliamo).
Il motivo è il seguente: booster-motion, per pubblicare i comandi da applicare, deve avere già a disposizione i joint states e l'imu del robot corrispondente. Questo significa che la pipeline corretta nella prima iterazione dovrebbe:
- simulatore manda joint e imu al bridge
- bridge pubblica joint e imu
- booster-motion legge joint e imu e pubblica torque
- bridge legge torque e le manda al simulatore
Per questo motivo ho differenziato il caso del primo messaggio da tutti gli altri (infatti nel primo messaggio mando solo il nome del robot e non le torque).
Per togliere questo codice il primo messaggio deve contenere anche delle torque fasulle (che possono essere tutte 0 ad esempio), che però verranno applicate nel primo step. Non penso possa creare problemi, però ufficialmente questa è la pipeline corretta in teoria
Gotta have both of them in the dockerfiles folder tho
Cosa raggiunge questa PR
Questa PR aggiunge il meccanismo di comunicazione tra bridge (c'è un'istanza di bridge dentro ogni istanza di docker, quindi un bridge per robot) e simulatore.
La comunicazione tra bridge e simulatore è stata divisa in due step (dovuto alla necessità di avere un determinato workflow necessario al funzionamento di booster-motion):
Quando il docker di un robot parte, viene mandato un messaggio dal bridge al simulatore contenente solo il nome del robot. Si può considerare come se il robot si fosse connesso al simulatore.
Il simulatore risponde a questo messaggio con
dati dei joints + imuche vengono inviati al bridgenome del robot + joint commands (torque). Il simulatore manda al bridge sempredati dei joints + imu.Ho anche aggiunto booster-motion nella cartella
resources/booster-motion, in modo che lo abbiamo tutti e nello stesso path. Ci sono cose in più da quella cartella che si possono eliminare, poi si farà.Come far partire la simulazione
Il funzionamento della simulazione, al momento, è strettamente correlato al branch
sim-bridgedi spqrbooster2026. Con altri branch non funziona.Requisiti
Spqrbooster2026
Sulla repo spqrbooster2026, assicuratevi di essere sul branch
sim-bridgedi spqrbooster2026.Una volta che siete su questo branch, dovete buildare il bridge:
Non serve fare altro su spqrbooster2026.
Circus
Per prima cosa bisogna creare l'immagine docker, usando il Dockerfile nella cartella
dockerfiles:Una volta creata l'immagine docker, dovete modificare il file
resources/config/framework_config.yaml:inserendo i vostri path delle repo (ATTENZIONE: non modificare i path
/app/booster_motione/app/bridge).Fatto questo, dovreste essere in grado di lanciare la simulazione con:
Simulazione
Una volta aperta la scena, la simulazione parte in pausa. Se guardate sul terminale, comparirà una stampa con scritto:
Connected robot: <nome_robot>. Prima di premere il pulsanteStart, aspettate che sul terminale compaia anche la stampaReceived message(PS: compariranno infinite stampe, lo so è una cosa da cambiare in modo più fancy :) ). A questo punto, avviando la simulazione, vedrete che i robot rimangono in piedi, oscillando leggermente. Al momento non si possono far muovere perché dentro al docker non c'è un framework che fornisce i comandi. Metterò nei prossimi giorni una cosa dummy per farlo muovere.Video.del.02-12-2025.12.20.42.webm
NEL CASO IL ROBOT CASCASSE
Significa che o il bridge o booster-motion sono crashati. Per capire qual è il problema, lasciate il simulatore aperto e aprite un altro terminale. Cercate l'id del container con
docker ps -aed entrate nel container:Aperto il terminale del container, dovreste poter vedere il problema facendo:
In caso servisse, potete vedere anche i file di errore e di log
/var/log/booster-motion.err,/var/log/booster-motion.log,/var/log/bridge.err,/var/log/bridge.log.Dettagli booster-motion
Una carrellata di roba su booster-motion, da lasciare ai posteri (sono tutte già fatte dentro al container, sono solo per informazione):
resources/booster-motionnelle cartellelib,lib-usr-local,lib-x86_64-linux-gnu./opt/booster/configsci sia il filesystem_settings_config.yaml(l'ho messo nella cartelladockerfiles/configs)Container.cpp):resources/booster-motion/fastdds_profile.xmlper la comunicazione dei topicProssimi Step
Received Messagecon una stmapa sola che dice "Robot X è pronto"