3D visionTroubleshooting

PCL visualization undefined reference: why viewer code won't link

Undefined references to pcl::visualization::PCLVisualizer mean the build never linked pcl_visualization. Add the component to find_package or reinstall PCL.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01Why does viewer code fail to link with undefined pcl::visualization references?
  2. 02Which PCL component does the linker look for?
  3. 03Which CMake change adds the visualization component to find_package?
  4. 04Why is libpcl_visualization missing from the PCL install?
  5. 05Why do hand-set PCL_INCLUDE_DIRS make the build worse?
  6. 06Does PCLVisualizer need VTK linked separately?
  7. 07How do you verify the fix before touching the algorithm?
  8. 08Which machine should build and run point cloud viewer tools?
  9. 09How do you keep the error from coming back?
  10. 10Questions

You added a viewer to a package that post-processes 3D camera point clouds. The compile finished clean, and the linker answered with a page of undefined references: the pcl::visualization::PCLVisualizer constructor, spin(), addPointCloud, even the destructor. A failure that uniform has one cause. Your executable never received the pcl_visualization library, because the find_package(PCL) call listed no components, so ${PCL_LIBRARIES} never contained it. The diagnosis below runs in the order of likelihood — the component request in CMake first, the library missing from the PCL install second, hand-set include paths last — and ends with the checks that keep the error away.

The compiler read the PCL headers and emitted calls into pcl::visualization::PCLVisualizer, but the linker never received libpcl_visualization: the find_package(PCL 1.5 REQUIRED COMPONENTS) line listed no components, so ${PCL_LIBRARIES} handed target_link_libraries a set without visualization in it.

Compilation needs only declarations, and the headers on the include path declare every class in pcl::visualization. The definitions of the non-inline members — the constructor pair, spin(), addCoordinateSystem, the geometry and color handler classes — live in libpcl_visualization. Template code such as pcl::PointCloud<pcl::PointXYZRGB> field access is fully defined in headers, which is why the point cloud math linked while only the viewer failed.

Two features of the trace confirm the diagnosis. Every undefined symbol starts with pcl::visualization::, even when the signature mentions VTK types such as vtkSmartPointer — the missing definitions belong to PCL's viewer functions, not to VTK. And the destructor is on the list: a wrong header or a name typo does not produce that. Only a library absent from the link line does.

Which PCL component does the linker look for?

PCL ships its viewer as a separate component named visualization, which builds into libpcl_visualization and declares its classes under pcl/visualization; find_package adds it to ${PCL_LIBRARIES} only when you name it in COMPONENTS, so an empty COMPONENTS list omits exactly this library.

The package config knows every component the install was built with — the PCL_COMPONENTS variable lists them — and it silently skips the ones nobody requests. find_package still succeeds, configure still passes, and nothing warns until link time. Read the prefix of each undefined symbol and the missing library falls out:

Symbol prefix in the error Library that defines it Where to fix it
pcl::visualization::PCLVisualizer, handler classes, createLine pcl_visualization COMPONENTS visualization in find_package
Bare vtk class methods, for example a missing vtkRenderer call VTK VTK install and CMake package paths
pcl::PointCloud field access, centroids, transforms pcl_common already linked in a working build

The third row explains why the rest of the cloud math kept linking while only the viewer broke. If a trace mixes prefixes from several libraries, sort the candidates first with diagnosing C++ camera SDK linker errors, then come back to the single-component case here.

Which CMake change adds the visualization component to find_package?

Name the component where the build system reads it: find_package(PCL 1.9 REQUIRED COMPONENTS common io visualization) makes the package config append pcl_visualization to ${PCL_LIBRARIES}, and the target_link_libraries call already in the file delivers it to the linker unchanged.

# before: an empty COMPONENTS list, so PCL_LIBRARIES never gains pcl_visualization
find_package(PCL 1.5 REQUIRED COMPONENTS)

# after: request what the viewer code actually calls
find_package(PCL 1.9 REQUIRED COMPONENTS common io visualization)

Keep the version the machine runs — 1.9 matches the pcl-1.9 include directory in this failing build — and leave the link call as it is:

target_link_libraries(pointcloud_viewer
  ${PCL_LIBRARIES}
  ${catkin_LIBRARIES})

If the machine's PCL was built without visualization, the new line now stops at configure with an error naming the missing component. That is the useful failure: it sends the build to the next check instead of a link-time wall.

