[pangolin] [pangolin practice] [pangolin learning and use records]

0 Preface

1 Instructions for use of pangolin

1.1 Instructions for using CMakeLists.txt

find_package(Pangolin REQUIRED)
include_directories(${
    
    Pangolin_INCLUDE_DIRS})

add_executable(xxx src/xxx.cpp)
target_link_libraries(xxx ${
    
    Pangolin_LIBRARIES})

1.2 Use of header files

#include <pangolin/pangolin.h>

1.3 Use of Code

1.3.1 Simple example

  1. function declaration
void DrawTrajectory(vector<Isometry3d, Eigen::aligned_allocator<Isometry3d>>);
  • Input parameter vector<Isometry3d, Eigen::aligned_allocator<Isometry3d>>: is a collection of poses of many points
  1. function implementation
/*******************************************************************************************/
void DrawTrajectory(vector<Isometry3d, Eigen::aligned_allocator<Isometry3d>> poses) {
    
    
    // create pangolin window and plot the trajectory
    /*
     * 接下来,我们使用CreateWindowAndBind命令创建了一个视窗对象,
     * 函数的入口的参数依次为视窗的名称、宽度和高度,
     * 该命令类似于OpenCV中的namedWindow,即创建一个用于显示的窗体。
     */
    // 创建名称为“Trajectory Viewer”的GUI窗口,尺寸为640×640
    pangolin::CreateWindowAndBind("Trajectory Viewer", 1024, 768);
    //启动深度测试。同时,我们启动了深度测试功能,
    // 该功能会使得pangolin只会绘制朝向镜头的那一面像素点,避免容易混淆的透视关系出现,因此在任何3D可视化中都应该开启该功能。
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 创建一个观察相机视图
    pangolin::OpenGlRenderState s_cam(
            pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
            pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
    );
    // 创建一个观察相机视图
    // ProjectMatrix(int w, int h, int fu, int fv, int cu, int cv, int znear, int zfar)
    //      参数依次为观察相机的图像宽度、高度、4个内参以及最近和最远视距
    // ModelViewLookAt(double x, double y, double z,double lx, double ly, double lz, AxisDirection Up)
    //      参数依次为相机所在的位置,以及相机所看的视点位置(一般会设置在原点)

    //在完成视窗的创建后,我们需要在视窗中“放置”一个摄像机(注意这里的摄像机是用于观察的摄像机而非SLAM中的相机),
    // 我们需要给出摄像机的内参矩阵ProjectionMatrix从而在我们对摄像机进行交互操作时,
    // Pangolin会自动根据内参矩阵完成对应的透视变换。
    // 此外,我们还需要给出摄像机初始时刻所处的位置,摄像机的视点位置(即摄像机的光轴朝向哪一个点)以及摄像机的本身哪一轴朝上。

    // 创建交互视图
    //pangolin::Handler3D handler(s_cam); //交互相机视图句柄
    pangolin::View &d_cam = pangolin::CreateDisplay()
            .SetBounds(0.0, 1.0, 0.0, 1.0, -1024.0f / 768.0f)
            .SetHandler(new pangolin::Handler3D(s_cam));//.SetHandler(&handler);
    //接下来我们需要创建一个交互式视图(view)用于显示上一步摄像机所“拍摄”到的内容,这一步类似于OpenGL中的viewport处理。
    // setBounds()函数前四个参数依次表示视图在视窗中的范围(下、上、左、右),可以采用相对坐标(0~1)以及绝对坐标(使用Attach对象)。


    while (pangolin::ShouldQuit() == false) {
    
    
        // 清空颜色和深度缓存
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        d_cam.Activate(s_cam);//激活
        //在完成了上述所有准备工作之后,我们就可以开始绘制我们需要的图形了,
        // 首先我们使用glclear命令分别清空色彩缓存和深度缓存
        // 并激活之前设定好的视窗对象(否则视窗内会保留上一帧的图形,这种“多重曝光”效果通常并不是我们需要的)


        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glLineWidth(2);//定义其中线条的宽度

        for (size_t i = 0; i < poses.size(); i++) {
    
    
            // 画每个位姿的三个坐标轴
            Vector3d Ow = poses[i].translation();
            Vector3d Xw = poses[i] * (0.1 * Vector3d(1, 0, 0));
            Vector3d Yw = poses[i] * (0.1 * Vector3d(0, 1, 0));
            Vector3d Zw = poses[i] * (0.1 * Vector3d(0, 0, 1));
            glBegin(GL_LINES);
            glColor3f(1.0, 0.0, 0.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Xw[0], Xw[1], Xw[2]);
            glColor3f(0.0, 1.0, 0.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Yw[0], Yw[1], Yw[2]);
            glColor3f(0.0, 0.0, 1.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Zw[0], Zw[1], Zw[2]);
            glEnd();
        }
        // 画出连线
        for (size_t i = 0; i < poses.size(); i++) {
    
    
            glColor3f(0.0, 0.0, 0.0);
            glBegin(GL_LINES);
            auto p1 = poses[i], p2 = poses[i + 1];
            glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]);
            glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
            glEnd();
        }
        //在绘制完成后,需要使用FinishFrame命令刷新视窗。
        pangolin::FinishFrame();
        usleep(5000);   // sleep 5 ms
    }
}

