When nothing happens
A symptom-to-cause table for the failures that stop people on their first afternoon.
Almost every one of these is a correct behaviour presenting as a broken one. The SDK reports what it can, but a great deal of the system communicates by silence: a command that landed on the wrong robot, a topic name with a typo, and a simulator that is not playing all look the same from inside your loop.
Two commands answer most of it before you read any further:
vrobots topic list
vrobots --version
Symptoms and causes
| Symptom | Likely cause | What to do | Where it is explained |
|---|---|---|---|
topic list shows (no topics) | The simulator is not in Play mode | Press Play; loading the project is not enough | First contact |
| The simulator is on another machine | Pass --router tcp/<host>:7447 | Two transports, one simulator | |
| zenoh discovery had too short a window | Retry with -t 5 | The vrobots command | |
A -k filter excluded everything | Drop the flag | The vrobots command | |
topic list marks a row (stale: no process attached) | The process that created that iceoryx2 record is gone, and the record stayed on disk | Once nothing is running, delete the leftover *.service files under the iceoryx2 root that the footer names | The vrobots command |
connect hangs, then times out | The sys_id does not exist in this scene | Read the ids out of topic list; ids are reallocated on every scene load | System ids |
connect(type, None) was refused by the scene’s catalog | Only multirotor, truck and msd are creatable in the sandbox; attach to the rest by id | Robot lifecycle | |
The first state sample never arrived within probe_timeout | Run with RUST_LOG=vrobots_sdk=debug to see which of the four connect steps stalled | What connect actually does | |
| A command has no visible effect | It reached a different robot | Compare the sys_id you connected with against topic list | System ids |
| The value is inside the band but does nothing useful, for example 1501 on a multirotor | Watch s.actuator.pwm: if it echoes your value, the command landed and the plant is the question | Hello control | |
| The array length is wrong for that robot | The simulator logs and ignores it, keeping the previous latched value; the SDK cannot see that log | Commands latch | |
| Nothing acts on that command id yet | SET_MR_THROTTLE and the body wrench commands are on the wire and unimplemented | Commands nothing acts on | |
A service acked ok and refused the value | Only srv/skin ever answers ok = false; measure the result in the state stream instead of trusting the ack | Five rules that explain everything | |
| Camera frames never arrive | The simulator is not on this host | iceoryx2 is shared memory: frames cannot cross machines even when zenoh can | Two transports, one simulator |
| The iceoryx2 pin differs from the simulator’s | Compare vrobots --version against the simulator build; a patch mismatch delivers nothing and raises nothing | Versions and pins | |
open_camera was given a name, resolution or format that does not match the publisher | Copy the stream key from topic list and split it back into its three parts | Mount, open and unmount | |
You are calling fresh() faster than frames arrive | That is correct: fresh() returns None until a new frame lands. Use latest() if you want the current one regardless | Freshness | |
open_camera fails with a message that the service “exists but does not pair” | A stale iceoryx2 service record, usually from an older build | Stop the simulator and every client, delete the leftover *.service files under the iceoryx2 root that the message names, and retry | The vrobots command |
| Fields look like garbage | Schema drift between the SDK and the simulator | Compare vrobots_msgs and schema_version from vrobots --version against the simulator build | Versions and pins |
| A vector was read in the wrong frame | Read s.coord_frame_id: the truck publishes fru while the multirotor and the Global Hawk publish frd, so the third component means opposite things | Frames, axes and units | |
The quaternion was unpacked as [w, x, y, z] | The order is [x, y, z, w], matching the wire’s Vec4 field order | Frames, axes and units | |
| Pose and twist were assumed to share a frame | Pose is world frame, twist and acceleration are body frame, in both the truth and the estimate blocks | Kinematics | |
| An unconverged estimator is mirroring truth | Check estimate.valid before trusting estimate.kin | Truth, measured and believed | |
| A created multirotor will not move | Simulator bug: its rigidbody never integrates | Attach to the scene’s multirotor by id instead of creating one | See the callout below |
| The numbers stopped changing | The simulator stopped, and states() keeps returning the last snapshot forever | Detect it with wait_new_state(timeout), not by expecting an error | Stream health |
| The loop stutters at the right average rate | Samples are being dropped | topic hz reports max interval and gap count, which is what a mean rate hides | Measuring rates |
Sim bug. In simulator v3.0.0 a client-created multirotor does not move. Its rigidbody never integrates, so it hangs where it spawned and ignores every pulse width and even a direct body force, while its actuator echo and rotor-speed model answer perfectly normally. Created trucks and mass-spring-dampers have live physics, and the scene’s own multirotor flies. See Known simulator issues. The three examples that would otherwise create one,
ex21_reset,ex22_physical_paramsandex27_rotor_config, take an optionalsys_idin every language, so you can attach to the scene’s one instead.
When the table does not have it
Turn the logs up. RUST_LOG overrides whatever filter the program passed to
init_logging, and the SDK’s own log events name each connect step as it happens.
RUST_LOG=vrobots_sdk=debug cargo run -p vrobots-examples --bin ex01_hello_states
RUST_LOG is read by the Rust crate’s init_logging, so the other two surfaces turn the
volume up in their own idiom instead. Python routes the same events into the standard
logging module, so vrsdk.init_logging("debug") (or raising the vrobots_sdk logger’s
level yourself) is the equivalent. C++ registers a handler with vrsdk::set_log_callback and then calls
vrsdk::set_log_level(vrsdk::LogLevel::Debug). Logging
covers all three.
In Rust and C++ the SDK’s library holds zenoh’s and iceoryx2’s own events at warn,
whatever the filter or level asks for, so a zenoh=info directive adds nothing. iceoryx2
also has a native logger that writes to stderr, outside the SDK’s events entirely, and it is
controlled by IOX2_LOG_LEVEL, which the SDK defaults to errors only.
Two counters are worth printing from inside a loop that misbehaves without failing:
stats() carries received, decode error, sequence gap and missed sample counts, and
last_error() holds the most recent decode error. Neither tears down the session, which
is the point: a malformed payload is counted and the loop keeps running.
Next: Concepts
See also: Logging, Appendix C: Error reference, Known simulator issues