10. QoS — the most common trap

Important

This is not an advanced topic. It is a leading cause of beginners losing a whole day with perfectly correct code.

The classic symptom: ros2 topic list shows the topic, ros2 node list shows every node, there is not a single error — and no data reaches anything.

What QoS is

An agreement about how data is delivered. The publisher offers terms; the subscriber requests terms. If they are incompatible the two never connect, and nothing reports it.

The three that break things most often:

Setting

Options

Meaning

Reliability

RELIABLE

Retry until delivered — nothing may be lost

BEST_EFFORT

Send and move on, loss is acceptable — faster

Durability

VOLATILE

Late joiners get nothing

TRANSIENT_LOCAL

The last value is kept for late joiners

History

KEEP_LAST (depth)

How many recent messages to hold

The compatibility rule — remember only this

Warning

🔴 The publisher must offer at least as much as the subscriber requests.

Publisher offers

Subscriber requests

Result

RELIABLE

RELIABLE

✅ connects

RELIABLE

BEST_EFFORT

✅ connects (offering more than asked)

BEST_EFFORT

RELIABLE

🔴 no connection, complete silence

BEST_EFFORT

BEST_EFFORT

✅ connects

The red row is the one that actually happens, almost every time, because:

  • Most sensors publish BEST_EFFORT (LiDAR, camera, IMU) — data arrives constantly, and a dropped frame is replaced 0.1 s later

  • The default for create_subscription is RELIABLE

So a straightforward subscriber listening to /scan receives nothing at all.

How to diagnose — one command

ros2 topic info -v /scan
Type: sensor_msgs/msg/LaserScan

Publisher count: 1
Node name: lidar_node
QoS profile:
  Reliability: BEST_EFFORT      ← all the publisher offers
  Durability: VOLATILE

Subscription count: 1
Node name: my_node
QoS profile:
  Reliability: RELIABLE          ← subscriber wants more → no connection
  Durability: VOLATILE

Tip

-v is the whole point. Without it you see only the publisher and subscriber counts, which both read 1 — everything looks fine while nothing is connected.

Memorise this command. It saves more time than anything else in this guide.

The fix

Use the profile that matches the kind of data:

from rclpy.qos import qos_profile_sensor_data

self.sub = self.create_subscription(
    LaserScan, '/scan', self.on_scan,
    qos_profile_sensor_data)          # instead of the plain 10

qos_profile_sensor_data is BEST_EFFORT with depth 5 — what sensors publish.

To set it explicitly:

from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy

qos = QoSProfile(
    depth=5,
    reliability=ReliabilityPolicy.BEST_EFFORT,
    durability=DurabilityPolicy.VOLATILE,
)

The nested trap — ros2 topic echo follows the same rule

ros2 topic echo is itself a subscriber, so it can hit the same mismatch.

If echo is silent but RViz shows data, try:

ros2 topic echo /scan --qos-profile sensor_data

Danger

🔴 Never conclude “there is no data” from a silent echo alone.

People have gone as far as replacing a LiDAR cable while the data was flowing perfectly the whole time — their inspection tool simply could not connect.

Always check ros2 topic info -v before concluding anything about the hardware.

TRANSIENT_LOCAL — data sent once

Some topics publish once and then go quiet:

Topic

Why

/map

The map is sent once when loaded

/tf_static

Sensor positions never change

With VOLATILE, a node that starts later never receives the map — it missed the one transmission.

from rclpy.qos import QoSProfile, DurabilityPolicy

qos = QoSProfile(depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL)
self.sub = self.create_subscription(OccupancyGrid, '/map', self.on_map, qos)

Note

The symptom is “it works if I start things in one order but not the other”, which looks like a timing problem but is really QoS.

What to remember

"Everything looks fine but no data"  →  ros2 topic info -v first, always

Sensors (LiDAR, camera, IMU)         →  qos_profile_sensor_data
Map, tf_static                        →  TRANSIENT_LOCAL
Control commands (/cmd_vel)           →  the default (RELIABLE) is correct

Important

That is the end of Part 1. You should now be able to:

  1. Read a system — what nodes exist, and which topics they use to talk

  2. Write your own nodes that publish and subscribe

  3. Diagnose why data is not arriving, which is what matters most once real hardware is involved

Part 2 starts connecting real hardware, beginning with making the wheels turn.


Previous: 9. RViz · Back to: ROS 2 for Mobile Robots