1.3.1.1 Code Analysis

1.3.1.1.1 Initialize pangolin
    /*
     * 接下来,我们使用CreateWindowAndBind命令创建了一个视窗对象,
     * 函数的入口的参数依次为视窗的名称、宽度和高度,
     * 该命令类似于OpenCV中的namedWindow,即创建一个用于显示的窗体。
     */
    // 创建名称为“Trajectory Viewer”的GUI窗口,尺寸为640×640
    pangolin::CreateWindowAndBind("Trajectory Viewer", 1024, 768);
    //启动深度测试。同时,我们启动了深度测试功能,
    // 该功能会使得pangolin只会绘制朝向镜头的那一面像素点,避免容易混淆的透视关系出现,因此在任何3D可视化中都应该开启该功能。
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  1. pangolin::CreateWindowAndBind("Trajectory Viewer", 1024, 768);: Create a GUI window named "Trajectory Viewer" with a size of 640×640; the parameters of the entry of the function are the name, width and height of the window in turn, this command is similar to namedWindow in OpenCV, that is, to create a window for display body.
  2. glEnable(GL_DEPTH_TEST);: Enable the depth test function, which will make pangolin only draw the pixels on the side facing the camera to avoid confusing perspective relationships, so this function should be enabled in any 3D visualization.
1.3.1.1.2 Create an observation camera view
    // 创建一个观察相机视图
    pangolin::OpenGlRenderState s_cam(
            pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
            pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
    );
    // 创建一个观察相机视图
    // ProjectMatrix(int w, int h, int fu, int fv, int cu, int cv, int znear, int zfar)
    //      参数依次为观察相机的图像宽度、高度、4个内参以及最近和最远视距
    // ModelViewLookAt(double x, double y, double z,double lx, double ly, double lz, AxisDirection Up)
    //      参数依次为相机所在的位置,以及相机所看的视点位置(一般会设置在原点)

    //在完成视窗的创建后,我们需要在视窗中“放置”一个摄像机(注意这里的摄像机是用于观察的摄像机而非SLAM中的相机),
    // 我们需要给出摄像机的内参矩阵ProjectionMatrix从而在我们对摄像机进行交互操作时,
    // Pangolin会自动根据内参矩阵完成对应的透视变换。
    // 此外,我们还需要给出摄像机初始时刻所处的位置,摄像机的视点位置(即摄像机的光轴朝向哪一个点)以及摄像机的本身哪一轴朝上。
  1. ProjectMatrix(int w, int h, int fu, int fv, int cu, int cv, int znear, int zfar): The parameters are the image width, height, 4 internal references, and the closest and farthest viewing distances of the observation camera in sequence
  2. ModelViewLookAt(double x, double y, double z,double lx, double ly, double lz, AxisDirection Up): The parameters are the position of the camera in turn, and the position of the viewpoint seen by the camera (usually set at the origin)
  3. A camera needs to be "placed" in the window (note that the camera here is a camera for observation rather than a camera in SLAM), and the camera's internal reference matrix ProjectionMatrix is ​​given so that when we interact with the camera, Pangolin will automatically use the internal reference The matrix completes the corresponding perspective transformation.
  4. We also need to give the position of the camera at the initial moment, the position of the camera's viewpoint (that is, which point the camera's optical axis is facing), and which axis of the camera itself is facing up.
