You add an image display in rviz, pick the depth topic, and the panel blinks black: a flash of depth, then black again at varying intervals, while the RGB image and the point cloud render steadily. Frames are arriving, so the camera and the launch file are fine. The pattern is transport, not optics: the depth topic is published RELIABLE while the rviz subscription runs BEST_EFFORT, a legal combination that lets the subscriber drop the heaviest stream on the rig. This guide gives you the two reliability rules, a two-check confirmation with one CLI command, the bandwidth arithmetic that explains the drops, and the settings to change on both ends.
What the blinking pattern tells you
Depth that flickers between a rendered frame and black has a specific signature, and three lookalikes fail it.
- The image shows, but in grayscale or bone-white: you subscribed to the infrared stream instead of depth. The depth camera RGB shows black and white guide covers that swap.
- The point cloud is the black one and you run inside a container: fix the GPU runtime passthrough for a point cloud black in Docker instead of touching QoS.
- The panel has never shown a frame and the rviz terminal prints a QoS incompatibility warning: that is the reverse mismatch, and the same two rules below fix it from the other side.
The blinking itself is rule 2 in action. RELIABLE on the publisher plus BEST_EFFORT on the subscription is a compatible pair, so the display connects and the topic count looks healthy. BEST_EFFORT tells the transport it may discard any sample instead of delaying or retransmitting it. Depth is the heaviest topic on the rig, so it is the stream where delivery fails first: most samples die in transit, the occasional survivor renders, and the panel alternates between a frame and whatever the driver emits in the gaps.
The two reliability rules that decide it
ROS 2 connects a publisher and a subscription only when the subscription's requests are satisfied by the publisher's offers. For reliability that leaves two rules.
- A subscription asking for RELIABLE refuses a BEST_EFFORT publisher. No frame ever arrives, and rviz prints an incompatibility warning.
- A subscription on BEST_EFFORT accepts a RELIABLE publisher. Frames flow, but each one may be dropped under load.
Durability behaves the same way at the edges: a TRANSIENT_LOCAL subscription against a VOLATILE publisher never connects, while the reverse pairing connects and just skips late-joining history. History and queue depth only tune how much jitter the link absorbs.
| Publisher | rviz subscription | What you see |
|---|---|---|
| RELIABLE | RELIABLE | Steady image; retransmission and the queue absorb jitter |
| RELIABLE | BEST_EFFORT | Blinking black on heavy topics, fine on small ones |
| BEST_EFFORT | RELIABLE | Nothing, ever, plus an incompatibility warning |
| BEST_EFFORT | BEST_EFFORT | Steady while the viewer keeps up, drops when it cannot |
Many camera drivers publish their image topics on the sensor-data profile: best effort, keep-last history, a queue of five. That profile suits a control loop that acts on the freshest frame only. A viewer or recorder that wants every frame should ask for RELIABLE, and then the publisher has to offer it.
Confirm the cause in two checks
Check 1 reads both endpoints with one command, using the registered depth topic as the example:
ros2 topic info /depth_registered --verbose
Read the Reliability line in each block of the abridged output:
Topic type: sensor_msgs/msg/Image
Publisher count: 1
Subscription count: 1
Endpoint type: PUBLISHER
QoS profile:
Reliability: RELIABLE
Durability: VOLATILE
Endpoint type: SUBSCRIPTION
QoS profile:
Reliability: BEST_EFFORT
Durability: VOLATILE
RELIABLE above BEST_EFFORT in the subscription block is the blinking case. The reverse pairing, a RELIABLE subscription under a BEST_EFFORT publisher, is the never-displays case that fixing an rviz2 image topic that is published but not displaying walks through end to end.
Check 2 measures the rate with ros2 topic hz /depth_registered. The publisher's average rate feeds the arithmetic in the next section, and a steady rate against a flickering display confirms the loss sits on the subscription side.
Worked example: 1280 × 720 float depth at 15 Hz on a best-effort subscription
Take the common case on a rig like this: a 1280 × 720 depth image, 32-bit float with one channel, so 4 bytes per pixel, published at 15 Hz — the depth rate when the color stream is configured for 15 fps. Substitute your measured rate from check 2; the method is unchanged. One MB counts as 1,000,000 bytes here.
The bandwidth rule for a raw image topic:
Arithmetic for this stream:
- 1280 × 720 = 921,600 pixels
- 921,600 px × 4 B = 3,686,400 B per frame, about 3.7 MB
- 3,686,400 B × 15 Hz = 55,296,000 B/s, about 55.3 MB/s
- 55,296,000 B/s × 8 = 442,368,000 bit/s, about 442 Mb/s
One topic drawing roughly 55 MB/s, which is 44 percent of a gigabit wire. The wire is not the limit; the subscriber is. rviz must deserialize, copy and repaint 55 MB of float data every second, and a keep-last queue of five holds 5 / 15 Hz = 0.33 s of stream, barely two frames of scheduling slack. Any host pause longer than that queue becomes dropped samples: the survivors render and the gaps read as black.
The decision follows from the numbers. If the viewer host sustains the full rate — a desktop CPU on wired gigabit handles 55 MB/s at 720p comfortably — set RELIABLE on both ends and the image steadies. If it cannot, as on an embedded ARM board also carrying a point cloud, match on best effort and cut the stream until the arithmetic fits: at 5 Hz the same image is 3,686,400 B × 5 Hz = 18,432,000 B/s, about 18.4 MB/s, which any viewer host holds. Budget the point cloud too: 921,600 points × 12 B (three floats) × 10 Hz = 110,592,000 B/s ≈ 110.6 MB/s, about 885 Mb/s, which saturates gigabit on its own. Publish it sparsely or leave it out of live sessions on a small host.
Fix the policies on both ends
- Match the subscription to the publisher. Select the image display, open its Topic section, and set Reliability to Reliable. Keep Durability at Volatile unless check 1 printed Transient Local on the publisher. Leave history on Keep Last with a queue depth of 5 to 10 frames; a deeper queue only delays the picture.
- Re-run check 1. The subscription block must now print Reliability: RELIABLE, and the blinking stops within seconds because rviz repaints every frame the publisher sends.
- Prefer the lossy pairing on purpose instead, for a real-time pipeline that consumes only the newest frame: put the publisher on the sensor-data profile and set the rviz subscription to Best Effort. Both ends agree, and drops stop mattering because every rendered frame is current.
- Cut the stream when the host is short: dropping the rate from 15 Hz to 5 Hz puts the example stream at about 18 MB/s, a third of its starting load.
- Keep the registered pairing honest. depth_registered is generated per color frame, so request depth at the same rate as the color stream; the device timestamp sync between RGB and depth keeps each depth image paired with the frame it belongs to. If you move to compressed transport, subscribe to the compressed topic, and when it is not published at all the compressed image topic not published guide is the one to follow.
Verify with a full minute of moving scene. At matched RELIABLE you expect continuous repaints at the publisher's rate; at 5 Hz you expect one update every 200 ms with no black gaps.
If the image still blinks after matching QoS
Work down this list in order of how often each turns out to be the cause.
- The rate still exceeds the host. Re-run the arithmetic with your measured rate and true resolution, and count every other topic that machine subscribes to; the point cloud outweighed depth two to one in the example above.
- Exposure is starving the depth. If the rate itself sags whenever auto-exposure lengthens the integration, the frame rate drops from exposure settings guide and the auto exposure priority guide show where the frames went.
- The source hands over empty depth. Large black areas inside otherwise live frames point at the buffer and its validity mask, which the empty depth image buffer and schema mask guide covers.
- The camera faults upstream of ROS. Open the same scene in the maker's own viewer; if depth cuts out there too, the QoS work was correct but this failure lives in exposure, range or mounting.
Budget the streams before the next launch
Two habits keep this fault from returning.
Read the publisher before you add a display. The verbose topic-info command takes seconds and prints reliability and durability together, so add each display with its subscription already matched.
Budget by arithmetic, not by feel. For every image topic a host subscribes to, multiply width × height × bytes per pixel × rate, sum the topics, and compare the total against what that machine demonstrably sustains. Publish diagnostic topics sparsely, run them only during commissioning, and keep one minimal launch that starts the camera with no displays: it separates transport from content in a single test.
Laser profilers and stereo 3D cameras
170 models · from $699.00The decision, case by case
- Blinking black with occasional frames and the other topics fine: publisher RELIABLE, subscription BEST_EFFORT. Set the rviz subscription's reliability to Reliable; the panel steadies within seconds.
- Black from the start with an incompatibility warning in the terminal: subscription RELIABLE, publisher BEST_EFFORT. Set the subscription to Best Effort, or raise the publisher to Reliable if you need every frame recorded.
- Matched policies and still dropping: the host cannot sustain the arithmetic. Cut the rate, drop the resolution, or compress until the sum of your streams fits.
- Depth black in rviz and in the maker's viewer alike: the fault is upstream of ROS, and exposure, range and mounting come next.
If you are sizing a stereo rig rather than repairing a stream, the 3D sensors category lists the laser profilers and stereo cameras we carry, the camera finder filters them by type and interface, and more depth-stream fixes live in the 3D topic section. For help matching a camera to your host and network, ask us through the contact page.
Questions
Why does the RGB image render fine while the depth image blinks black?
Because the mismatch is per topic, not per camera. Each publisher and each subscription carries its own reliability setting, so the color topic can be matched while the depth topic is not. Depth is also the heaviest stream on the rig — a float depth image holds four bytes per pixel against one for grayscale — so it is where an unmatched pair shows up as drops first. Run the verbose topic-info command on each image topic; the mismatch is rarely where you expect.
Which reliability should a depth topic use?
Match RELIABLE on both ends when a person watches or a recorder writes: every frame matters. Use the sensor-data profile — best effort, keep-last history, a queue of five — on both ends when a control loop acts only on the freshest frame, because stale queued frames are worse than dropped ones. What breaks images is asymmetry: a subscription on RELIABLE against a BEST_EFFORT publisher never connects, and the reverse pairing connects but drops. Agreement is the fix, either way.
Does lowering the camera frame rate fix the blinking without touching QoS?
It helps only when both ends already agree on best effort. With a RELIABLE publisher against a BEST_EFFORT subscription, halving the rate shrinks the drops but the pairing stays lossy by policy, so flashes return whenever the host stalls. Cut the rate as the second step, not the first: in the worked example, 15 Hz to 5 Hz takes the depth stream from about 55 MB/s to about 18 MB/s, which a viewer host holds easily.
Does compressed transport replace the QoS fix?
No — it treats bandwidth, not policy. Compression shrinks each frame so a best-effort subscription drops fewer of them, which masks the blink until load rises again. Match reliability first, then compress if the bandwidth arithmetic still says the host is short. And check that the compressed topic actually exists on both sides: when the compressed image topic is not published at all, the transport plugins are missing rather than the QoS being wrong.
Can a camera fault cause the same blinking black depth image?
Yes, and there is a clean separator. Open the depth stream in the camera maker's own viewer with the same scene, exposure and range settings: if depth cuts out there too, the fault is upstream of ROS, and exposure, range limits or mounting are the suspects. If the vendor viewer holds steady while rviz blinks, the publisher is healthy and the subscription's reliability setting is the cause. The two checks in this guide take about a minute and put you on the right side.