Your stereo driver streams both image_raw topics, the nodes look healthy, and the calibration tool still refuses to start: it prints Waiting for service, then Service not found, in a loop. The tool is not broken and neither is your network. It blocks by design until every camera in the pair exposes a set_camera_info service, and a hand-written driver does not get one for free. You will decode the exact two names the calibrator waits for, rank the four causes from most to least likely, fix each one, and prove both services exist with a single command before you launch the calibrator again.
What the calibrator is waiting for
cameracalibrator.py resolves four graph names from its remaps: an image topic and a camera namespace for each eye. Given right:=/xb3_short/right/image_raw and right_camera:=/xb3_short/right (with the left pair the same way), it joins each camera namespace with /set_camera_info and polls the master for both services. Service not found prints after every poll, and your 8x6 board with its 0.108 m squares never gets scanned.
Two services are required, not one. Stereo calibration saves each camera's intrinsics through its own call, so both eyes must expose the service under their own namespace.
A camera driver is expected to advertise them. The camera_info_manager package exists for exactly this job: it provides the camera information interface, handles the SetCameraInfo service requests, and saves and restores sensor_msgs/CameraInfo data. Construct that class in your driver and the services appear. Construct it in the wrong namespace, or fail to link it, and you reproduce this failure exactly.
The four causes, most likely first
In order of how often each explains the message:
- Nobody advertises the service. The driver publishes image_raw and looks complete, but never constructs a camera_info_manager. Most common in hand-written drivers, because everything else about the stream works.
- The service sits in the wrong namespace. The manager exists but was built on the node's root handle, so the service lands at /set_camera_info while the calibrator polls two levels deeper under /xb3_short.
- The node never linked the package. The build dies at the link step with an undefined reference to the manager constructor. No binary, no service.
- The manager dies before the save. The object was built as a temporary or in a scope that exits, and an advertised service is withdrawn when its owner is destroyed.
Each cause has a distinct test, and the fixes run in that order.
Causes 1 and 2: build one manager per camera, on that camera's handle
The manager advertises set_camera_info in the namespace of the NodeHandle you pass it. The handle, not the camera-name string, decides the path. A driver publishing /xb3_short/left/image_raw must therefore hand the left manager a handle in /xb3_short/left, not the root handle it uses for housekeeping.
// Illustrative driver code; match the namespace strings to your own remaps.
ros::NodeHandle nh; // node root, resolves to "/"
ros::NodeHandle nh_left(nh, "xb3_short/left"); // namespace of the left camera
ros::NodeHandle nh_right(nh, "xb3_short/right"); // namespace of the right camera
camera_info_manager::CameraInfoManager cinfo_left(nh_left, "left");
camera_info_manager::CameraInfoManager cinfo_right(nh_right, "right");
cinfo_left and cinfo_right are objects at driver scope, declared once and kept alive for the whole run. The string argument names each camera in its saved calibration; the three-argument form from the linker error adds a URL for loading a stored file. After this change, confirm where the two services landed with the check in the worked example below. The manager follows the handle you gave it, the calibrator follows the remap you gave it, and both must point at the same namespace.
Cause 3: the undefined reference at link time
The compile succeeds and the link fails:
undefined reference to `camera_info_manager::CameraInfoManager::CameraInfoManager(ros::NodeHandle, std::basic_string<char, ...> const&, std::basic_string<char, ...> const&)'
That is a build-file problem, not a code problem. The compiler found the header; the final node target never linked the camera_info_manager library. It typically surfaces right after dynamic-reconfigure lines are added to the build, because that edit rewrites the target list and the executable target ends up without the calibration package. Give the node target both dependencies - camera_info_manager and dynamic_reconfigure - in the build file and the package manifest, then rebuild. In the failing run behind the worked example below, the driver code was correct all along; the build file was the entire bug.
Cause 4: the manager's lifetime
An advertised service exists exactly as long as the object that owns it. A manager built as an unnamed temporary, in an initialisation block that exits, or in a function that returns withdraws its service at scope exit - sometimes between the calibrator starting and the moment it calls the service to save. Declare both managers at the scope that lives as long as the node, alongside the publishers that share their namespaces.
Worked example: the two service names for an 8x6 board at 0.108 m
Inputs, all from the failing run's command line. The board is --size 8x6 (8 by 6 inner corners) with --square 0.108 (0.108 m per side). The image remaps are right:=/xb3_short/right/image_raw and left:=/xb3_short/left/image_raw. The camera remaps are right_camera:=/xb3_short/right and left_camera:=/xb3_short/left.
Rule, in words: the calibrator joins each camera namespace with the literal /set_camera_info and blocks until both resolved services exist on the master.
Arithmetic:
- Left: /xb3_short/left + /set_camera_info gives /xb3_short/left/set_camera_info
- Right: /xb3_short/right + /set_camera_info gives /xb3_short/right/set_camera_info
- Services required: 2. Services a root-handle manager provides: 1, at /set_camera_info, matching neither required name. Names matched: 0 of 2.
| Service the calibrator polls for | Built from | Provided by |
|---|---|---|
| /xb3_short/left/set_camera_info | left_camera remap + /set_camera_info | the left camera's manager |
| /xb3_short/right/set_camera_info | right_camera remap + /set_camera_info | the right camera's manager |
Result: 0 of the 2 polled names exist, so the wait never ends and the board is never scanned. The failure happens before the first frame, which is why no change to the images can fix it.
Decision: run rosservice list | grep set_camera_info before every calibration attempt. The check passes only when it prints exactly the two lines in the table. Zero lines means the package never linked or the driver never started. One line means a manager is missing, or both were built on the same handle. Two lines under a different prefix means a namespace mismatch. Fix the count first; the calibrator cannot recover from a missing service by itself.
Fix and verify, in order
- Fix the build first if the link fails: give the node target its camera_info_manager dependency and rebuild until the undefined reference is gone. Nothing else works without a binary.
- Create one manager per camera on the handle in that camera's namespace, at driver scope, next to the publishers for that eye.
- Start the driver and run rosservice list | grep set_camera_info. Require exactly the two names from the worked example, or their equivalents under your own prefixes.
- Launch the calibrator with the same remaps you just checked against. The waiting lines should clear within a second or two; if they persist, the remaps and the handles point at different namespaces.
- Capture the board, let the tool call both services to save, and keep the driver running until it finishes.
- Restart the driver and confirm the saved intrinsics load: the CameraInfo your driver builds from each manager now carries calibrated values instead of defaults.
The same failure in ROS 2
Current ROS 2 distributions behave the same way. The calibration node blocks on the two camera services and reports the same form of failure - Service right_camera/set_camera_info not found, for example - when the driver provides none. The manager class ships with the distributions, so the fix carries over unchanged: one manager per camera, on the handle in that camera's namespace, alive for the whole run.
What differs downstream is how camera_info is published and validated once calibration exists. That half - checking fields, frame names and timestamps - is walked through in ROS 2 camera calibration: validate and publish camera_info. For the names each image carries afterwards, the stereo camera_info frame_id on the right camera: intentional or a bug case is usually the next one people hit.
Check the graph first, then decide whether to build at all
Three habits prevent the whole failure class:
- Add the two-line service check to the driver's bring-up test, so a namespace regression fails fast.
- Keep one manager per camera at driver scope, never a temporary.
- Declare the package dependency when you create the driver, not when the linker complains.
Then zoom out. This failure class exists because the driver is hand-written. Shipped stereo and structured-light 3D cameras publish calibrated intrinsics and expose a calibration interface out of the box, and mainstream area scan cameras reach your code through maintained drivers that already provide the service. If the task is measurement rather than driver work, supported hardware shortens the path by days.
Before any calibration run, check what each eye actually delivers: raw stereo image calibration: verify streams and metadata walks that check. Once the rig is calibrated and mounted on a robot, the next missing-name failure is usually the target frame - hand-eye calibration target frame missing: what to check covers it. More fault-finding in this family is collected on the 3D topic page.
If you would rather buy the calibrated path than debug it, start from the sensor categories above or write to us through the contact page with your working distance and accuracy targets.
Questions
Does a stereo driver need one set_camera_info service or two?
Two. The calibrator resolves left_camera and right_camera separately and blocks until both set_camera_info services exist. Create one camera_info_manager per camera on that camera's own namespace so each eye saves its own intrinsics. A single service under the node root satisfies neither wait, because neither resolved name matches it - the namespaces, not the camera names, decide the paths.
Can I skip the service and load a calibration file instead?
For running the camera, yes: the manager loads intrinsics from the URL you pass its constructor, so a previously saved file works without any service. For running the calibration, no: the standard tool saves through set_camera_info, so a driver without the service can stream images but can never be calibrated by it. Advertise the service even if you normally load files.
Is dynamic_reconfigure involved in advertising set_camera_info?
No. dynamic_reconfigure serves runtime-tunable driver parameters and never touches calibration storage. The service comes from camera_info_manager, whose class advertises set_camera_info when constructed. Using both in one driver is fine, but the build file must link both into the final node target, or the link step fails with an undefined reference to the manager constructor.
Where does the saved calibration end up?
In the YAML file at the URL passed to the manager; with no URL, the package uses its documented default location and names the file after the camera-name string you passed. On the next start the manager restores that file, which is why the name matters even though it never appears in the service path. Check the file exists after a successful save.
Is the fix the same in ROS 2?
Yes, including the names. On current ROS 2 distributions the calibration node blocks on the two camera services and reports the same missing-service message, and the manager class ships with the distribution. Create one manager per camera in the camera's namespace and keep it alive for the run; then validate the published camera_info the way the ROS 2 calibration checks describe.