1.3.1.1.3 Create an interactive view
    // 创建交互视图
    //pangolin::Handler3D handler(s_cam); //交互相机视图句柄
    pangolin::View &d_cam = pangolin::CreateDisplay()
            .SetBounds(0.0, 1.0, 0.0, 1.0, -1024.0f / 768.0f)
            .SetHandler(new pangolin::Handler3D(s_cam));//.SetHandler(&handler);
    //接下来我们需要创建一个交互式视图(view)用于显示上一步摄像机所“拍摄”到的内容,这一步类似于OpenGL中的viewport处理。
    // setBounds()函数前四个参数依次表示视图在视窗中的范围(下、上、左、右),可以采用相对坐标(0~1)以及绝对坐标(使用Attach对象)。
  1. Create an interactive view (view) to display what the camera "shot" in the previous step
  2. setBounds(): The first four parameters represent the scope of the view in the window (bottom, top, left, right) in turn, and can use relative coordinates (0~1) and absolute coordinates (using the Attach object)
1.3.1.1.4 Drawing loop

The operation of drawing graphics is done in the while() loop

    while (pangolin::ShouldQuit() == false) {
    
    
        // 清空颜色和深度缓存
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        d_cam.Activate(s_cam);//激活
        //在完成了上述所有准备工作之后,我们就可以开始绘制我们需要的图形了,
        // 首先我们使用glclear命令分别清空色彩缓存和深度缓存
        // 并激活之前设定好的视窗对象(否则视窗内会保留上一帧的图形,这种“多重曝光”效果通常并不是我们需要的)

        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);//设定背景的颜色,此处为白色

        //glLineWidth(2);//定义其中线条的宽度

		//这里省略画图的代码,会在下面展示

        //在绘制完成后,需要使用FinishFrame命令刷新视窗。
        pangolin::FinishFrame();
        usleep(5000);   // sleep 5 ms
    }
  1. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);It is to clear the color and depth buffer, otherwise the graphics of the previous frame will be kept in the window. This "multiple exposure" effect is usually not what we need
  2. d_cam.Activate(s_cam);It is the window object set before activation
  3. glClearColor(1.0f, 1.0f, 1.0f, 1.0f);: Set the color of the background, here it is white
  4. pangolin::FinishFrame();: After the drawing is completed, you need to use the FinishFrame command to refresh the window.
1.3.1.1.5 Drawing operations
1.3.1.1.5.1 Point-based poser coordinate axes
        glLineWidth(2);//定义其中线条的宽度

        for (size_t i = 0; i < poses.size(); i++) {
    
    
            // 画每个位姿的三个坐标轴
            Vector3d Ow = poses[i].translation();
            Vector3d Xw = poses[i] * (0.1 * Vector3d(1, 0, 0));
            Vector3d Yw = poses[i] * (0.1 * Vector3d(0, 1, 0));
            Vector3d Zw = poses[i] * (0.1 * Vector3d(0, 0, 1));
            
            glBegin(GL_LINES);
            
            glColor3f(1.0, 0.0, 0.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Xw[0], Xw[1], Xw[2]);
            
            glColor3f(0.0, 1.0, 0.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Yw[0], Yw[1], Yw[2]);
            
            glColor3f(0.0, 0.0, 1.0);
            glVertex3d(Ow[0], Ow[1], Ow[2]);
            glVertex3d(Zw[0], Zw[1], Zw[2]);
            
            glEnd();
        }
1.3.1.1.5.2 Draw a line between two poses (pose)
        // 画出连线
        for (size_t i = 0; i < poses.size(); i++) {
    
    
        
            glColor3f(0.0, 0.0, 0.0);
            
            glBegin(GL_LINES);
            
            auto p1 = poses[i], p2 = poses[i + 1];
            glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]);
            glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
            
            glEnd();
        }