Why is libpcl_visualization missing from the PCL install?

If requesting the component makes configure fail, the library itself is not on the machine: PCL produces libpcl_visualization only when its build finds VTK, so an install compiled without VTK has no visualization component to find, and reinstalling PCL with that component built is the fix.

The build that produced PCL decides which components exist. In the case behind this trace, the install had no visualization folder at all, and reinstalling PCL cleared every undefined reference. Two filesystem checks settle the local case in a minute:

  1. ls /usr/local/lib | grep pcl_visualization — a hit means a source install built the component; the library exists, so return to the find_package line.
  2. ls /usr/lib/x86_64-linux-gnu | grep pcl_visualization — a hit means the distribution packages supplied it; the same applies.

Nothing in either place means the component was never built: rebuild or reinstall PCL where VTK is found, then run sudo ldconfig so the loader cache picks up the new file. After any PCL change, delete the build directory and configure again — a stale cache keeps serving the old answer, the same start-from-a-known-state reset that fixes flaky camera tests with leftover settings.

Why do hand-set PCL_INCLUDE_DIRS make the build worse?

Hand-set include paths split a build across two PCL installs: overriding PCL_INCLUDE_DIRS to /usr/local/include/pcl-1.9 while the linker takes libraries from the default search couples one install's headers to another install's binaries, and that mismatch produces undefined references of exactly this shape.

PCL's package config already exports PCL_INCLUDE_DIRS, PCL_LIBRARY_DIRS and PCL_DEFINITIONS for the install it found, so pass them through untouched: include_directories(${PCL_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS}) needs no edit, and ${PCL_LIBRARIES} resolves without a hand-fed link_directories. Keep exactly one PCL per build machine.

The failing CMakeLists also carried add_definitions(${PCL_DEFINITONS}), spelled without the second I. CMake substitutes an empty string for an undefined variable, so the line did nothing, silently. Type package variable names as the package documentation prints them — PCL_DEFINITIONS is the correct spelling — and treat any hand-written PCL path as a bug.

For the version-mix disease specifically, fixing a compiler mismatch that surfaces as undefined references shows the same failure from the compiler side.

Does PCLVisualizer need VTK linked separately?

On a normal Linux shared-library install it does not: libpcl_visualization records its VTK dependencies at build time, so target_link_libraries with ${PCL_LIBRARIES} resolves the updateCells and createActorFromVTKDataSet symbols once pcl_visualization itself is on the link line.

The loader walks those dependencies when the executable starts, which is why a shared PCL hides the whole VTK graph from the CMakeLists. Link VTK directly in two cases: code that creates vtkSmartPointer objects itself, or a static PCL build, where archive order matters and VTK must follow the PCL libraries. Keep the diagnostics straight meanwhile: undefined references whose names begin with vtk, such as a missing vtkRenderer method, point at the VTK install or its CMake paths — a different fix from the missing component above.

How do you verify the fix before touching the algorithm?

Verify at three points — the configure output, the link line and the running window — before touching the algorithm, so a later failure cannot hide behind the one just fixed.

  1. Configure from an empty build directory and read the find_package output: the component list must include visualization.
  2. Build verbosely with make VERBOSE=1 and read the link line for the viewer target: pcl_visualization must appear beside the other pcl libraries.
  3. Run ldd pointcloud_viewer and confirm libpcl_visualization.so resolves to a real path rather than a not-found entry.
  4. Run the minimal viewer below: the window opens, one point renders, and spin returns when the window closes.
// minimal link check: declared types first, then the calls that failed before
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::PointXYZRGB p;                 // default point: all fields zero
p.x = 0.0f; p.y = 0.0f; p.z = 0.0f;
cloud->push_back (p);

pcl::visualization::PCLVisualizer viewer ("link check");
viewer.addPointCloud (cloud);       // this call produced the undefined references
viewer.spin ();

If this compiles and runs, the linker error is gone; bring the algorithm back in steps from there. On a headless build server the last step fails for a different reason — no display to open — which is a runtime condition, not a link error; the FAQ below covers it. A working viewer also renders slightly differently from exported data: why exported point clouds differ from the viewer render explains that gap, and fixing muted point cloud colors in a live visualization covers the washed-out look.

Which machine should build and run point cloud viewer tools?

