glog使用:

项目地址:
git@github.com:xychen5/tryGlog.git
推荐使用类似于clion的ide,然后打开该项目,即可编译运行。

1 主要作用:

能够将glog的日志在cmd中打印
主要调用了函数:

1
google::SetStderrLogging(google::INFO); // print the logs whose severity > [info]

2 output:

样例输出代码如下:

1
2
3
4
5
6
7
I0718 16:09:07.626883 18628 main.cpp:13] glog used in cmd!!

W0718 16:09:07.627887 18628 main.cpp:14] glog used in cmd!!

E0718 16:09:07.628882 18628 main.cpp:15] glog used in cmd!!

F0718 16:09:07.628882 18628 main.cpp:16] glog used in cmd!!

3 使用clion需要注意编译器的版本得和库对应

如果使用x86版本的toolchain,会报错如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
====================[ Build | tryGlogLion | Debug ]=============================
"Z:\softwares\CLion 2021.1.3\bin\cmake\win\bin\cmake.exe" --build F:\cppTry\tryGlogLion\cmake-build-debug --target tryGlogLion
Scanning dependencies of target tryGlogLion
[ 50%] Building CXX object CMakeFiles/tryGlogLion.dir/main.cpp.obj
main.cpp
[100%] Linking CXX executable tryGlogLion.exe
NMAKE : fatal error U1077: “"Z:\softwares\CLion 2021.1.3\bin\cmake\win\bin\cmake.exe"”: 返回代码“0xffffffff”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\bin\HostX86\x86\nmake.exe"”: 返回代码“0x2”
Stop.
LINK Pass 1: command "C:\PROGRA~2\MICROS~2\2019\COMMUN~1\VC\Tools\MSVC\1429~1.300\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\tryGlogLion.dir\objects1.rsp /out:tryGlogLion.exe /implib:tryGlogLion.lib /pdb:F:\cppTry\tryGlogLion\cmake-build-debug\tryGlogLion.pdb /version:0.0 /machine:X86 /debug /INCREMENTAL /subsystem:console -LIBPATH:F:\prjs\ThirdParty\glog\lib -LIBPATH:F:\prjs\ThirdParty\gflags\lib glog.lib gflags_static.lib glog.lib gflags_static.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:CMakeFiles\tryGlogLion.dir/intermediate.manifest CMakeFiles\tryGlogLion.dir/manifest.res" failed (exit code 1120) with the following output:
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) void __cdecl google::InitGoogleLogging(char const *)" (__imp_?InitGoogleLogging@google@@YAXPBD@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::LogMessage(char const *,int)" (__imp_??0LogMessage@google@@QAE@PBDH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::LogMessage(char const *,int,int)" (__imp_??0LogMessage@google@@QAE@PBDHH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessage::~LogMessage(void)" (__imp_??1LogMessage@google@@QAE@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: class std::basic_ostream<char,struct std::char_traits<char> > & __thiscall google::LogMessage::stream(void)" (__imp_?stream@LogMessage@google@@QAEAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessageFatal::LogMessageFatal(char const *,int)" (__imp_??0LogMessageFatal@google@@QAE@PBDH@Z),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: __thiscall google::LogMessageFatal::~LogMessageFatal(void)" (__imp_??1LogMessageFatal@google@@QAE@XZ),函数 _main 中引用了该符号
main.cpp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) void __cdecl google::SetStderrLogging(int)" (__imp_?SetStderrLogging@google@@YAXH@Z),函数 _main 中引用了该符号
F:\prjs\ThirdParty\glog\lib\glog.lib : warning LNK4272:库计算机类型“x64”与目标计算机类型“x86”冲突
F:\prjs\ThirdParty\gflags\lib\gflags_static.lib : warning LNK4272:库计算机类型“x64”与目标计算机类型“x86”冲突
tryGlogLion.exe : fatal error LNK1120: 8 个无法解析的外部命令

解决方案:
将files->settings->buidl,execut,deploy->toolchains->enviroment->architecture设置为x86_amd64

0 结果

https://cdn.jsdelivr.net/gh/xychen5/blogImgs@main/imgs/freeImg2cvMat.7hbdeyhx4n80.PNG

1 代码

将freeImage转为cv::mat,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;

// #define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
void FI2MAT(FIBITMAP* src, Mat& dst)
{
//FIT_BITMAP //standard image : 1 - , 4 - , 8 - , 16 - , 24 - , 32 - bit
//FIT_UINT16 //array of unsigned short : unsigned 16 - bit
//FIT_INT16 //array of short : signed 16 - bit
//FIT_UINT32 //array of unsigned long : unsigned 32 - bit
//FIT_INT32 //array of long : signed 32 - bit
//FIT_FLOAT //array of float : 32 - bit IEEE floating point
//FIT_DOUBLE //array of double : 64 - bit IEEE floating point
//FIT_COMPLEX //array of FICOMPLEX : 2 x 64 - bit IEEE floating point
//FIT_RGB16 //48 - bit RGB image : 3 x 16 - bit
//FIT_RGBA16 //64 - bit RGBA image : 4 x 16 - bit
//FIT_RGBF //96 - bit RGB float image : 3 x 32 - bit IEEE floating point
//FIT_RGBAF //128 - bit RGBA float image : 4 x 32 - bit IEEE floating point

int bpp = FreeImage_GetBPP(src);
FREE_IMAGE_TYPE fit = FreeImage_GetImageType(src);

int cv_type = -1;
int cv_cvt = -1;

switch (fit)
{
case FIT_UINT16: cv_type = DataType<ushort>::type; break;
case FIT_INT16: cv_type = DataType<short>::type; break;
case FIT_UINT32: cv_type = DataType<unsigned>::type; break;
case FIT_INT32: cv_type = DataType<int>::type; break;
case FIT_FLOAT: cv_type = DataType<float>::type; break;
case FIT_DOUBLE: cv_type = DataType<double>::type; break;
case FIT_COMPLEX: cv_type = DataType<Complex<double>>::type; break;
case FIT_RGB16: cv_type = DataType<Vec<ushort, 3>>::type; cv_cvt = COLOR_RGB2BGR; break;
case FIT_RGBA16: cv_type = DataType<Vec<ushort, 4>>::type; cv_cvt = COLOR_RGBA2BGRA; break;
case FIT_RGBF: cv_type = DataType<Vec<float, 3>>::type; cv_cvt = COLOR_RGB2BGR; break;
case FIT_RGBAF: cv_type = DataType<Vec<float, 4>>::type; cv_cvt = COLOR_RGBA2BGRA; break;
case FIT_BITMAP:
switch (bpp) {
case 8: cv_type = DataType<Vec<uchar, 1>>::type; break;
case 16: cv_type = DataType<Vec<uchar, 2>>::type; break;
case 24: cv_type = DataType<Vec<uchar, 3>>::type; break;
case 32: cv_type = DataType<Vec<uchar, 4>>::type; break;
default:
// 1, 4 // Unsupported natively
cv_type = -1;
}
break;
default:
// FIT_UNKNOWN // unknown type
dst = Mat(); // return empty Mat
return;
}

int width = FreeImage_GetWidth(src);
int height = FreeImage_GetHeight(src);
int step = FreeImage_GetPitch(src);

if (cv_type >= 0) {
dst = Mat(height, width, cv_type, FreeImage_GetBits(src), step);
if (cv_cvt > 0)
{
cvtColor(dst, dst, cv_cvt);
}
}
else {

std::vector<uchar> lut;
int n = pow(2, bpp);
for (int i = 0; i < n; ++i)
{
lut.push_back(static_cast<uchar>((255 / (n - 1))*i));
}

FIBITMAP* palletized = FreeImage_ConvertTo8Bits(src);
BYTE* data = FreeImage_GetBits(src);
for (int r = 0; r < height; ++r) {
for (int c = 0; c < width; ++c) {
dst.at<uchar>(r, c) = saturate_cast<uchar>(lut[data[r*step + c]]);
}
}
}

flip(dst, dst, 0);
}

int main()
{
FreeImage_Initialise();
// std::string imgPath = "F:/cppTry/index3.png";
std::string imgPath = "F:/cppTry/ger.JPG";
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(imgPath.c_str(), 0);
FIBITMAP* fi_image = FreeImage_Load(format, imgPath.c_str());

Mat cvImg2;
FI2MAT(fi_image, cvImg2);
cvNamedWindow("Image1:",1);
cv::imshow("Image1:",cvImg2);
// imshow(cv_img);

IplImage *img = cvLoadImage(imgPath.c_str());
cvNamedWindow("Image2:",1);
cvShowImage("Image2:", img);
std::cout << "[free's conv]: type is: " << FreeImage_GetColorType(fi_image) << "\n";
std::cout << "[free's conv]: depth is: " << FreeImage_GetBPP(fi_image) << "\n";
std::cout << "[free's conv]: pitch is: " << FreeImage_GetPitch(fi_image) << "\n";
std::cout << "[cv2 conv]: type is: " << "/" << cvImg2.type() << "\n";
std::cout << "[cv2 conv]: depth is: " << "/" << cvImg2.depth() << "\n";
std::cout << "[cv2 conv]: channel is: " << "/" << cvImg2.channels() << "\n";
std::cout << "[cv2 conv]: elem size is: " << "/" << cvImg2.elemSize() << "\n";

cvWaitKey();
cvDestroyWindow("Image:");
return 0;
}

2 将1通道转为3通道

1
2
3
4
5
6
7
void convert1to3Channel(cv::Mat& src, cv::Mat& dst) {
vector<cv::Mat> channels;
for (int i = 0; i < 3; i++) {
channels.push_back(src);
}
cv::merge(&channels[0], channels.size(), dst);
}

0 结果展示

https://cdn.jsdelivr.net/gh/xychen5/blogImgs@main/imgs/dynamicPly.4903brhv6b00.gif

1 下载编译pangolin的库

https://github.com/stevenlovegrove/Pangolin.git
该库中本身含有libpng, libjpg, libzip

2 当需要调用该库时:

调用时依赖如下:(本项目并未上传所有依赖,部分依赖需要单独下载然后放到thirdparty目录里)

1
2
3
4
5
# include
F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\include;F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\build\src\include;F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\Thirdparty\Pangolin\build\external\glew\include;F:\BASE_ENV\forOpenMVS\eigen;%(AdditionalIncludeDirectories)

# lib
..\..\..\lib\Release\pangolin.lib;opengl32.lib;glu32.lib;..\..\external\glew\lib\glew.lib;..\..\external\libpng\lib\libpng16_static.lib;..\..\external\zlib\lib\zlibstatic.lib;..\..\external\libjpeg\lib\jpeg.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib

3 动态展示点云的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
void viewThread(pangolin::OpenGlMatrix &Twc) {
pangolin::CreateWindowAndBind("Main", 1024, 768);
glEnable(GL_DEPTH_TEST);
// Issue specific OpenGl we might need
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Define Projection and initial ModelView matrix
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
pangolin::ModelViewLookAt(50, 50, 50, 0, 0, 0, pangolin::AxisY)
);

glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

// Add named OpenGL viewport to window and provide 3D Handler
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
.SetHandler(new pangolin::Handler3D(s_cam));

size_t frame = 0;
while( !pangolin::ShouldQuit() ) {
// Clear screen and activate view to render into
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0f,1.0f,1.0f,1.0f);
//! Activate Displays and set State Matrices
d_cam.Activate(s_cam);
// draw camera
const float w = 2;
const float h = w*0.75;
const float z = w*0.6;
glPushMatrix();
glMultMatrixd(Twc.m);
glLineWidth(1);
glColor3f(0.0f,1.0f,0.0f);
glBegin(GL_LINES);
glVertex3f(0,0,0);
glVertex3f(w,h,z);
glVertex3f(0,0,0);
glVertex3f(w,-h,z);
glVertex3f(0,0,0);
glVertex3f(-w,-h,z);
glVertex3f(0,0,0);
glVertex3f(-w,h,z);

glVertex3f(w,h,z);
glVertex3f(w,-h,z);

glVertex3f(-w,h,z);
glVertex3f(-w,-h,z);

glVertex3f(-w,h,z);
glVertex3f(w,h,z);

glVertex3f(-w,-h,z);
glVertex3f(w,-h,z);
glEnd();

glPopMatrix();

// draw points
glPointSize(2);
glBegin(GL_POINTS);
glColor3f(0.0, 0.0, 0.0);

// get all points to m_pos.m_ppatches
updatePointsMutex.lock();
for (size_t p = 0; p < (size_t)allPPatchesRender.size(); ++p)
{
auto patch = *allPPatchesRender[p];
glVertex3f(
patch.m_coord[0],
patch.m_coord[1],
patch.m_coord[2]
);
}
updatePointsMutex.unlock();
glEnd();

// Swap frames and Process Events
pangolin::FinishFrame();

std::this_thread::sleep_for(std::chrono::milliseconds(100)); // ms
// std::cout << "frame��" << ++frame << std::endl;
// std::cout << "point nums are: " << findMatch->m_pos.m_ppatches.size() << std::endl;
}
}