1.3.1.1.5.3 Draw dots
    glPointSize(2);//设定点的尺寸
    glBegin(GL_POINTS);//设定为点的绘制
    for (auto& landmark : active_landmarks_) {
    
    //获取各当前活跃路标点
       Eigen::Matrix<double, 3, 1> pos = landmark.second->Pos();//获取该路标点3D坐标
        glColor3f(red[0], red[1], red[2]);
        glVertex3d(pos[0], pos[1], pos[2]);
    }
    glEnd();
1.3.1.1.6 Advanced Operation
1.3.1.1.6.1 Set temporary origin
  • The name I named is temporary origin, because it does not change the real origin and can be restored to its original state. Please point out if your understanding is wrong.
  • The main function is: after the realization, the position is drawn centered on this temporary origin, and it will be automatically converted to the original center
  • Purpose: After knowing the changing camera pose, it is relatively simple to draw things in this way
  • Lite version of necessary code
    glPushMatrix();//必须,类似于标志

    Sophus::Matrix4f m = Twc.matrix().template cast<float>();//定义所能接受的位姿格式为  4*4矩阵
    glMultMatrixf((GLfloat*)m.data());//设定原点
    
	//,,,,,此处省略,类似于1.3.1.1.5.1和1.3.1.1.5.2
	
    glPopMatrix();//删除这个暂时的原点
}
  • full code
void Viewer::DrawFrame(Frame::Ptr frame, const float* color) {
    
    
    SE3 Twc = frame->Pose().inverse();//此处获取的是相机在世界坐标系下的位姿
    const float sz = 1.0;//深度
    const int line_width = 2.0;//线宽
    //相机内参
    const float fx = 400;
    const float fy = 400;
    const float cx = 512;
    const float cy = 384;
    const float width = 1080;
    const float height = 768;

    glPushMatrix();//必须,类似于标志

    Sophus::Matrix4f m = Twc.matrix().template cast<float>();//定义所能接受的位姿格式为  4*4矩阵
    glMultMatrixf((GLfloat*)m.data());//设定原点

    if (color == nullptr) {
    
    
        glColor3f(1, 0, 0);
    } else
        glColor3f(color[0], color[1], color[2]);

    glLineWidth(line_width);
    glBegin(GL_LINES);
    glVertex3f(0, 0, 0);
    glVertex3f(sz * (0 - cx) / fx, sz * (0 - cy) / fy, sz);
    glVertex3f(0, 0, 0);
    glVertex3f(sz * (0 - cx) / fx, sz * (height - 1 - cy) / fy, sz);
    glVertex3f(0, 0, 0);
    glVertex3f(sz * (width - 1 - cx) / fx, sz * (height - 1 - cy) / fy, sz);
    glVertex3f(0, 0, 0);
    glVertex3f(sz * (width - 1 - cx) / fx, sz * (0 - cy) / fy, sz);

    glVertex3f(sz * (width - 1 - cx) / fx, sz * (0 - cy) / fy, sz);
    glVertex3f(sz * (width - 1 - cx) / fx, sz * (height - 1 - cy) / fy, sz);

    glVertex3f(sz * (width - 1 - cx) / fx, sz * (height - 1 - cy) / fy, sz);
    glVertex3f(sz * (0 - cx) / fx, sz * (height - 1 - cy) / fy, sz);

    glVertex3f(sz * (0 - cx) / fx, sz * (height - 1 - cy) / fy, sz);
    glVertex3f(sz * (0 - cx) / fx, sz * (0 - cy) / fy, sz);

    glVertex3f(sz * (0 - cx) / fx, sz * (0 - cy) / fy, sz);
    glVertex3f(sz * (width - 1 - cx) / fx, sz * (0 - cy) / fy, sz);

    glEnd();
    glPopMatrix();//删除这个暂时的原点
}
1.3.1.1.6.2 Resetting a new perspective
    SE3 Twc = current_frame_->Pose().inverse();// SE3格式的位姿
    pangolin::OpenGlMatrix m(Twc.matrix());//定义还是需要4*4的matrix矩阵形式
    vis_camera.Follow(m, true);

1.3.2 The use of pangolin in multithreading

  • Let's talk about it here, first know that there is such a usage, refer to SLAM Visual Drawing Library - Task2 of Pangolin Tutorial (1): Pangolin and Multithreading

2 reference articles

Guess you like

Origin blog.csdn.net/qq_45954434/article/details/126392006