Keep the build and the viewer on the machine that runs the cell: a vision controller already carries the GigE ports the 3D sensors use, and giving the compiler memory and SSD headroom keeps cloud-heavy rebuilds quick.

A controller from the VC5000 line suits a box that streams profilers daily and compiles weekly: the MV-VC5525-256G70-NN pairs 32 GB of memory with PoE camera ports.

Vision controllersHikrobot MV-VC5525-256G70-NN VC5000 Vision Controller, 12th-I5, 32GB+256G SSD, Supported PCIEMV-VC5525-256G70-NN
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
PoE
PoE camera ports
Light outputs
8 built-in channels
$4,479.00 · quotedView and quoteBuild a system with it

A lighter VC2000 unit runs the viewer while another machine compiles: the MV-VC2140-128G40-1T combines 8 GB of memory with a 128 GB SSD and a 1 TB hard drive, and its four built-in light channels drive the inspection lights directly.

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$1,679.00View partBuild a system with it

The sensors feeding these clouds sit in the 3D sensors category — laser profilers and stereo cameras whose outputs end up in exactly these viewer calls. Once the viewer runs, the next knob is the cloud itself: 3D point cloud x y z resolution settings without distortion walks that setting.

How do you keep the error from coming back?

Ask for every component by name in every CMakeLists that links viewer code, use only the variables PCL's package config exports, and reset the build directory after any PCL change — an explicit configure error then replaces the wall of undefined references permanently.

  • List components explicitly in every find_package(PCL) call — common, io, filters, visualization as the target needs. The empty COMPONENTS list is the trap this trace fell into.
  • Use only the exported variables: PCL_INCLUDE_DIRS, PCL_LIBRARY_DIRS, PCL_DEFINITIONS, PCL_LIBRARIES. No hand-written paths.
  • Spell package variables exactly: PCL_DEFINITIONS, not PCL_DEFINITONS. CMake expands a misspelled variable to nothing without a word of warning.
  • Keep one PCL per build machine. After any install or upgrade: sudo ldconfig, delete the build directory, reconfigure.
  • Keep the viewer in its own executable target, so inspection targets never depend on visualization and link cleanly on machines that lack it.

The decision in one line: undefined references prefixed with pcl::visualization mean request COMPONENTS visualization. A configure failure after that means the PCL install lacks the component, so reinstall PCL with VTK found; then confirm on the link line and with the minimal viewer before the algorithm returns. Size the box that runs all of it from the vision controllers category, browse the neighbouring fixes on the 3D vision guides page, and write to us through the contact page when a build needs a second pair of eyes — expert support comes with every order.

Questions

Will PCLVisualizer run on a headless vision controller with no monitor?

Linking and running are separate failures. Once pcl_visualization links, PCLVisualizer still needs a display and an OpenGL context to open its window, so on a headless controller the call fails at runtime, not at link time. Keep the production cell headless and run the viewer as a debugging tool where a screen exists, or give the build machine a virtual display for automated checks.

Is appending -lpcl_visualization to the link command a valid shortcut?

It works once, but the fix then lives outside the build system: the next configure still runs without the component, a PCL upgrade can rename the library, and a clean checkout loses the flag. Naming visualization in COMPONENTS records the dependency where every developer and CI job sees it, and it turns an absent library into a configure-time error instead of a link-time surprise.

Do PCL and VTK versions have to match for the visualization library?

The headers you compile against and the libpcl_visualization you link must come from the same PCL build, because that build fixed which VTK it depends on. Mixing one build's include path with another's libraries produces undefined references shaped like the ones here. Keep one PCL per machine, and after any change reinstall, run ldconfig and reconfigure from an empty build directory.

Where does a Linux system expect libpcl_visualization to live?

Distribution packages install PCL libraries under /usr/lib/x86_64-linux-gnu, and a PCL you build yourself lands in /usr/local/lib; both directories are on the default loader search path. After installing or rebuilding PCL, run sudo ldconfig so the cache picks up the new file, then confirm with ldd that your executable resolves the library before you blame your own CMake files.

Does catkin change any of the component rules?

No: the package's CMakeLists still needs visualization named in find_package, and ${catkin_LIBRARIES} only adds ROS-side libraries on top of ${PCL_LIBRARIES}. If a standalone test program links and the catkin package does not, diff the two find_package(PCL) calls first — the standalone script almost always carries the component list the package lost in a copy-paste.