int main(int argc, char* argv[])
{
pangolin::OpenGlMatrix Twc, Twr;
Twc.SetIdentity();
std::thread viewThd(viewThread, Twc);

while(1) {
...<省略>
// update render points
updatePointsMutex.lock();
allPPatchesRender = findMatch->m_pos.m_ppatches; // to get the newest points
updatePointsMutex.unlock();
...<省略>
}


viewThd.join();

//writePointCloundPly(prefix);
releasePMVS();
return 0;
}


1 osg智能指针错误 - Warning: deleting still referenced object

1.1 问题描述

类的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class QOsgWidget {
public:
///< essential widget, use this ptr to be the real widget
osgQOpenGLWidget* pWidget = nullptr;
// QOsgWidget(QWidget* parent = nullptr);
QOsgWidget(const std::string& modelPath, QWidget* parent = nullptr);
~QOsgWidget();

///< osg base vars
osg::ref_ptr<osg::Group> mRoot = nullptr; // root node of the osg scene
osg::ref_ptr<osg::Camera> camera = nullptr; // osg camera
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr; // osg viewer
osg::ref_ptr<osgGA::TrackballManipulator> trackball = nullptr;
osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = nullptr;
osg::ref_ptr<osgViewer::StatsHandler> stats = nullptr;
<被省略>
}

类的析构函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
QOsgWidget::~QOsgWidget() {
// end the render thread and destroy the osgOpenglWidget
std::this_thread::sleep_for(std::chrono::microseconds(1000));
mViewer->setDone(true);
std::this_thread::sleep_for(std::chrono::microseconds(1000));
mViewer->stopThreading();

//////////////////////////////////////////////////////////////////////////
/// without folowing post process, u will crash with the bellowing warning when: delete pWidget
/// Warning: deleting still referenced object 000002433CA59BB0 of type 'class osg::Referenced * __ptr64'
/// the final reference count was 1, memory corruption possible.
///
/// the reason i guess is:
/// the bellowing vars can not be automatically set to null when delete pWidget, because they does
/// not belong to the pWidget, so u need to set them to null manually, otherwise when delete pWidget,
/// the destructor of pWidget will try to free the bellowing vars, but their ref != 0
///////////////////////////////// PART 1 /////////////////////////////////
mRoot = nullptr;
mViewer = nullptr;
camera = nullptr;
trackball = nullptr;
keyswitchManipulator = nullptr;
//////////////////////////////////////////////////////////////////////////

delete pWidget; // call the destructor of the osgOpenglWidge
}

问题描述,在执行类的析构的时候:
当没有PART1的时候,直接执行pWidget会出错

1.2 原因推测:

  • 1 osg::ref_ptr<> 是一种智能指针,自动计算指针的引用个数,当引用个数为0的时候,自动回收其指向的对象。
  • 2 于是出现一个大问题,当pWidget这个类指针所指向的类,里面很多组件(比如 camera,mViewer)是在QOsgWidget类中申明的,那么当析构pWidget的时候,它会去析构自己的组件,然后当它析构了自己类里面的那些指针后,发现QOsgWidget里面的智能指针不是null(这些指针很可能在被别的模块访问),所以引用计数不为0,所以无法删除,或者删除会crash

1.3 得出结论:

  • 1 如果类mon里面嵌套了类son,那么当mon的析构的时候,要注意如下两点:
    • 1.1 将son析构
    • 1.2 执行son的析构前,对son的析构可能产生影响的内存,指针,都要释放掉和置空

2 关于内存申请位置不对带来的野指针问题

2.1 问题描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
            resize(height() * 639 / 480, height());
QOsgWidget *bBoxEdit = new QOsgWidget(modelPath, static_cast<QWidget*>(this));
// use an ArgumentParser object to manage the program arguments.
bBoxEdit->pWidget = new osgQOpenGLWidget(&arguments, this);
bBoxEdit->pWidget->show();

// init the manipulators
bBoxEdit->InitManipulators();
bBoxEdit->InitModel(modelPath, bBoxEdit->mRoot);

// **************** part0: inside code segment of func call **********************/
osg::ref_ptr<osgViewer::Viewer> mViewer = bBoxEdit->pWidget->getOsgViewer();
// Add a Stats Handler to the viewer
mViewer->addEventHandler(new osgViewer::StatsHandler);
// Add the Camera to the Viewer
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
// Set projection matrix and camera attribtues
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setClearColor(osg::Vec3f(0.2f, 0.2f, 0.4f, 1.0f));
//mViewer->addSlave(camera.get());
mViewer->setCamera(camera.get());
// Set the Scene Data

// *************************** part1: func call **********************************/
// bBoxEdit->InitCameraConfig();

如上代码段,是一段qt程序中,在MainWindow的一个子函数的一段代码,其中bBoxEdit是一个自定义的供调用的类,
问题来了, part0的代码就是把part2的代码拿出来,仅仅使用part1的代码就会出错,仅仅使用part2就不会,请推测原因?

这里给出part1的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void QOsgWidget::InitCameraConfig() {
// Create the viewer for this window
mViewer = this->pWidget->getOsgViewer();
// Add a Stats Handler to the viewer
mViewer->addEventHandler(new osgViewer::StatsHandler);

// Add the Camera to the Viewer
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
// Set projection matrix and camera attribtues
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setClearColor(osg::Vec3f(0.2f, 0.2f, 0.4f, 1.0f));
//mViewer->addSlave(camera.get());
mViewer->setCamera(camera.get());

// Add the Camera Manipulator to the Viewer
osg::ref_ptr<osgGA::TrackballManipulator> trackball = new osgGA::TrackballManipulator();
mViewer->setCameraManipulator(trackball.get());

// Set the Scene Data
mViewer->setSceneData(mRoot.get());
}

2.2 错误原因推测:

