杰瑞科技汇

pangolin怎么用?新手入门指南

Pangolin 使用教程

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

pangolin怎么用?新手入门指南-图1
(图片来源网络,侵删)

本教程将分为以下几个部分:

  1. 环境准备 - 安装 Pangolin 及其依赖。
  2. 第一个 Pangolin 程序 - 运行一个最简单的窗口。
  3. 基本概念与核心组件 - 理解 Pangolin 的核心对象。
  4. 核心功能详解 - 绘制、交互、数据管理等。
  5. 完整示例:绘制轨迹和点云 - 一个实用的综合案例。
  6. 高级功能与技巧 - 多窗口、纹理、视频等。
  7. 常见问题 - 遇到问题怎么办。

环境准备

Pangolin 本身是一个头文件和库,但它依赖于其他几个图形库,最常见和推荐的安装方式是使用 vcpkg

使用 vcpkg (推荐)

vcpkg 是一个跨平台的 C++ 库管理器,可以非常方便地安装 Pangolin 及其所有依赖。

  1. 安装 vcpkg

    pangolin怎么用?新手入门指南-图2
    (图片来源网络,侵删)
    • 克隆 vcpkg 仓库:
      git clone https://github.com/microsoft/vcpkg.git
      cd vcpkg
    • 运行 bootstrap 脚本:
      • Windows (PowerShell):
        .\bootstrap-vcpkg.bat
      • Linux/macOS (Bash):
        ./bootstrap-vcpkg.sh
  2. 安装 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]
  3. 配置你的项目

    • 如果你使用了 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)

运行:

  1. 创建一个 build 目录并进入:mkdir build && cd build
  2. 运行 CMake:cmake ..
  3. 编译:make (Linux/macOS) 或 cmake --build . (Windows)
  4. 运行:./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;
}

高级功能与技巧

  • 多窗口和布局: 你可以通过 SetBoundsSetLayout 精确控制 View 的布局,创建复杂的界面。
  • 视频录制: Pangolin 可以轻松地将窗口内容录制为视频。
    pangolin::VideoOutput video("output.mp4", 640, 480);
    while (!pangolin::ShouldQuit()) {
        // ... 绘制代码 ...
        video.WriteFrame(glReadPixels(...), GL_RGB); // 需要获取像素数据
    }
  • 着色器: 你可以使用自定义的 GLSL 着色器来实现更复杂的渲染效果。
  • 数据记录: Var 对象可以记录其值随时间变化的历史,这对于调试和分析非常有用。

常见问题

  1. Could NOT find Pangolin (CMake 找不到 Pangolin)

    • 原因: Pangolin 未正确安装或 CMake 未找到其路径。
    • 解决:
      1. 确保你使用了 vcpkg integrate install 或在 CMakeLists.txt 中正确设置了 PANGOLIN_ROOT
      2. 如果你手动编译了 Pangolin,确保在 CMakeLists.txt 中指定了 Pangolin_DIR 指向 Pangolin 的构建目录中的 PangolinConfig.cmake 文件。
  2. 窗口一闪而过,立即关闭

    • 原因: 程序执行完毕,窗口被销毁。
    • 解决: 确保你的主循环 while (!pangolin::ShouldQuit()) 正常运行,检查 pangolin::FinishFrame() 是否被调用。
  3. 无法交互(鼠标拖动无效)

    • 原因: View 没有绑定 Handler,或者 Handler 类型不正确。
    • 解决: 确保在创建 View 时设置了正确的 Handlernew pangolin::Handler3D(...)
  4. 编译时出现 undefined reference to pangolin::... 错误

    • 原因: 链接了 Pangolin 的头文件但未链接其库文件。
    • 解决: 在 CMakeLists.txt 中使用 target_link_libraries(your_target PRIVATE Pangolin::Pangolin) 正确链接库。

希望这份详细的教程能帮助你顺利地使用 Pangolin!如果你有任何具体问题,可以随时提问。

分享:
扫描分享到社交APP
上一篇
下一篇