Pangolin 使用教程
Pangolin 是一个轻量级的、用于 3D 可视化和用户交互的库,它常与 SLAM(即时定位与地图构建)、深度学习、机器人学等领域结合使用,可以方便地显示点云、相机轨迹、2D/3D 数据等。

本教程将分为以下几个部分:
- 环境准备 - 安装 Pangolin 及其依赖。
- 第一个 Pangolin 程序 - 运行一个最简单的窗口。
- 基本概念与核心组件 - 理解 Pangolin 的核心对象。
- 核心功能详解 - 绘制、交互、数据管理等。
- 完整示例:绘制轨迹和点云 - 一个实用的综合案例。
- 高级功能与技巧 - 多窗口、纹理、视频等。
- 常见问题 - 遇到问题怎么办。
环境准备
Pangolin 本身是一个头文件和库,但它依赖于其他几个图形库,最常见和推荐的安装方式是使用 vcpkg。
使用 vcpkg (推荐)
vcpkg 是一个跨平台的 C++ 库管理器,可以非常方便地安装 Pangolin 及其所有依赖。
-
安装 vcpkg
(图片来源网络,侵删)- 克隆 vcpkg 仓库:
git clone https://github.com/microsoft/vcpkg.git cd vcpkg
- 运行 bootstrap 脚本:
- Windows (PowerShell):
.\bootstrap-vcpkg.bat
- Linux/macOS (Bash):
./bootstrap-vcpkg.sh
- Windows (PowerShell):
- 克隆 vcpkg 仓库:
-
安装 Pangolin
-
Windows (使用 Visual Studio):
# 首先设置编译器 triplet (x64-release 是最常用的) .\vcpkg integrate install # 安装 pangolin .\vcpkg install pangolin[tools]
[tools]会额外安装一些有用的命令行工具,如pangolin-viewer。 -
Linux/macOS:
# 设置编译器 triplet (x64-release 是最常用的) ./vcpkg install pangolin[tools]
-
-
配置你的项目
- 如果你使用了
vcpkg integrate install,CMake 会自动找到 Pangolin。 - 如果没有,你需要在你的
CMakeLists.txt中手动指定路径:find_package(Pangolin REQUIRED) target_link_libraries(your_target PRIVATE Pangolin::Pangolin)
- 如果你使用了
手动安装 (不推荐,仅用于了解)
手动安装比较复杂,需要分别安装以下依赖:
- OpenGL / GLU: 图形库。
- GLEW: 扩展库,用于加载 OpenGL 扩展。
- GLFW: 窗口管理库。
- Boost: Pangolin 的许多功能依赖 Boost 库。
- Pybind11: 如果你需要 Python 绑定。
安装完所有依赖后,从 Pangolin 的 GitHub 仓库克隆源码并编译。
第一个 Pangolin 程序
让我们从一个最简单的“Hello World”程序开始,它只创建一个窗口。
代码 (hello_pangolin.cpp):
#include <pangolin/pangolin.h>
int main(int argc, char** argv)
{
// 1. 创建一个窗口,并设置其大小和标题
pangolin::CreateWindowAndBind("Main", 640, 480);
// 2. 启用深度测试,这对于 3D 图形至关重要
glEnable(GL_DEPTH_TEST);
// 3. 定义投影和模型视图矩阵
// 设置投影矩阵 (透视投影)
pangolin::OpenGlMatrix proj = pangolin::ProjectionMatrixRDF_TopLeft(640, 480, 500, 500, 320, 240, 0.1, 1000);
// 设置模型视图矩阵 (摄像机位置和朝向)
pangolin::OpenGlStacks s_cam;
s_cam.SetIdentity(); // 初始化为单位矩阵
// 4. 创建一个处理视图的对象
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(1.0f, 0.0f, 0.0f, 1.0f) // 设置视图在窗口中的位置和大小
.SetHandler(new pangolin::Handler3D(s_cam)); // 为视图添加一个3D交互处理器
// 5. 进入主循环
while (!pangolin::ShouldQuit())
{
// 清除颜色和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 激活视图
d_cam.Activate(proj);
// 在这里绘制你的图形...
// 绘制一个简单的立方体
glColor3f(1.0f, 0.0f, 0.0f); // 设置颜色为红色
pangolin::glDrawColouredCube(); // Pangolin 提供的绘制立方体的函数
// 交换缓冲区并处理事件
pangolin::FinishFrame();
}
return 0;
}
编译 (CMakeLists.txt):
cmake_minimum_required(VERSION 3.10) project(hello_pangolin) # 查找 Pangolin 包 find_package(Pangolin REQUIRED) add_executable(hello_pangolin hello_pangolin.cpp) # 链接 Pangolin 库 target_link_libraries(hello_pangolin PRIVATE Pangolin::Pangolin)
运行:
- 创建一个
build目录并进入:mkdir build && cd build - 运行 CMake:
cmake .. - 编译:
make(Linux/macOS) 或cmake --build .(Windows) - 运行:
./hello_pangolin(Linux/macOS) 或hello_pangolin.exe(Windows)
你会看到一个窗口,里面有一个红色的立方体,你可以用鼠标拖动来旋转视角,滚轮来缩放。
基本概念与核心组件
理解以下核心对象对掌握 Pangolin 至关重要:
pangolin::CreateWindowAndBind(): 创建并绑定一个 OpenGL 窗口,这是所有可视化程序的起点。pangolin::View: Pangolin 中最重要的概念,它代表屏幕上的一个矩形区域,可以看作是一个独立的“画布”,你可以将窗口分割成多个View,每个View可以有自己的 3D 场景、2D 图像或数据图表。pangolin::Handler: 事件处理器,它定义了View如何响应用户的输入(鼠标、键盘)。Handler3D: 处理 3D 视图的旋转、平移和缩放。HandlerImage: 处理 2D 图像的平移和缩放。
pangolin::OpenGlMatrix: 用于表示 4x4 的变换矩阵(模型视图矩阵、投影矩阵)。pangolin::GlTexture: 用于加载和管理 2D 纹理(如图片)。pangolin::Var: 用于创建与程序变量绑定的交互式 UI 控件(如滑动条、复选框),当你通过 UI 改变Var的值时,程序中的对应变量也会实时更新。pangolin::Plotter: 用于绘制 2D 数据图表(如折线图)。
核心功能详解
1 绘制 3D 对象
除了 glDrawColouredCube,你还可以使用标准的 OpenGL 命令来绘制:
// 在 d_cam.Activate(proj) 之后
glColor3f(0.0f, 1.0f, 0.0f); // 绿色
glBegin(GL_LINES);
glVertex3f(-1, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(0, -1, 0);
glVertex3f(0, 1, 0);
glVertex3f(0, 0, -1);
glVertex3f(0, 0, 1);
glEnd();
2 绘制点云
点云是 Pangolin 的核心功能之一。
// 假设你有一个 std::vector<Eigen::Vector3d> points 存储点云数据
std::vector<Eigen::Vector3d> points;
// ... 填充点云数据 ...
// 在绘制循环中
glPointSize(2.0); // 设置点的大小
glColor3f(0.0f, 1.0f, 1.0f); // 青色
glBegin(GL_POINTS);
for(const auto& p : points) {
glVertex3f(p.x(), p.y(), p.z());
}
glEnd();
3 绘制相机轨迹
在 SLAM 中,这非常常见,通常用线段连接相机位姿。
// 假设你有一个 std::vector<Eigen::Isometry3d> trajectory 存储相机位姿
std::vector<Eigen::Isometry3d> trajectory;
// ... 填充轨迹数据 ...
// 在绘制循环中
glLineWidth(2.0);
glColor3f(1.0f, 1.0f, 0.0f); // 黄色
glBegin(GL_LINE_STRIP);
for(const auto& T : trajectory) {
glVertex3f(T.translation().x(), T.translation().y(), T.translation().z());
}
glEnd();
// 也可以绘制每个位姿处的坐标系
for(const auto& T : trajectory) {
pangolin::glDrawFrame(T.matrix()); // 绘制一个小的坐标系
}
4 绘制 2D 图像
将一张图片显示在窗口的另一个 View 中。
// 1. 加载图片
cv::Mat img = cv::imread("path/to/your/image.jpg");
if (img.empty()) {
// 处理错误
}
// 2. 创建纹理
pangolin::GlTexture tex(img.cols, img.rows, GL_RGB, false, 0, GL_BGR, GL_UNSIGNED_BYTE);
// 3. 在主循环中更新纹理
while (!pangolin::ShouldQuit()) {
// ... 3D视图的绘制 ...
// 激活 2D 图像视图
image_view.Activate(); // image_view 是另一个 pangolin::View
// 更新纹理数据
tex.Upload(img.data, GL_BGR, GL_UNSIGNED_BYTE);
// 绘制纹理
tex.RenderToViewport();
}
5 使用交互式控件 (Var)
Var 可以让你在运行时动态调整参数。
// 在 main 函数开头,创建一些变量
bool follow_cam = true;
float point_size = 2.0f;
pangolin::Var<bool> var_follow_cam("ui.Follow Cam", follow_cam);
pangolin::Var<float> var_point_size("ui.Point Size", point_size, 1.0f, 10.0f);
// 在主循环中
while (!pangolin::ShouldQuit()) {
// ... 绘制代码 ...
// 当 UI 控件改变时,更新你的变量
if (var_follow_cam.GuiChanged()) {
follow_cam = var_follow_cam;
// 可以在这里根据 follow_cam 的值改变 Handler
}
glPointSize(var_point_size);
}
这些 Var 控件会自动出现在 Pangolin 窗口的顶部或侧边栏。
完整示例:绘制轨迹和点云
这个示例将 3D 视图和 2D 图像视图结合起来,并绘制相机轨迹和点云。
代码 (visualizer.cpp):
#include <pangolin/pangolin.h>
#include <Eigen/Core>
#include <vector>
// 一些模拟数据
std::vector<Eigen::Vector3d> GeneratePointCloud() {
std::vector<Eigen::Vector3d> points;
for (int i = 0; i < 1000; ++i) {
points.push_back(Eigen::Vector3d(
(rand() % 100) / 100.0,
(rand() % 100) / 100.0,
(rand() % 100) / 100.0
));
}
return points;
}
std::vector<Eigen::Isometry3d> GenerateTrajectory() {
std::vector<Eigen::Isometry3d> trajectory;
for (int i = 0; i < 20; ++i) {
Eigen::Isometry3d T = Eigen::Isometry3d::Identity();
T.translation() = Eigen::Vector3d(i * 0.1, 0, 0);
T.linear() = Eigen::AngleAxisd(i * 0.1, Eigen::Vector3d::UnitZ()).toRotationMatrix();
trajectory.push_back(T);
}
return trajectory;
}
int main(int argc, char** argv)
{
// 创建窗口
pangolin::CreateWindowAndBind("Visualizer", 1024, 768);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 定义投影和模型视图
pangolin::OpenGlMatrix proj = pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000);
pangolin::OpenGlStacks s_cam;
s_cam.SetIdentity();
// 创建两个视图:一个用于3D场景,一个用于2D图像
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(1.0, 0.0, 0.0, 1.0) // 全屏
.SetHandler(new pangolin::Handler3D(s_cam));
pangolin::View& d_image = pangolin::CreateDisplay()
.SetBounds(1.0, 0.0, 0.0, 0.3) // 占据底部30%的高度
.SetHandler(new pangolin::HandlerImage());
// 生成模拟数据
auto points = GeneratePointCloud();
auto trajectory = GenerateTrajectory();
// 创建一个纹理来显示图像
pangolin::GlTexture tex(640, 480, GL_RGB, false, 0, GL_BGR, GL_UNSIGNED_BYTE);
cv::Mat img = cv::Mat::zeros(480, 640, CV_8UC3);
while (!pangolin::ShouldQuit())
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 绘制3D场景
d_cam.Activate(proj);
// 绘制点云
glPointSize(2.0);
glColor3f(0.7, 0.7, 1.0);
glBegin(GL_POINTS);
for(const auto& p : points) {
glVertex3d(p.x(), p.y(), p.z());
}
glEnd();
// 绘制轨迹
glLineWidth(2.0);
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_LINE_STRIP);
for(const auto& T : trajectory) {
glVertex3d(T.translation().x(), T.translation().y(), T.translation().z());
}
glEnd();
// 绘制相机位姿
for(const auto& T : trajectory) {
pangolin::glDrawFrame(T.matrix(), 0.1);
}
// 绘制2D图像
d_image.Activate();
// 更新图像数据(这里只是画一个简单的渐变)
for(int y = 0; y < img.rows; ++y) {
for(int x = 0; x < img.cols; ++x) {
img.at<cv::Vec3b>(y, x) = cv::Vec3b(x % 256, y % 256, 128);
}
}
tex.Upload(img.data, GL_BGR, GL_UNSIGNED_BYTE);
tex.RenderToViewport();
pangolin::FinishFrame();
}
return 0;
}
高级功能与技巧
- 多窗口和布局: 你可以通过
SetBounds和SetLayout精确控制View的布局,创建复杂的界面。 - 视频录制: Pangolin 可以轻松地将窗口内容录制为视频。
pangolin::VideoOutput video("output.mp4", 640, 480); while (!pangolin::ShouldQuit()) { // ... 绘制代码 ... video.WriteFrame(glReadPixels(...), GL_RGB); // 需要获取像素数据 } - 着色器: 你可以使用自定义的 GLSL 着色器来实现更复杂的渲染效果。
- 数据记录:
Var对象可以记录其值随时间变化的历史,这对于调试和分析非常有用。
常见问题
-
Could NOT find Pangolin(CMake 找不到 Pangolin)- 原因: Pangolin 未正确安装或 CMake 未找到其路径。
- 解决:
- 确保你使用了
vcpkg integrate install或在CMakeLists.txt中正确设置了PANGOLIN_ROOT。 - 如果你手动编译了 Pangolin,确保在
CMakeLists.txt中指定了Pangolin_DIR指向 Pangolin 的构建目录中的PangolinConfig.cmake文件。
- 确保你使用了
-
窗口一闪而过,立即关闭
- 原因: 程序执行完毕,窗口被销毁。
- 解决: 确保你的主循环
while (!pangolin::ShouldQuit())正常运行,检查pangolin::FinishFrame()是否被调用。
-
无法交互(鼠标拖动无效)
- 原因:
View没有绑定Handler,或者Handler类型不正确。 - 解决: 确保在创建
View时设置了正确的Handler,new pangolin::Handler3D(...)。
- 原因:
-
编译时出现
undefined reference to pangolin::...错误- 原因: 链接了 Pangolin 的头文件但未链接其库文件。
- 解决: 在
CMakeLists.txt中使用target_link_libraries(your_target PRIVATE Pangolin::Pangolin)正确链接库。
希望这份详细的教程能帮助你顺利地使用 Pangolin!如果你有任何具体问题,可以随时提问。