首先想到,这肯定是指针带来的问题,指针和作用域和代码范围息息相关,所以放到函数里面和放到mainwindow的一个子函数的随便的一处,最大的区别在于,

  • 0 part2的情况:part2里面的函数调用所用到的指针,是存在qosgwidget的类的栈空间的,意味着只要类不析构,那么该指针所指向的内存就不会被回收。
  • 1 part1的情况:part1里面申请的内存是在mainwindow的一个子函数调用里面申请的,意味着该指针,在该子函数调用结束,它指向的内存会被回收,倘若这一块内存里运行的函数是我们恰恰不希望他在函数结束后就消失,那么我们就需要把这个指针存在一个 ‘安全’ 的地方。所以你需要存在哪里? 因为上面可以知道,该指针的内存里的函数是被 qosgwidget这个类所需要的,也就是当这个类结束啦,这个指针才会被回收,所以我们把指针存在这里,就不会出现上面的问题了。

2.3 得出结论(个人观点)

cpp的内存管理问题:

  • 0 尽量不要在业务流程里申请内存,释放内存,如果遇到需要申请内存和释放的地方,请把这一块儿写成一个类,
    用类的变量来保存内存指针,用类的析构来释放内存。
  • 1 重复1的观点:基于类作为内存管理的最小粒度,不要基于指针

1 osgQT的移植

首先参考官方项目:
https://github.com/openscenegraph/osgQt

这里先给出结果图:
https://cdn.jsdelivr.net/gh/xychen5/blogImgs@main/imgs/gliderQT.1w4nlzuxphsw.png

1.1 修改osgviewerqt.cpp如下: 这样你就能自己读模型了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

#include <osgQOpenGL/osgQOpenGLWidget>

#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/CoordinateSystemNode>

#include <osg/Switch>
#include <osg/Types>
#include <osgViewer/Viewer>
#include <osgText/Text>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <osgGA/SphericalManipulator>

#include <osgGA/Device>

#include <QApplication>
#include <QSurfaceFormat>

#include <iostream>


int main( int argc, char** argv )
{


QSurfaceFormat format = QSurfaceFormat::defaultFormat();

#ifdef OSG_GL3_AVAILABLE
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setOption(QSurfaceFormat::DebugContext);
#else
format.setVersion(2, 0);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setOption(QSurfaceFormat::DebugContext);
#endif
format.setDepthBufferSize(24);
//format.setAlphaBufferSize(8);
format.setSamples(8);
format.setStencilBufferSize(8);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
QSurfaceFormat::setDefaultFormat(format);

QApplication app(argc, argv);

// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc, argv);

arguments.getApplicationUsage()->setApplicationName(
arguments.getApplicationName());
arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() +
" is the standard OpenSceneGraph example which loads and visualises 3d models.");
arguments.getApplicationUsage()->setCommandLineUsage(
arguments.getApplicationName() + " [options] filename ...");
arguments.getApplicationUsage()->addCommandLineOption("--image <filename>",
"Load an image and render it on a quad");
arguments.getApplicationUsage()->addCommandLineOption("--dem <filename>",
"Load an image/DEM and render it on a HeightField");
arguments.getApplicationUsage()->addCommandLineOption("--login <url> <username> <password>",
"Provide authentication information for http file access.");
arguments.getApplicationUsage()->addCommandLineOption("-p <filename>",
"Play specified camera path animation file, previously saved with 'z' key.",
"F:/dataSets/OpenSceneGraph-Data-3.4.0/OpenSceneGraph-Data/glider.osg");
arguments.getApplicationUsage()->addCommandLineOption("--speed <factor>",
"Speed factor for animation playing (1 == normal speed).");
arguments.getApplicationUsage()->addCommandLineOption("--device <device-name>",
"add named device to the viewer");

osgQOpenGLWidget widget(&arguments);

if (true) {

osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("F:/dataSets/OpenSceneGraph-Data-3.4.0/OpenSceneGraph-Data/glider.osg");

if (!loadedModel)
{
std::cout << arguments.getApplicationName() << ": No data loaded" << std::endl;
return 1;
}

// any option left unread are converted into errors to write out later.
arguments.reportRemainingOptionsAsUnrecognized();

// report any errors if they have occurred when parsing the program arguments.
if (arguments.errors())
{
arguments.writeErrorMessages(std::cout);
return 1;
}

widget.show();

// optimize the scene graph, remove redundant nodes and state etc.
osgUtil::Optimizer optimizer;
optimizer.optimize(loadedModel);

// mViewer = new osgViewer::Viewer();
osg::ref_ptr<osgViewer::Viewer> mViewer = widget.getOsgViewer();

osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();

camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));

// Add the Camera to the Viewer
mViewer->setCamera(camera.get());

// Add the Camera Manipulator to the Viewer
// mViewer->setCameraManipulator(keyswitchManipulator.get());
osg::ref_ptr<osgGA::TrackballManipulator> trackball = new osgGA::TrackballManipulator();
mViewer->setCameraManipulator(trackball.get());

mViewer->setSceneData(loadedModel);

// // Realize the Viewer's graphics context, which already done in the default pWidget
// mViewer->realize();

return app.exec();
}
}

1.2 qt中使用

  • 1 封装一个自己的类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include <iostream>
    #include <memory>

    #include "osgHeaders.h"

    /*
    * this class is mainly for initialize the pWidget
    */
    class QOsgWidget {
    public:
    // essential widget, use this ptr to be the real widget
    osgQOpenGLWidget* pWidget = nullptr;
    // QOsgWidget(QWidget* parent = nullptr);
    QOsgWidget(const std::string& modelPath, QWidget* parent = nullptr);
    ~QOsgWidget();

    // osg base vars
    osg::ref_ptr<osg::Group> mRoot = nullptr;
    osg::ref_ptr<osg::Camera> camera = nullptr;
    osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
    osg::ref_ptr<osgGA::TrackballManipulator> trackball = nullptr;
    osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = nullptr;

    // for experiment:
    osg::ref_ptr<osgViewer::StatsHandler> pStat = nullptr;
    osg::ref_ptr<osgGA::TrackballManipulator> pTrackball = nullptr;
    osg::ref_ptr<osgViewer::Viewer> pmViewer = nullptr;

    // osg base funcs
    void InitManipulators();
    // load model into the mRoot
    void InitModel(const std::string& modelPathm, osg::ref_ptr<osg::Group>& mRoot);
    // init the cmaera
    void InitCameraConfig();
    osg::ref_ptr<osgViewer::Viewer> getViewer() { return mViewer; }

    // QObject::connect(&widget, &osgQOpenGLWidget::initialized);
    };
  • 2 调用这个类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    QOsgWidget *bBoxEdit = new QOsgWidget(modelPath, static_cast<QWidget*>(this));

    // use an ArgumentParser object to manage the program arguments.
    bBoxEdit->pWidget = new osgQOpenGLWidget(&arguments, this);
    bBoxEdit->pWidget->show();

    // init the manipulators
    bBoxEdit->InitManipulators();
    // init Scene Graph, that is to load the model
    bBoxEdit->InitModel(modelPath, bBoxEdit->mRoot);
    // init camera config
    bBoxEdit->InitCameraConfig();

    // 在这一行,将这个worksapceWidget添加到你想要添加的位置上就ok
    workspaceWidget = static_cast<QOpenGLWidget*>(&(*(bBoxEdit->pWidget)));

1 编译orb_slam

1
git clone https://github.com/UZ-SLAMLab/ORB_SLAM3.git ORB_SLAM3

建议编译前,先看一下该项目上的一个pull request:(使用原来项目问题过多, 遇到的每一个问题在第4节都会详细描述,于是使用如下的一个PR)
https://github.com/UZ-SLAMLab/ORB_SLAM3/pull/53
推荐使用vs2015生成器;

2 编译

2.1 DBoW2 编译(如果使用pr,本步骤可以不看,2.3的orbslam3也对所有cmakelist.txt的修改做了说明)

主要是需要添加oepncv和boost的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
PS F:\prjs\ORB_SLAM3> git diff
diff --git a/Thirdparty/DBoW2/CMakeLists.txt b/Thirdparty/DBoW2/CMakeLists.txt
index c561724..2368c23 100644
--- a/Thirdparty/DBoW2/CMakeLists.txt
+++ b/Thirdparty/DBoW2/CMakeLists.txt
@@ -32,9 +32,22 @@ if(NOT OpenCV_FOUND)
endif()
endif()

+
+set(Boost_USE_STATIC_LIBS ON)
+add_definitions("-DBOOST_ALL_NO_LIB=1")
+find_package(Boost REQUIRED COMPONENTS
+ serialization)
+
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

-include_directories(${OpenCV_INCLUDE_DIRS})
+include_directories(${OpenCV_INCLUDE_DIRS} ${BOOST_INCLUDEDIR})
+link_directories(${Boost_LIBRARY_DIRS})
+
+message("lib is: " ${Boost_SERIALIZATION_LIBRARY})
+
add_library(DBoW2 SHARED ${SRCS_DBOW2} ${SRCS_DUTILS})
-target_link_libraries(DBoW2 ${OpenCV_LIBS})
+target_link_libraries(DBoW2
+ ${OpenCV_LIBS}
+ ${Boost_SERIALIZATION_LIBRARY}
+)

diff --git a/Thirdparty/DBoW2/DBoW2/FORB.cpp b/Thirdparty/DBoW2/DBoW2/FORB.cpp
index 1f1990c..80bf473 100644
--- a/Thirdparty/DBoW2/DBoW2/FORB.cpp
+++ b/Thirdparty/DBoW2/DBoW2/FORB.cpp
@@ -13,7 +13,7 @@
#include <vector>
#include <string>
#include <sstream>
-#include <stdint-gcc.h>
+#include <stdint.h>

#include "FORB.h"

如果指定了vs2019,然后又用vs2015编译的库,就需要用如下的最后一个参数去掉boost的autolink;

1
2
3
4
5
6
7
8
9
mkdir build && cd build
cmake .. \
-G "Visual Studio 16" \
-DCMAKE_BUILDY_TYPE=Release \
-DOpenCV_DIR="F:/BASE_ENV/forOpenMVS/opencv/build" \
-DBOOST_ROOT="F:/BASE_ENV/forOpenMVS/boost_1_73_0_v140" \
-DBOOST_INCLUDEDIR="F:/BASE_ENV/forOpenMVS/boost_1_73_0_v140" \
-DBOOST_LIBRARYDIR="F:/BASE_ENV/forOpenMVS/boost_1_73_0_v140" \
-DBOOST_ALL_NO_LIB=1 \ # 避免boost的autolink,autolink会要求vs版本和boost版本一致

2.2 g2o

项目->属性->c++->预处器宏定义->添加: WINDOWS,否则会出现vasprintf找不到定义。

1
2
3
4
5
6
7
8
9
10
11
cd ../../g2o

echo "Configuring and building Thirdparty/g2o ..."

mkdir build
cd build
cmake .. \
-G "Visual Studio 14" \
-DCMAKE_BUILDY_TYPE=Release \
-DEIGEN3_INCLUDE_DIR="F:\BASE_ENV\forOpenMVS\eigen" \
-DBOOST_ALL_NO_LIB=1 \

2.3 orbslam3

2.3.0 首先按照下面的diff修改所有的internal::axpy和internal::atxpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
+// add these marcos to fix:  
+// 1>f:\prjs\orb_slam3\thirdparty\g2o\g2o\core\sparse_block_matrix.hpp(277): fatal error C1001: 编译器中发生内部错误。
+
+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
+
namespace g2o {
using namespace Eigen;

@@ -249,7 +256,8 @@ namespace g2o {
const typename SparseBlockMatrix<MatrixType>::SparseMatrixBlock* a=it->second;
int destOffset = it->first ? _rowBlockIndices[it->first - 1] : 0;
// destVec += *a * srcVec (according to the sub-vector parts)
- internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}
}
@@ -274,9 +282,12 @@ namespace g2o {
if (destOffset > srcOffset) // only upper triangle
break;
// destVec += *a * srcVec (according to the sub-vector parts)
- internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
- if (destOffset < srcOffset)
- internal::atxpy(*a, srcVec, destOffset, destVec, srcOffset);
+ // internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
+ if (destOffset < srcOffset) {
+ // internal::atxpy(*a, srcVec, destOffset, destVec, srcOffset);
+ _ATXPY(MatrixType, *a, srcVec, destOffset, destVec, srcOffset);
+ }
}
}
}
@@ -305,7 +316,8 @@ namespace g2o {
const typename SparseBlockMatrix<MatrixType>::SparseMatrixBlock* a=it->second;
int srcOffset = rowBaseOfBlock(it->first);
// destVec += *a.transpose() * srcVec (according to the sub-vector parts)
- internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _ATXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}

diff --git a/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h b/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
index 36ddfe6..91832cd 100644
--- a/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
+++ b/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
@@ -24,6 +24,9 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
#ifndef G2O_SPARSE_BLOCK_MATRIX_CCS_H
#define G2O_SPARSE_BLOCK_MATRIX_CCS_H

@@ -122,7 +125,8 @@ namespace g2o {
const SparseMatrixBlock* a = it->block;
int srcOffset = rowBaseOfBlock(it->row);
// destVec += *a.transpose() * srcVec (according to the sub-vector parts)
- internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _ATXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}
}
diff --git a/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h b/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
index 7b13b9f..8605a5f 100644
--- a/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
+++ b/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
@@ -24,6 +24,9 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
#ifndef G2O_SPARSE_BLOCK_MATRIX_DIAGONAL_H
#define G2O_SPARSE_BLOCK_MATRIX_DIAGONAL_H

@@ -94,7 +97,8 @@ namespace g2o {
int srcOffset = destOffset;
const SparseMatrixBlock& A = _diagonal[i];
// destVec += *A.transpose() * srcVec (according to the sub-vector parts)
- internal::axpy(A, srcVec, srcOffset, destVec, destOffset);
+ // internal::axpy(A, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, A, srcVec, srcOffset, destVec, destOffset);
}
}

如果你是自己在orbslam3直接编译的话,请参考: https://github.com/RainerKuemmerle/g2o/issues/91

2.3.1 找不到unistd.h

首先:unistd.h需要修改调,主要是为了使用usleep,该函数使用如下代码替换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
```cpp
#include <windows.h>

void usleep(__int64 usec)
{
HANDLE timer;
LARGE_INTEGER ft;

ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time

timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}

或者如下一行能搞定:推荐这个方法

1
2
3
4
if (ttrack < T) {
long usec = static_cast<long>((T - ttrack) * 1e6);
std::this_thread::sleep_for(std::chrono::microseconds(usec));
}

2.3.2 使用cmake-gui开始编译

cmake-gui配置:
opencv,boost,需要修改cmakelist.txt:
添加如下定义:

1
-DBOOST_ALL_NO_LIB=1

如下是我对cmakeList.txt做出的修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 70d03fe..79b32e7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
endif()

+add_definitions("-DBOOST_ALL_NO_LIB=1")

LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

@@ -46,13 +47,15 @@ if(NOT OpenCV_FOUND)
endif()
MESSAGE(STATUS "OpenCV VERSION: ${OpenCV_VERSION}")

-find_package(Eigen3 3.1.0 REQUIRED)
+# find_package(Eigen3 REQUIRED)
+set(EIGEN3_INCLUDE_DIR "F:/BASE_ENV/forOpenMVS/eigen")
+MESSAGE("eigen & boost inlcude dir is: @@@@@@" ${EIGEN3_INCLUDE_DIRS})

find_package(Pangolin REQUIRED)
find_package(realsense2)

find_package(Boost REQUIRED COMPONENTS serialization)
-MESSAGE(STATUS "Boost_LIBRARIES: ${Boost_LIBRARIES}")
+MESSAGE(STATUS "cxy@@@@@@@@@@ Boost_INCLUDE_DIRS & Boost_LIBRARIES: ${Boost_INCLUDE_DIRS} ${Boost_LIBRARIES}")

set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED) # for crypto library
@@ -65,6 +68,7 @@ include_directories(
${EIGEN3_INCLUDE_DIR}
${Pangolin_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
+ ${Boost_INCLUDE_DIRS}
)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
diff --git a/Thirdparty/DBoW2/CMakeLists.txt b/Thirdparty/DBoW2/CMakeLists.txt
index bf987be..e527175 100644
--- a/Thirdparty/DBoW2/CMakeLists.txt
+++ b/Thirdparty/DBoW2/CMakeLists.txt
@@ -51,10 +51,16 @@ if(NOT OpenCV_FOUND)
endif()
endif()
MESSAGE(STATUS "OpenCV VERSION: ${OpenCV_VERSION}")
-include_directories(${OpenCV_INCLUDE_DIRS})
-target_link_libraries(DBoW2 ${OpenCV_LIBS})

# add Boost
-find_package(Boost REQUIRED COMPONENTS serialization)
-message(STATUS "Boost_LIBRARIES: ${Boost_LIBRARIES}")
-target_link_libraries(DBoW2 ${Boost_LIBRARIES})
+set(Boost_USE_STATIC_LIBS ON)
+add_definitions("-DBOOST_ALL_NO_LIB=1")
+find_package(Boost REQUIRED COMPONENTS
+ serialization)
+message(STATUS "cxy@@@@@@@@ Boost_INCLUDE_DIRS & libs: ${Boost_INCLUDE_DIRS} >>>> ${Boost_LIBRARIES}")
+
+include_directories(${OpenCV_INCLUDE_DIRS} ${BOOST_INCLUDEDIR})
+target_link_libraries(DBoW2
+ ${OpenCV_LIBS}
+ ${Boost_LIBRARIES}
+)
diff --git a/Thirdparty/g2o/g2o/core/sparse_block_matrix.hpp b/Thirdparty/g2o/g2o/core/sparse_block_matrix.hpp
index 8dfa99c..80e4fa8 100644
--- a/Thirdparty/g2o/g2o/core/sparse_block_matrix.hpp
+++ b/Thirdparty/g2o/g2o/core/sparse_block_matrix.hpp
@@ -24,6 +24,13 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+// add these marcos to fix:
+// 1>f:\prjs\orb_slam3\thirdparty\g2o\g2o\core\sparse_block_matrix.hpp(277): fatal error C1001: 编译器中发生内部错误。
+
+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
+
namespace g2o {
using namespace Eigen;

@@ -249,7 +256,8 @@ namespace g2o {
const typename SparseBlockMatrix<MatrixType>::SparseMatrixBlock* a=it->second;
int destOffset = it->first ? _rowBlockIndices[it->first - 1] : 0;
// destVec += *a * srcVec (according to the sub-vector parts)
- internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}
}
@@ -274,9 +282,12 @@ namespace g2o {
if (destOffset > srcOffset) // only upper triangle
break;
// destVec += *a * srcVec (according to the sub-vector parts)
- internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
- if (destOffset < srcOffset)
- internal::atxpy(*a, srcVec, destOffset, destVec, srcOffset);
+ // internal::axpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
+ if (destOffset < srcOffset) {
+ // internal::atxpy(*a, srcVec, destOffset, destVec, srcOffset);
+ _ATXPY(MatrixType, *a, srcVec, destOffset, destVec, srcOffset);
+ }
}
}
}
@@ -305,7 +316,8 @@ namespace g2o {
const typename SparseBlockMatrix<MatrixType>::SparseMatrixBlock* a=it->second;
int srcOffset = rowBaseOfBlock(it->first);
// destVec += *a.transpose() * srcVec (according to the sub-vector parts)
- internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _ATXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}

diff --git a/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h b/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
index 36ddfe6..91832cd 100644
--- a/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
+++ b/Thirdparty/g2o/g2o/core/sparse_block_matrix_ccs.h
@@ -24,6 +24,9 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
#ifndef G2O_SPARSE_BLOCK_MATRIX_CCS_H
#define G2O_SPARSE_BLOCK_MATRIX_CCS_H

@@ -122,7 +125,8 @@ namespace g2o {
const SparseMatrixBlock* a = it->block;
int srcOffset = rowBaseOfBlock(it->row);
// destVec += *a.transpose() * srcVec (according to the sub-vector parts)
- internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ // internal::atxpy(*a, srcVec, srcOffset, destVec, destOffset);
+ _ATXPY(MatrixType, *a, srcVec, srcOffset, destVec, destOffset);
}
}
}
diff --git a/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h b/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
index 7b13b9f..8605a5f 100644
--- a/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
+++ b/Thirdparty/g2o/g2o/core/sparse_block_matrix_diagonal.h
@@ -24,6 +24,9 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+#define _AXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::RowsAtCompileTime>(yoff) += (A) * (x).segment<MatrixType::ColsAtCompileTime>(xoff)
+#define _ATXPY(MatrixType,A,x,xoff,y,yoff)y.segment<MatrixType::ColsAtCompileTime>(yoff) += (A).transpose() * (x).segment<MatrixType::RowsAtCompileTime>(xoff)
+
#ifndef G2O_SPARSE_BLOCK_MATRIX_DIAGONAL_H
#define G2O_SPARSE_BLOCK_MATRIX_DIAGONAL_H

@@ -94,7 +97,8 @@ namespace g2o {
int srcOffset = destOffset;
const SparseMatrixBlock& A = _diagonal[i];
// destVec += *A.transpose() * srcVec (according to the sub-vector parts)
- internal::axpy(A, srcVec, srcOffset, destVec, destOffset);
+ // internal::axpy(A, srcVec, srcOffset, destVec, destOffset);
+ _AXPY(MatrixType, A, srcVec, srcOffset, destVec, destOffset);
}
}


3 编译成功显示:

1
2
3
4
5
1>F:\prjs\ORB_SLAM3\Thirdparty\Pangolin\include\pangolin/gl/gldraw.h(109,1): warning C4267: “参数”: 从“size_t”转换到“GLint”,可能丢失数据
1> 正在创建库 F:/prjs/ORB_SLAM3_Fix/ORB_SLAM3/lib/mono_kitti.lib 和对象 F:/prjs/ORB_SLAM3_Fix/ORB_SLAM3/lib/mono_kitti.exp
1>mono_kitti.vcxproj -> F:\prjs\ORB_SLAM3_Fix\ORB_SLAM3\bin\mono_kitti.exe
1>已完成生成项目“mono_kitti.vcxproj”的操作。
========== 生成: 成功 1 个,失败 0 个,最新 4 个,跳过 0 个 ==========

如下是一个调用gif:

4 可能遇到的问题

4.1 g2o -> vasprint 找不到标识符

到它的声明处,就会发现,它的声明和定义均位于非活动预处理器块中,被宏定义WINDOWS关闭了。所以添加对应的宏定义即可:右键项目->属性->C++->预处理器->预处理器定义,为其添加一个变量,WINDOWS,保存设定之后,这个错误也就消失了。
以上过程也可以在cmakelist.txt中添加: ADD_DEFINITIONS(-DWINDOWS)

4.2 orb-slam3 -> cl.exe has no C++11 support. Please use a different C++ compiler.

因为cmakelist对于cl的c++11的支持方式需要如下编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,19 +17,20 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=native")

# Check C++11 or C++0x support
include(CheckCXXCompilerFlag)
-CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
-CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
-if(COMPILER_SUPPORTS_CXX11)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
- add_definitions(-DCOMPILEDWITHC11)
- message(STATUS "Using flag -std=c++11.")
-elseif(COMPILER_SUPPORTS_CXX0X)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
- add_definitions(-DCOMPILEDWITHC0X)
- message(STATUS "Using flag -std=c++0x.")
-else()
- message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
-endif()
+set (CMAKE_CXX_STANDARD 11)
+# CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
+# CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
+# if(COMPILER_SUPPORTS_CXX11)
+# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+# add_definitions(-DCOMPILEDWITHC11)
+# message(STATUS "Using flag -std=c++11.")
+# elseif(COMPILER_SUPPORTS_CXX0X)
+# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+# add_definitions(-DCOMPILEDWITHC0X)
+# message(STATUS "Using flag -std=c++0x.")
+# else()
+# message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
+# endif()

4.3 stdio-gcc.h找不到

将其改为stdio.h即可

4.4 Failed to run MSBuild command: C:/Program Files (x86)/MSBuild/14.0/bin/MSBuild.exe

将上述的MSBuild.exe添加到里面,然后https://developer.microsoft.com/zh-cn/windows/downloads/sdk-archive/
里面找到win8.1的sdk,安装即可。

4.5 fata_error: compiler internal error on msc1.cpp:1xxx编译器内部错误

和g2o/core/matrix_operation.h的代码有关系,参考如下修改:
https://github.com/RainerKuemmerle/g2o/issues/91
https://github.com/UZ-SLAMLab/ORB_SLAM3/pull/53

4.6 std::max找不到定义

添加:

1
#include <algorithm>

4.7 找不到openssl/md5.h

可以尝试安装strawberry-perl,里面有oepnssl的库,然后下载openssl的源码获得其include。
这里也有openssl编译好的链接库: https://indy.fulgan.com/SSL/LinkLibs/
我的下载地址为:https://indy.fulgan.com/SSL/LinkLibs/openssl-1.0.2g-x64_86-win64_LinkLibs.zip

配置完成后比如一些openssl的依赖就需要手动添加了:
F:\BASE_ENV\openSSL\openssl\include
F:\BASE_ENV\openSSL\openssl\lib\ssleay32.lib
F:\BASE_ENV\openSSL\openssl\lib\libeay32.lib

4.8 link2005 error 将所有项目->属性->c++->代码生成->MD改成MT

1
2
3
4
5
1>msvcprt.lib(MSVCP140.dll) : error LNK2005: "public: class std::basic_istream<char,struct std::char_traits<char> > & __cdecl std::basic_istream<char,struct std::char_traits<char> >::operator>>(double &)" (??5?$basic_istream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@AEAN@Z) 已经在 pangolin.lib(widgets.obj) 中定义
1>msvcprt.lib(MSVCP140.dll) : error LNK2005: "public: virtual __cdecl std::basic_iostream<char,struct std::char_traits<char> >::~basic_iostream<char,struct std::char_traits<char> >(void)" (??1?$basic_iostream@DU?$char_traits@D@std@@@std@@UEAA@XZ) 已经在 ORB_SLAM3.lib(System.obj) 中定义
1>libcpmt.lib(locale0.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”(stereo_euroc.obj 中)
1>libcpmt.lib(locale0.obj) : error LNK2005: "void __cdecl std::_Facet_Register(class std::_Facet_base *)" (?_Facet_Register@std@@YAXPEAV_Facet_base@1@@Z) 已经在 msv

4.9 QT5找不到FindQT5.cmake

1
2
3
4
5
6
7
8
9
10
# step1: 设置qt的安装位置
set(CMAKE_PREFIX_PATH "/opt/Qt/5.12.3/gcc_64") # windows下应该是msvc2015 或者之后的

# step2: 设置你需要的qt的库的dir
set(Qt5_DIR "${CMAKE_PREFIX_PATH}/lib/cmake/Qt5")
set(Qt5Widgets_DIR "${CMAKE_PREFIX_PATH}/lib/cmake/Qt5Widgets")
set(Qt5Network_DIR "${CMAKE_PREFIX_PATH}/lib/cmake/Qt5Network")
set(Qt5LinguistTools_DIR "${CMAKE_PREFIX_PATH}/lib/cmake/Qt5LinguistTools")

find_package(Qt5 COMPONENTS Widgets Network LinguistTools)

1 实现要求

  • 1 地面车辆按照规定的起点和终点运行
  • 2 地面车辆必须贴地运动
  • 3 地面车辆的必须有俯仰角的变化
  • 4 循环播放,且车辆经过路径动态高亮,下一次循环清除高亮

2 实现效果

在这里插入图片描述

3 实现代码

将如下代码替换到cesium的一个例子中即可:https://sandcastle.cesium.com/index.html?src=Interpolation.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
	var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false, //Disable InfoBox widget
selectionIndicator: false, //Disable selection indicator
shouldAnimate: true, // Enable animations
terrainProvider: Cesium.createWorldTerrain(),
});

//Enable lighting based on the sun position
viewer.scene.globe.enableLighting = true;

//Enable depth testing so things behind the terrain disappear.
viewer.scene.globe.depthTestAgainstTerrain = true;

//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);

let times = [
Cesium.JulianDate.fromIso8601("2018-07-19T15:18:00Z"),
Cesium.JulianDate.fromIso8601("2018-07-19T15:24:00Z")
];
let stTime = times[0];
let endTime = times[1];
var start = stTime.clone();
var stop = endTime.clone();

//Make sure viewer is at the desired time.
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end
viewer.clock.multiplier = 30;

//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);

//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius) {
var property = new Cesium.SampledPositionProperty();
for (var i = 0; i <= 360; i += 45) {
var radians = Cesium.Math.toRadians(i);
var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
var position = Cesium.Cartesian3.fromDegrees(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 500 + 1750
);
property.addSample(time, position);
console.log(time.toString(), " -> ", position.toString());

//Also create a point for each sample we generate.
viewer.entities.add({
position: position,
point: {
pixelSize: 8,
color: Cesium.Color.TRANSPARENT,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 3,
},
});
}
return property;
}

let positions = [
// new Cartesian3(1216348.1632364073, -4736348.958775471, 4081284.5528982095),
// new Cartesian3(1216369.1229444197, -4736377.467107148, 4081240.888485707)
new Cesium.Cartesian3(
-2358138.847340281,
-3744072.459541374,
4581158.5714175375
),
new Cesium.Cartesian3(
-2357231.4925370603,
-3745103.7886602185,
4580702.9757762635
),
];
let stPos = positions[0];
let endPos = positions[1];

// sampled postion's time resolution
let timeOfResolution = 6;

// using sampled property to get sampled data
let oriSamples = new Cesium.SampledProperty(Cesium.Cartesian3);
oriSamples.addSamples(times, positions);

// get sampled data, ervery "distanceOfResolution" we take a sample
let geodesic = new Cesium.EllipsoidGeodesic(
Cesium.Cartographic.fromCartesian(stPos),
Cesium.Cartographic.fromCartesian(endPos)
);
let lenInMeters = Math.ceil(geodesic.surfaceDistance); // avoid overflow when take samples
let samplesNum = Math.floor(
Cesium.JulianDate.secondsDifference(endTime, stTime) / timeOfResolution
);
//let secondsInterval = Math.floor(Cesium.JulianDate.secondsDifference(endTime, stTime) / samplesNum);
console.log(
"len: ",
lenInMeters,
"samplesNum",
samplesNum,
"secondsInterval",
timeOfResolution
);

// get sampled data, ervery "timeOfResolution" passed, we take a sample
let sampledPositions = [];
let sampledTimes = [];
for (let i = 0; i < samplesNum + 1; i++) {
let sampleTime = Cesium.JulianDate.addSeconds(
stTime,
i * timeOfResolution,
new Cesium.JulianDate()
);
let tmpPos = oriSamples.getValue(sampleTime);
console.log(sampleTime.toString(), " -> || -> ", tmpPos.toString());
sampledPositions.push(Cesium.Cartographic.fromCartesian(tmpPos));
sampledTimes.push(sampleTime);
}

let promise = Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
sampledPositions
).then(() => {
console.log(
"start adding!",
"time and pos size: ",
sampledTimes.length,
sampledPositions.length
);

let carPositionProperty = new Cesium.SampledPositionProperty();

// add positions which are clamped to ground to the carPositionProperty
for (let i = 0; i < samplesNum + 1; i++) {
carPositionProperty.addSample(
sampledTimes[i],
// new Cesium.Cartesian3.fromDegrees( // this way of changing pos is not right, all should be under WGS84
// sampledPositions[i].longitude,
// sampledPositions[i].latitude,
// sampledPositions[i].height));
Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[i])
);
// console.log(sampledTimes[i], " ------->>> ", sampledPositions[i]);
}

// after the clamped to ground data computed, dynamically show the path
let isConstant = false;
let curSegmentNo = 0; // the polyLine are divided into samplesNum's segements
let lastSegementNo = -1;
let p2 = Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[1]);
let curPolyline = [stPos]; // represent the polyline
console.log("init 2 points are: ", stPos.toString(), " ", p2.toString());
//let timeNow = Cesium.JulianDate.now().clone();

console.log("starting add entity");
viewer.entities.add({
polyline: {
// This callback updates positions each frame.
// Ellipsoid.WGS84.cartographicArrayToCartesianArray(sampledPositions),
positions: new Cesium.CallbackProperty(function (time, result) {
//console.log("len: ", lenInMeters, "samplesNum", samplesNum, "timeOfResolution", timeOfResolution);

//let st
curSegmentNo = Math.floor(
Cesium.JulianDate.secondsDifference(time, stTime) / timeOfResolution
);
console.log(curSegmentNo);
if (curSegmentNo !== lastSegementNo) {
//console.log("curSegmentNo is: ", curSegmentNo.toString(), "\ncurTime: ", time.toString(), "\nstTime: ", stTime.toString());
// tmmP => curPolyine[lastSegementNo+1 : CurSegmentNo]
let tmpP = Cesium.Ellipsoid.WGS84.cartographicToCartesian(
sampledPositions[curSegmentNo]
);
//console.log("adding new points: ", tmpP.toString(), "\nsize is:", curPolyline.length);
curPolyline.push(tmpP);
lastSegementNo = curSegmentNo;
}
// if reach the end of sampled positions, clear the polyline's positions
if (curSegmentNo === samplesNum - 1) {
curSegmentNo = 0;
curPolyline = [];
console.log("cleared!");
}

return curPolyline;
}, isConstant),
//clampToGround: true,
width: 5,
material: Cesium.Color.RED,
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: stTime,
stop: endTime,
}),
]),
},
});
console.log("end adding polyline");

//Compute the entity position property.
//var position = computeCirclularFlight(-112.110693, 36.0994841, 0.03);
var position = carPositionProperty;
//Actually create the entity
var entity = viewer.entities.add({
//Set the entity availability to the same interval as the simulation time.
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),

//Use our computed positions
position: position,

//Automatically compute orientation based on position movement.
orientation: new Cesium.VelocityOrientationProperty(position),

//Load the Cesium plane model to represent the entity
model: {
uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
minimumPixelSize: 64,
},

//Show the path as a pink line sampled in 1 second increments.
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW,
}),
width: 1,
},
});

//Add button to view the path from the top down
Sandcastle.addDefaultToolbarButton("View Top Down", function () {
viewer.trackedEntity = undefined;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90))
);
});

//Add button to view the path from the side
Sandcastle.addToolbarButton("View Side", function () {
viewer.trackedEntity = undefined;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(-90),
Cesium.Math.toRadians(-15),
7500
)
);
});

//Add button to track the entity as it moves
Sandcastle.addToolbarButton("View Aircraft", function () {
// viewer.trackedEntity = entity;
viewer.zoomTo(
viewer.entities,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(15),
Cesium.Math.toRadians(-45),
2500
)
);
});

//Add a combo box for selecting each interpolation mode.
Sandcastle.addToolbarMenu(
[
{
text: "Interpolation: Linear Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 1,
interpolationAlgorithm: Cesium.LinearApproximation,
});
},
},
{
text: "Interpolation: Lagrange Polynomial Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 5,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
});
},
},
{
text: "Interpolation: Hermite Polynomial Approximation",
onselect: function () {
entity.position.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
});
},
},
],
"interpolationMenu"
);
});

4 参考

0 paxos算法解决了什么问题

现在有n个人组成提一个会议,这个会议的目的是为了确定今年的税率,那么每个人都会提出自己认为的今年的合理的税率,为了大家能够达成一致,有了paxos算法。实际里,这个会议就是一个集群。

1 paxos算法详解

1.1 基本概念

    1. 角色:
      提议者(proposer):提议的发起者。
      批准者(acceptor):对提议进行批准。
      学习者(learner):作为一致性协议的副本因子,对被超过半数的 acceptor
      批准的方案进行学习。
    1. 提议(propose):
      Proposer(提议者)可以提出提议,最终要达成一致的 value 就在提议里。Acceptor(接收者)根据提议的编号来选择是否接受(accept)提议。如果超过半数的cceptor 接收了一个提议,那么这个提议就被接受(accepted)了,提议里的 value 也就被选定了。
    1. 仲裁集合(Quorums): 是 Acceptor(假设有 N 个)的一个子集,任何两个 Quorums 至少有一个相同的成员,也就是说一个 quorums 是一个包含了超过 N/2+1 个 Acceptor 的一个集合。
    1. 提议序号(Proposal number)和批准值(agreed value):对于任意一个给定的提议者,其给定的提议对应的提议号必定全局唯一。每一个提议(n,v 代表的话)都有一个提议序号(n)和其想要通过的提议值(v),为了便于理解,设想一个权力机关去决定今年的税率,那么每个人会给自己的提议有一个全局唯一的序号(n),然后还包含对于想要决定的目标变量(税率)的值(n)也就是税率的大小。

1.2 算法具体流程

Paxos(这里主要说的是 Basic paxos 算法)的具体流程分为两阶段:

Phase1

  • (1) Phase 1a - proposer 准备(Prepare):
    Proposer 创建一个条消息,我们将其称为“Prepare”,以数字 n 标识。请注意,n 不是要提议的值也不是可能会被同意的商定的值,而只是一个数字,该数字由提议者唯一标识此初始消息(发送给接收者)。数字 n 必须大于此提议者在先前的任何 Prepare 消息中使用的任何数字。然后,它将包含 n 的 Prepare消息发送到接受方的一个仲裁集合(超过一半以上的 Acceptor 的集合)。请注意,Prepare 消息仅包含数字 n(也就是说,它不必包含例如建议的值,通常用 v 表示)。提议者决定谁在仲裁集合。如果提议者不能与至少一个接受者的仲裁集合进行通信,则他不应启动 Paxos。
  • (2) Phase 1b - acceptor 承诺(Promise):
    任何接受者都在等待来自任何提议者的准备消息。如果接受方收到一条准备消息,则接受方必须查看刚刚收到的准备消息的标识符编号 n。有两种情况:
      1. 如果 n 高于接受者从任何提议者接收到的每个先前的提议编号,那么接受者必须向提议者返回一条消息,我们称其为“承诺”,以忽略所有将来的提议数量少的提议比 n。如果接受者在过去某个时候接受了提议者的提议(也就是第二阶段中批准的提议),则在对提议者的答复中必须包含先前接受的的提议编号(例如 m)和相应的接受(批准)值(例如 w)。
      1. 否则(即,n 小于或等于接受者从任何提议者收到的任何先前提议编号),接受者可以忽略收到的提议。在这种情况下,Paxos 不必工作。但是,为了优化起见,发送拒绝(Nack)响应将告诉提议者它停止以 n 作为提议的序号去建立共识(这是优化手段,因为即使不主动告诉,其提议的序号也会增长)。

Phase2

  • (1) Phase 2a - proposer 发送 Accept 消息:
    • 1 如果提议者从接受者的一个仲裁集合中获得大部分承诺,则需要为其提议设置值 v。
    • 2 如果任何接受者先前已接受过一个提议,那么他们会将这个提议发送给提议者,提议者现在必须将其提议值 v 设置为与接受者报告的(与这些接受者最高提议编号关联的)提议值(也就是提议者之前接受过的提议),我们称之为 z。
    • 3 如果到目前为止,没有一个接受者接受提议,则提
      议者可以选择其最初想要提议的值,例如 x。
      在这个阶段,提议者会发送一个 Accept 信息:(n,v)给一个接受者个仲裁集合。(n 就是之前提议者发给接受者的准备信息里的提议里的提议序号。v=z 或 者 v=x (当所有接受者都没有接受过提议时。)),这个 accept 请求可以理解为一个请求:“请接受这个提议!”。
  • (2) Phase 2b - acceptor 发送 Accepted 消息:
    如果接受者从提议者接收到 Accept 信息(n,v),分为两种情况:
    • 1 如果:它尚未承诺(在 Paxos 协议的阶段 1b 中)仅考虑提议序号大于 n 的提议时,则它应该将(刚接收到的 Accept 消息的)值 v 注册为(协议)的接受值,并向提议者和每个学习者(通常是提议者本身)发送一条接受消息。
    • 2 否则:它可以忽略这个 Accept 消息。

1.3 paxos算法的活锁问题

如上图,(从上图也可以看出集群里的一个机器可以有多重身份)首先 s1-3 给出 3 个 preparemeesage,然后 s1-3 是一个 quorum 得到了大多数支持,开始发送 phase2-a 的 accept 信息(A3.1),但是由于在发送phase2-a 的 accept 消息之前,s3-5 给出 3 个 prepare message,s3 又更新了自己主张的提议,那么之前发送的(accept 信息)A3.1 就不会被回应,之后 s1-2 发现由于超过半数(s3-5)拒绝了自己,然后就出了新的一轮 prepare message 为 P4.1,
然后,就这样循环往复下去出现了活锁。

其改进方法:

  • 解决方案1:出现冲突的时候,在重新开始执行prepare message流程之前,施加随机的延迟;让其他proposers有机会完成value的确认
  • 解决方案2:multi-paxos会使用leader来避免活锁;

2 paxos算法实现

2.1 paxos.cpp

模拟paxos算法的主要流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdlib.h>
#include <stdio.h>
#include "Paxos/Acceptor.h"
#include "Paxos/Proposer.h"
#include "lib/Thread.h"
#include "lib/Lock.h"
#include "lib/mapi.h"
#include "lib/atom.h"
#include "lib/Logger.h"

paxos::Proposer p[5];
paxos::Acceptor a[11];
mdk::Mutex l[11];
int finishedCount = 0;
int finalValue = -1;
bool isFinished = false;
mdk::uint64 g_start;
mdk::Logger g_log;

void* Proposer(void *id)
{
mdk::Logger log;
char logName[256];
sprintf( logName, "Proposer%d", (long)id );
log.SetLogName(logName);
log.SetMaxLogSize(10);
log.SetMaxExistDay(30);
log.SetPrintLog(false);

paxos::Proposer &proposer = p[(long)id];
paxos::PROPOSAL value = proposer.GetProposal();
paxos::PROPOSAL lastValue;


int acceptorId[11];
int count = 0;

mdk::uint64 start = mdk::MillTime();
while ( true )
{
value = proposer.GetProposal();//拿到提议
log.Info("Info", "Proposer%d号开始(Propose阶段):提议=[编号:%d,提议:%d]\n",
(long)id, value.serialNum, value.value);
count = 0;
int i = 0;
for (i = 0; i < 11; i++ )
{
/*
* 发送消息到第i个acceptor
* 经过一定时间达到acceptor,sleep(随机时间)模拟
* acceptor处理消息,mAcceptors[i].Propose()
* 回应proposer
* 经过一定时间proposer收到回应,sleep(随机时间)模拟
* proposer处理回应mProposer.proposed(ok, lastValue)
*/
mdk::m_sleep(rand()%500);//经过随机时间,消息到达了mAcceptors[i]
//处理消息
l[i].Lock();
bool ok = a[i].Propose(value.serialNum, lastValue);
l[i].Unlock();
mdk::m_sleep(rand()%500);//经过随机时间,消息到达Proposer
//处理Propose回应
if ( !proposer.Proposed(ok, lastValue) ) //重新开始Propose阶段
{
mdk::m_sleep(1000);//为了降低活锁,多等一会让别的proposer有机会完成自己的2阶段批准
break;
}
paxos::PROPOSAL curValue = proposer.GetProposal();//拿到提议
if ( curValue.value != value.value )//acceptor本次回应可能推荐了一个提议
{
log.Info("Info", "Proposer%d号修改了提议:提议=[编号:%d,提议:%d]\n",
(long)id, curValue.serialNum, curValue.value);
break;
}
acceptorId[count++] = i;//记录愿意投票的acceptor
if ( proposer.StartAccept() ) break;
}
//检查有没有达到Accept开始条件,如果没有表示要重新开始Propose阶段
if ( !proposer.StartAccept() ) continue;

//开始Accept阶段
//发送Accept消息到所有愿意投票的acceptor
value = proposer.GetProposal();
log.Info("Info", "Proposer%d号开始(Accept阶段):提议=[编号:%d,提议:%d]\n",
(long)id, value.serialNum, value.value);
for (i = 0; i < count; i++ )
{
//发送accept消息到acceptor
//减少accept阶段等待时间,加快收敛
mdk::m_sleep(rand()%200);//经过随机时间,accept消息到达acceptor
//处理accept消息
l[acceptorId[i]].Lock();
bool ok = a[acceptorId[i]].Accept(value);
l[acceptorId[i]].Unlock();
mdk::m_sleep(rand()%200);//经过随机时间,accept回应到达proposer
//处理accept回应
if ( !proposer.Accepted(ok) ) //重新开始Propose阶段
{
mdk::m_sleep(1000);//为了降低活锁,多等一会让别的proposer有机会完成自己的2阶段批准
break;
}
if ( proposer.IsAgree() )//成功批准了提议
{
start = mdk::MillTime() - start;
log.Info("Info", "Proposer%d号的提议被批准,用时%lluMS:最终提议 = [编号:%d,提议:%d]\n", (long)id, start, value.serialNum, value.value);
g_log.Info("Info", "Proposer%d号的提议被批准,用时%lluMS:最终提议 = [编号:%d,提议:%d]\n", (long)id, start, value.serialNum, value.value);
if(finalValue == -1) finalValue = value.value;
else if(finalValue != value.value) finalValue = 0;
if ( 4 == mdk::AtomAdd(&finishedCount, 1) )
{
isFinished = true;
g_start = mdk::MillTime() - g_start;
if(finalValue > 0){
g_log.Info("Info", "Paxos完成,用时%lluMS,最终通过提议值为:%d\n", g_start, finalValue);
}
else{
g_log.Info("Info", "Paxos完成,用时%lluMS,最终结果不一致!\n", g_start);
}
}
return NULL;
}
}
}
return NULL;
}

//Paxos过程模拟演示程序
int main(int argc, char* argv[])
{
int i = 0;
g_log.SetLogName("Paxos");
g_log.SetMaxLogSize(10);
g_log.SetMaxExistDay(30);
g_log.SetPrintLog(true);
g_log.Info("Info", "5个Proposer, 11个Acceptor准备进行Paxos\n"
"每个Proposer独立线程,Acceptor不需要线程\n"
"Proposer编号从0-10,编号为i的Proposer初始提议编号和提议值是(i+1, i+1)\n"
"Proposer每次重新提议会将提议编号增加5\n"
"Proposer被批准后结束线程,其它线程继续投票最终,全部批准相同的值,达成一致。\n");
g_start = mdk::MillTime();
g_log.Info("Info", "Paxos开始\n" );
paxos::PROPOSAL value;

for ( i = 0; i < 5; i++ )
{
p[i].SetPlayerCount(5, 11);
value.serialNum = value.value = i + 1;
p[i].StartPropose(value);
}

mdk::Thread t[5];
for ( i = 0; i < 5; i++ ) t[i].Run(Proposer, (void*)i);
//for ( i = 0; i < 5; i++ ) t[i].WaitStop();
while(true){
if(isFinished) break;
mdk::m_sleep(500);
}
return 0;
}

2.2 proposer.cpp

模拟提议者的主要流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include "Proposer.h"

namespace paxos
{

Proposer::Proposer()
{
SetPlayerCount(0, 0);
}

Proposer::Proposer(short proposerCount, short acceptorCount)
{
SetPlayerCount(proposerCount, acceptorCount);
}

Proposer::~Proposer()
{
}

void Proposer::SetPlayerCount(short proposerCount, short acceptorCount)
{
m_proposerCount = proposerCount;
m_acceptorCount = acceptorCount;

return;
}

void Proposer::StartPropose(PROPOSAL &value)
{
m_value = value;
m_proposeFinished = false;
m_isAgree = false;
m_maxAcceptedSerialNum = 0;
m_okCount = 0;
m_refuseCount = 0;
m_start = time(NULL);

return;
}

PROPOSAL& Proposer::GetProposal()
{
return m_value;
}
/**
//提议者
class Proposer
{
public:
Proposer();
Proposer(short proposerCount, short acceptorCount);
virtual ~Proposer();
//设置参与者数量
void SetPlayerCount(short proposerCount, short acceptorCount);
//开始Propose阶段
void StartPropose(PROPOSAL &value);
//取得提议
PROPOSAL& GetProposal();
//提议被投票,Proposed失败则重新开始Propose阶段
bool Proposed(bool ok, PROPOSAL &lastAcceptValue);
//开始Accept阶段,满足条件成功开始accept阶段返回ture,不满足开始条件返回false
bool StartAccept();
//提议被接受,Accepted失败则重新开始Propose阶段
bool Accepted(bool ok);
//提议被批准
bool IsAgree();

private:
short m_proposerCount;///proposer数量
short m_acceptorCount;//acceptor数量
PROPOSAL m_value;//预备提议
bool m_proposeFinished;//完成拉票,准备开始二阶段
bool m_isAgree;//m_value被批准
unsigned int m_maxAcceptedSerialNum;//已被接受的提议中流水号最大的
time_t m_start;//阶段开始时间,阶段一,阶段二共用
short m_okCount;//投票数量,阶段一,阶段二共用
short m_refuseCount;//拒绝数量,阶段一,阶段二共用
};


//提议数据结构
typedef struct PROPOSAL
{
unsigned int serialNum;//流水号,1开始递增,保证全局唯一
unsigned int value;//提议内容
}PROPOSAL;
**/
bool Proposer::Proposed(bool ok, PROPOSAL &lastAcceptValue)
{
if ( m_proposeFinished ) return true;//可能是一阶段迟到的回应,直接忽略消息

if ( !ok )
{
m_refuseCount++;
//已有半数拒绝,不需要等待其它acceptor投票了,重新开始Propose阶段
//使用StartPropose(m_value)重置状态

//请完善下面逻辑
/**********Begin**********/
if ( m_refuseCount > m_acceptorCount/2 ){
// 重新开始投票阶段,将提案号自增
m_value.serialNum += 5;
StartPropose(m_value);
return false;
}
/**********End**********/
//拒绝数不到一半
return true;
}

m_okCount++;
/*
没有必要检查分支:serialNum为null
因为serialNum>m_maxAcceptedSerialNum,与serialNum非0互为必要条件
*/
//如果已经有提议被接受,修改成已被接受的提议
//请完善下面逻辑
/**********Begin**********/

if(lastAcceptValue.serialNum > m_maxAcceptedSerialNum){
m_value.value = lastAcceptValue.value;
m_maxAcceptedSerialNum = lastAcceptValue.serialNum;
return true;
}
/**********End**********/





//如果自己的提议被接受
if ( m_okCount > m_acceptorCount / 2 )
{
m_okCount = 0;
m_proposeFinished = true;
}
return true;
}

bool Proposer::StartAccept()
{
return m_proposeFinished;
}

bool Proposer::Accepted(bool ok)
{
if ( !m_proposeFinished ) return true;//可能是上次第二阶段迟到的回应,直接忽略消息

if ( !ok )
{
m_refuseCount++;
//已有半数拒绝,不需要等待其它acceptor投票了,重新开始Propose阶段
//使用StartPropose(m_value)重置状态
//请完善下面逻辑
/**********Begin**********/
if ( m_refuseCount > m_acceptorCount/2 ){
// 重新开始投票阶段,将提案号自增
m_value.serialNum += 5;
StartPropose(m_value);
return false;
}
/**********End**********/

return true;
}

m_okCount++;
if ( m_okCount > m_acceptorCount / 2 ) m_isAgree = true;

return true;
}

bool Proposer::IsAgree()
{
return m_isAgree;
}

}

2.3 acceptor.cpp

模拟接受者的主要流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "Acceptor.h"


namespace paxos
{


Acceptor::Acceptor(void)
{
m_maxSerialNum = 0;
m_lastAcceptValue.serialNum = 0;
m_lastAcceptValue.value = 0;
}

Acceptor::~Acceptor(void)
{
}
/***
//投票者
class Acceptor
{
public:
Acceptor(void);
virtual ~Acceptor(void);

//同意投票
bool Propose(unsigned int serialNum, PROPOSAL &lastAcceptValue);
//接受提议
bool Accept(PROPOSAL &value);

private:
PROPOSAL m_lastAcceptValue;//最后接受的提议
unsigned int m_maxSerialNum;//Propose提交的最大流水号
};


//提议数据结构
typedef struct PROPOSAL
{
unsigned int serialNum;//流水号,1开始递增,保证全局唯一
unsigned int value;//提议内容
}PROPOSAL;

***/
bool Acceptor::Propose(unsigned int serialNum, PROPOSAL &lastAcceptValue)
{
if ( 0 == serialNum ) return false;
//提议不通过
if ( m_maxSerialNum > serialNum ) return false;
//接受提议
//请完善下面逻辑

/**********Begin**********/
//m_lastAcceptValue = lastAcceptValue;
// If n is higher than every previous proposal number received, from any of the Proposers, by the Acceptor,
// then the Acceptor must return a message, which we call a "Promise", to the Proposer, to ignore all future
// proposals having a number less than n. If the Acceptor accepted a proposal at some point in the past, it
// must include the previous proposal number, say m, and the corresponding accepted value, say w, in its response to the Proposer.
m_maxSerialNum = serialNum;

lastAcceptValue = m_lastAcceptValue; // which contains m -> w
/**********End**********/

return true;
}

bool Acceptor::Accept(PROPOSAL &value)
{
if ( 0 == value.serialNum ) return false;
//Acceptor又重新答应了其他提议
//请完善下面逻辑
/**********Begin**********/
if (m_maxSerialNum > value.serialNum ){
return false;
}
// 然后接受新的提案
m_lastAcceptValue = value;
/**********End**********/


//批准提议通过
//请完善下面逻辑
/**********Begin**********/
m_maxSerialNum = value.serialNum;
/**********End**********/

return true;
}

}

3 参考

[1] https://en.wikipedia.org/wiki/Paxos_(computer_science)#Byzantine_Paxos
[2] https://www.cs.rutgers.edu/~pxk/417/notes/paxos.html
[3] https://github.com/HaHaJeff/Paxos/blob/master/basic_paxos.md

需求

给定地对空导弹发射起始点和终止点(3维坐标),拟合导弹轨迹

思路

  • 1 首先简单化问题,假定导弹轨迹位于同一个平面内,则可以将轨迹投影到x-O-z平面,先求出x-z的关系,求一系列的散点,
    然后根据x,y的关系求出y的散点值即可
  • 2 之所以选取抛物线,因为导弹轨迹需要垂直发射的过程,存在斜率不存在的点常见的曲线为抛物线和圆,由于圆显然不合适,我们选取抛物线

实现的数学逻辑

  • 1 起始点和终止点的定义:
    1
    2
    3
    # startPos and endPos in [lon, lat, height]
    stPos = [117.9844565, 24.1658956, 1.597]
    edPos = [118.1167438, 24.5656241, 5211.73]
  • 2 数学关系
    1
    2
    3
    4
    5
    6
    7
    # 由于整个轨迹位于一个平面内,则可以考虑x,y存在线性关系
    y1 = k * x1 + b
    y2 = k * x2 + b
    则y可以由x得到

    # [x1, y1, z1]为起始点,则有如下抛物线,根据终止点[x2, y2, z2]求出p
    (z-z1)**2 = p*(x-x1)

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import numpy as np
import matplotlib.pyplot as plt


# set figure
fig = plt.figure()
ax = fig.gca(projection='3d')

################################################## input args #############################################
# startPos and endPos in [lon, lat, height]
stPos = [117.9844565, 24.1658956, 1.597]
edPos = [118.1167438, 24.5656241, 5211.73]

# # stTime, endTime, in seconds, assume it take 1 minute for missile from emit to explode
# stTime = 0
# endTime = 10

# step number
stepNumber = 30
################################################## input args #############################################

################################################## output #############################################
# res
resX = []
resY = []
resZ = []
################################################## output #############################################

# land defend air means, should satisfy: st.z < ed.z
# (z-z1)**2 = p*(x-x1)
def computeLnadDefendAirParabolaWithSteps(
st, # start point: [x, y, z]
ed, # end point: [x, y, z]
resX, # x-axis's result
resY, # y-axis's result
resZ, # z-axis's result
stepNum # how may fitted points you want
):
resX.append(st[0])
resY.append(st[1])
resZ.append(st[2])

p = (ed[2] - st[2])**2 / (ed[0] - st[0])
step = (ed[0] - st[0]) / stepNum
for stepCnt in range(stepNum)[1:]:
curX = st[0] + stepCnt * step
curZ = (p * (curX - st[0])) ** 0.5 + st[2]
resZ.append(curZ)
# compute projection from x to y: x = k*y + b
if st[1] != ed[1]:
k = (st[0] - ed[0]) / (st[1] - ed[1])
b = st[0] - k * st[1]
curY = (curX - b) / k
resY.append(curY)
resX.append(curX)
else:
# all the y is the same
resY.append(curY)
resX.append(curX)
resX.append(ed[0])
resY.append(ed[1])
resZ.append(ed[2])

# print
for idx in range(stepNum + 1):
print("%10.6f, %10.6f, %10.3f"%(resX[idx], resY[idx], resZ[idx]))


computeLnadDefendAirParabolaWithSteps(stPos, edPos, resX, resY, resZ, stepNumber)

# 绘制图形
ax.plot(resX, resY, resZ, label='fitted curve')

# 显示图例
ax.legend()

# 显示图形
plt.show()