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

这里先给出结果图:
OSG Qt glider

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算法的活锁问题

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算法实现

示例源码:main.cpp、proposer.cpp、acceptor.cpp。

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()

使用拉格朗日插值平滑曲线

1 平滑前后效果对比:

平滑前

平滑后

2 拉格朗日插值代码:(参考cesium源码)

结合拉格朗日原理:https://zhuanlan.zhihu.com/p/135229305

源码如下:https://github.com/CesiumGS/cesium/blob/b30/Source/Core/LagrangePolynomialApproximation.js#L39

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
/**
* Interpolates values using Lagrange Polynomial Approximation.
*
* @param {Number} x The independent variable for which the dependent variables will be interpolated.
* @param {Number[]} xTable The array of independent variables to use to interpolate. The values
* in this array must be in increasing order and the same value must not occur twice in the array.
* @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three
* dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}.
* @param {Number} yStride The number of dependent variable values in yTable corresponding to
* each independent variable value in xTable.
* @param {Number[]} [result] An existing array into which to store the result.
* @returns {Number[]} The array of interpolated values, or the result parameter if one was provided.
*/
LagrangePolynomialApproximation.interpolateOrderZero = function(x, xTable, yTable, yStride, result) {
if (!defined(result)) {
result = new Array(yStride);
}

var i;
var j;
var length = xTable.length;

for (i = 0; i < yStride; i++) {
result[i] = 0;
}

for (i = 0; i < length; i++) {
var coefficient = 1;

for (j = 0; j < length; j++) {
if (j !== i) {
var diffX = xTable[i] - xTable[j];
coefficient *= (x - xTable[j]) / diffX;
}
}

for (j = 0; j < yStride; j++) {
result[j] += coefficient * yTable[i * yStride + j];
}
}

return result;
};

3 cesium中实现代码

放入https://sandcastle.cesium.com/index.html?src=Callback%20Property.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
// This example illustrates a Callback Property, a property whose
// value is lazily evaluated by a callback function.
// Use a CallbackProperty when your data can't be pre-computed
// or needs to be derived from other properties at runtime.
var viewer = new Cesium.Viewer("cesiumContainer");
viewer.clock.shouldAnimate = true;

var startLatitude = 35;
var startLongitude = -120;
var endLongitude;
var startTime = Cesium.JulianDate.now();
var stTime = startTime;


// Add a polyline to the scene. Positions are dynamic.
var isConstant = false;
var redLine = viewer.entities.add({
polyline: {
// This callback updates positions each frame.
positions: new Cesium.CallbackProperty(function (time, result) {
endLongitude =
startLongitude +
0.001 * Cesium.JulianDate.secondsDifference(time, startTime);

// 平滑前效果
// return computeCirclularFlight(-112.110693, 36.0994841, 0.03, time);
// 平滑后效果
return computeCirclularFlightSmoothly(-112.110693, 36.0994841, 0.03, 500, stTime, time);
/*
return Cesium.Cartesian3.fromDegreesArray(
[startLongitude, startLatitude, endLongitude, startLatitude],
Cesium.Ellipsoid.WGS84,
result
);
*/
}, isConstant),
width: 5,
material: Cesium.Color.RED,
},
});


//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius, curTime) {

// let a = JulianDate.secondsDifference(JulianDate.now(), stTime);
let positions = [];
// Generate a random circular pattern with letying heights.
let property = new Cesium.SampledPositionProperty();
for (let i = 0; i <= 360; i += 45) {
let radians = Cesium.Math.toRadians(i);
let time = Cesium.JulianDate.addSeconds(
stTime,
i,
new Cesium.JulianDate()
);

let position = Cesium.Cartesian3.fromDegrees(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 500 +
Math.random() * 20 * Cesium.JulianDate.secondsDifference(curTime, stTime)
);

positions.push(position);
property.addSample(time, position);
}

return positions;
}

// Generate a random circular pattern with varying heights.
function computeCirclularFlightSmoothly(lon, lat, radius, levelHeight, stTime, curTime) {

// let a = JulianDate.secondsDifference(JulianDate.now(), stTime);
let positions = [];
let llhPositions = [];
// let property = new PositionProperty();
// Generate a random circular pattern with letying heights.
for (let i = 0; i <= 360; i += 45) {
let radians = Cesium.Math.toRadians(i);
let time = Cesium.JulianDate.addSeconds(
stTime,
i,
new Cesium.JulianDate()
);

let tmpPoint = new Cesium.Cartographic(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 0.1 * levelHeight + levelHeight +
Cesium.Math.random() * 20 * Cesium.JulianDate.secondsDifference(curTime, stTime)
);
llhPositions.push(tmpPoint);
positions.push(
Cesium.Cartesian3.fromDegrees(
tmpPoint.longitude,
tmpPoint.latitude,
tmpPoint.height
));
}

// let iptPositions = Array<Cartographic>();
let iptPositions = [];
let xRes = [];
let xTable = []; // interpolate at xTable[i]
for(let ix = 0; ix < positions.length; ++ix) {
xTable.push(ix * 100);
}
let yTable = []; // used to interpolate, in {la1, lon1, h1, l2, lon2, h2}
for(let iy = 0; iy < positions.length; ++iy) {
yTable.push(llhPositions[iy].longitude);
yTable.push(llhPositions[iy].latitude);
yTable.push(llhPositions[iy].height);
}
let yStride = 3; // 3 dependent vaule in yTable is viewed as one item

for (let ix = 0; ix < xTable[positions.length - 1]; ++ix) {
let iptRes = [];
Cesium.LagrangePolynomialApproximation.interpolateOrderZero(
ix,
xTable,
yTable,
yStride,
iptRes
);

iptPositions.push(
Cesium.Cartesian3.fromDegrees(
iptRes[0],
iptRes[1],
iptRes[2]
));
}

return iptPositions;
}

// Keep the view centered.
viewer.trackedEntity = redLine;

在cesium中实现HPR

主要参考例子:

https://sandcastle.cesium.com/index.html?src=HeadingPitchRoll.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
        let canvas = viewer.canvas;
canvas.setAttribute("tabindex", "0"); // needed to put focus on the canvas
canvas.addEventListener("click", function () {
canvas.focus();
});
canvas.focus();

let scene = viewer.scene;

let pathPosition = new SampledPositionProperty();
this.planePath = viewer.entities.add({
id: "keyboardDemoPlanePath",
position: pathPosition,
// name: "path",
path: {
show: true,
leadTime: 0,
trailTime: 60,
width: 10,
resolution: 1,
material: new PolylineGlowMaterialProperty({
glowPower: 0.3,
taperPower: 0.3,
color: Color.PALEGOLDENROD
})
}
});

let camera = viewer.camera;
let controller = scene.screenSpaceCameraController;
let r = 0;
let center = new Cartesian3();

let hpRoll = new HeadingPitchRoll();
let hpRange = new HeadingPitchRange();
let speed = 1;
let deltaRadians = _Math.toRadians(3.0);

let position = Cartesian3.fromDegrees(
114.4152843,
30.512368,
5000.0
);
let speedVector = new Cartesian3();
let fixedFrameTransform = Transforms.localFrameToFixedFrameGenerator(
"north",
"west"
);

let planePrimitive = scene.primitives.add(
Model.fromGltf({
url: `${webSetting.assetsUrl}/map/su30.gltf`,
// url: `${webSetting.assetsUrl}/map/apaqi_x-90_y180.glb`,
modelMatrix: Transforms.headingPitchRollToFixedFrame(
position,
hpRoll,
Ellipsoid.WGS84,
fixedFrameTransform
),
minimumPixelSize: 256
})
);
this.plane = planePrimitive;

planePrimitive.readyPromise.then(function (model) {
// Play and loop all animations at half-speed
model.activeAnimations.addAll({
multiplier: 0.5,
loop: ModelAnimationLoop.REPEAT
});

// Zoom to model
r = 2.0 * Math.max(model.boundingSphere.radius, camera.frustum.near);
controller.minimumZoomDistance = r * 0.5;
Matrix4.multiplyByPoint(
model.modelMatrix,
model.boundingSphere.center,
center
);
let heading = _Math.toRadians(230.0);
let pitch = _Math.toRadians(-20.0);
hpRange.heading = heading;
hpRange.pitch = pitch;
hpRange.range = r * 50.0;
camera.lookAt(center, hpRange);
});

this.keyBoardListener = function (e) {
switch (e.keyCode) {
case 40:
if (e.shiftKey) {
// speed down
speed -= 10;
speed = Math.max(speed, 1);
} else {
// pitch down
hpRoll.pitch -= deltaRadians;
if (hpRoll.pitch < -_Math.TWO_PI) {
hpRoll.pitch += _Math.TWO_PI;
}
}
break;
case 38:
if (e.shiftKey) {
// speed up
speed += 10;
speed = Math.min(speed, 1000);
} else {
// pitch up
hpRoll.pitch += deltaRadians;
if (hpRoll.pitch > _Math.TWO_PI) {
hpRoll.pitch -= _Math.TWO_PI;
}
}
break;
case 39:
if (e.shiftKey) {
// roll right
hpRoll.roll += deltaRadians;
if (hpRoll.roll > _Math.TWO_PI) {
hpRoll.roll -= _Math.TWO_PI;
}
} else {
// turn right
hpRoll.heading += deltaRadians;
if (hpRoll.heading > _Math.TWO_PI) {
hpRoll.heading -= _Math.TWO_PI;
}
}
break;
case 37:
if (e.shiftKey) {
// roll left until
hpRoll.roll -= deltaRadians;
if (hpRoll.roll < 0.0) {
hpRoll.roll += _Math.TWO_PI;
}
} else {
// turn left
hpRoll.heading -= deltaRadians;
if (hpRoll.heading < 0.0) {
hpRoll.heading += _Math.TWO_PI;
}
}
break;
default:
break;
}
};
document.addEventListener("keydown", this.keyBoardListener);

let headingSpan = document.getElementById("heading");
let pitchSpan = document.getElementById("pitch");
let rollSpan = document.getElementById("roll");
let speedSpan = document.getElementById("speed");
let fromBehind = document.getElementById("fromBehind");

this.preUpateSpeedViewFunc = (scene, time) => {
speedVector = Cartesian3.multiplyByScalar(
Cartesian3.UNIT_X,
speed / 10,
speedVector
);

// let lastPosition = position.clone();
position = Matrix4.multiplyByPoint(
planePrimitive.modelMatrix,
speedVector,
position
);
this.center = position;


// pathPosition.addSample(JulianDate.now(), position);
pathPosition.addSample(time, position);
// console.log("adding: t->p: ", JulianDate.now().toString(), " -> ",position.toString());
Transforms.headingPitchRollToFixedFrame(
position,
hpRoll,
Ellipsoid.WGS84,
fixedFrameTransform,
planePrimitive.modelMatrix
);

// view at fisrt player
if (this.fpsFlag) {
// Zoom to model
Matrix4.multiplyByPoint(
planePrimitive.modelMatrix,
planePrimitive.boundingSphere.center,
center
);

hpRange.heading = hpRoll.heading;
hpRange.pitch = hpRoll.pitch;
hpRange.range = camera.frustum.near * 0.05;
// mainly using this to set the camera position relatively to the plane
camera.lookAt(center, hpRange);
camera.moveForward(8.2);
camera.moveLeft(2.65);
camera.moveUp(0.7);
// console.log("cur camera hpr is: ",camera.heading, " ", camera.pitch, " ", camera.roll,
// "hpRoll's hpr is ", hpRoll.heading, " ", hpRoll.pitch, " ", hpRoll.roll, " ");

// set the hpr according to the hpr of the plane
camera.setView({
orientation: {
heading : hpRoll.heading, // east, default value is 0.0 (north)
pitch : hpRoll.pitch, // default value (looking down)
roll : hpRoll.roll // default value
}
});
}
};
viewer.scene.preUpdate.addEventListener(this.preUpateSpeedViewFunc, this);

this.preUpateHPRFunc = (scene, time) => {
this.headingShow = _Math.toDegrees(hpRoll.heading).toFixed(2);

this.pitchShow = _Math.toDegrees(hpRoll.pitch).toFixed(2);
this.rollShow = _Math.toDegrees(hpRoll.roll).toFixed(2);
this.speedShow = speed.toFixed(2);

let tmp = Cartographic.fromCartesian(this.center, Ellipsoid.WGS84);
this.planeLon = tmp.longitude.toFixed(2);
this.planeLat = tmp.latitude.toFixed(2);
this.planeHei = tmp.height.toFixed(2);

// headingSpan.innerHTML = _Math.toDegrees(hpRoll.heading).toFixed(
// 1
// );
// pitchSpan.innerHTML = _Math.toDegrees(hpRoll.pitch).toFixed(1);
// rollSpan.innerHTML = _Math.toDegrees(hpRoll.roll).toFixed(1);
// speedSpan.innerHTML = speed.toFixed(1);
};
viewer.scene.preRender.addEventListener(this.preUpateHPRFunc, this);
});
}

1 物理地址tag/set/block_offset说明:

1.1 以 2路组相连cache为例子介绍cache行和物理地址行的构成

2way cache
上图是2路组相连的cache(引自https://www.sciencedirect.com/topics/computer-science/set-associative-cache)。
(直接映射cache也被称为单路cache的原因就是因为只有1路。)

1.1.1 cache的一行构成

tag data block flag bits
  • tag: 标签位用于比较在way1和way0两路中,哪一个cache块存了我们的目的数据(可以看到图中的两个等号的用硬件实现的比较器,就是用来比较way1 and way0中的两个tag的)
  • data block: 数据位用来存储(内存和cache交换的数据块)目标tag的数据块。
  • v: 有效位用来 表示该cache块是否有效。(复杂的cache设计可能有更多位)(比如当一开始cache里面一个数据块都没有为空的时候,所有的v标记都为0,然后当该数据块载入了,代表该块有效了,那么将其置为1即可。)

1.1.2 要去cache中找数据的内存地址

一个地址构成如下图: 左侧为MSB,右侧为LSB(lowest significant bits):

tag index block offset
  • block offset: 说的是对于内存地址来说,其后block offset个字节的数据会构成一个和cache做数据交换的块,故就是cache块的大小;
  • index: 说的是对于内存地址来说,其应该被映射到cache里的哪一组,故该部分的位数代表的是整个cache能hold住多少组,而组数目又由每个cache块大小和(4路组相连那路数就是4)“路数”有关的,其计算方式为:
    $$
    setNums = \frac{cacheWholeSize}{(numOfWays) * (blockSize))}
    $$
    拿最开始的2路组相连来说,其中的MemoryAddress里的set我们看到了是2位,那么这个内存地址就可以根据set的两位去找到自己在cache中对应的组是哪一组,拿图中来说,当set的两位为10时,那么对应到第3组set2。
  • tag: 用来在使用index选出cache位于哪一组后去比较具体的cache块位于哪一路。

    2 结合实际例子

    2.1 example1

    一个四路相联cache,CPU字长为4字节,内存和cache都是以字节编址,cache和内存交换单位为块,每个块大小为512字节,cache能够容纳1024个块。如果物理内存为32位地址:
  • q1: 画出cache和内存的地址格式,标明块内偏移地址、组号和标签位置;
  • q2: 请计算内存地址FAB12389(16进制)在cache中可能的位置块号;

answer q1:
根据1.1.2的计算方式:
$$
setNums = \frac{cacheWholeSize}{(numOfWays) * (blockSize))} = \frac{1024512 Byte}{4512Byte} = 2^{8}
$$
故,最后的结果如下:

tag(32-8-9=17bit) index(8bit) block offset(9bit)

answer q2:
其内存地址分成4个Byte分别是:

FA(1111 1010) B1(1011 0001) 23(0010 0011) 89(1000 1001)

根据q1的结果,我们容易得出其tag,index和blockoffset对应的值分别如下:

tag(32-8-9=17bit) = (don't care) index(8bit)= 1001 0001(B) = 128+16+1=145(D) block offset(9bit) = 1 1000 1001(B)
则其位于第145组,那么由于块号从左上角从左到右从上到下块号递增,那么位于145组的那几块的块号就是答案:

145*4 + (0,1,2,3) = 580,581,582,583块

2.2 example2 [2]

An Example
A computer uses 32-bit byte addressing. The computer uses a 2-way associative cache with a capacity of 32KB. Each cache block contains 16 bytes. Calculate the number of bits in the TAG, SET, and OFFSET fields of a main memory address.

Answer
Since there are 16 bytes in a cache block, the OFFSET field must contain 4 bits (24 = 16). To determine the number of bits in the SET field, we need to determine the number of sets. Each set contains 2 cache blocks (2-way associative) so a set contains 32 bytes. There are 32KB bytes in the entire cache, so there are 32KB/32B = 1K sets. Thus the set field contains 10 bits (210 = 1K).

Finally, the TAG field contains the remaining 18 bits (32 - 4 - 10). Thus a main memory address is decomposed as shown below.












18 10 4
TAG SET OFFSET

3 ref:

osg模型调整光照以及模型闪烁问题[z-lighting]

0 模型闪烁原因推测

  • 1 关于模型闪烁的问题,很可能是由于坐标的值的有效位数超过了7位,目前的opengl的gpu渲染(老一点的显卡以及gl)都是以单精度来渲染点的位置的,所以如果坐标很大,很可能导致单精度无法精确表示有效位数超过7位的数据,然后发生截断。
  • 2 关于z-lighting的问题,尝试了网上大多数的方法:可以使用osg封装的polygonOffset来避免重叠面片交替出现的问题,当然最好的方案是找的模型本身重叠面片就少

1 meshlab手动调模型

filters->Normals,curve,orientations->transform…

2 使用osg降低模型的点的坐标大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
osg::Vec3f center1 = TerrainM1->getBound().center();
float radius1 = TerrainM1->getBound().radius();
osg::Vec3f center2 = TerrainM2->getBound().center();
float radius2 = TerrainM2->getBound().radius();
TerrainM1->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z()) *
osg::Matrix::rotate(osg::DegreesToRadians(180.0), 1, 0, 0) *
osg::Matrix::translate(center1.x(), center1.y(), center1.z()) *
osg::Matrix::translate(0, 0, radius2 * 0.80 + radius1)
);

// to modify the model's points render value
mRoot->getChild(0)->asTransform()->asMatrixTransform()->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z())
);
osgDB::writeNodeFile(*(mRoot->getChild(0)->asNode()), "sc.obj");

3 关于材质,光照,以及模型读写旋转平移的例子

关于材料的颜色光照等可以参考https://learnopengl-cn.readthedocs.io/zh/latest/02%20Lighting/03%20Materials/

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
 // mRoot是一个osg::ref_ptr<osg::Group>
auto TerrainM1 = new osg::MatrixTransform;
auto terrain1 = new osg::PositionAttitudeTransform;

auto TerrainM2 = new osg::MatrixTransform;
auto terrain2 = new osg::PositionAttitudeTransform;
osgDB::Options *a = new osgDB::Options(std::string("noRotation")); // 关掉模型优化绘制(OSG在加载obj模型的时候,会默认将模型绕x轴逆时针旋转90度,此处设置不旋转)

// setting material
osg::Material *material = new osg::Material;
material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.75, 0.80, 0.75, 1.0));
material->setAmbient(osg::Material::FRONT, osg::Vec4(0.75, 0.80, 0.75, 1.0));
// material->setShininess(osg::Material::FRONT, 90.0);

// turn off light effect
auto Model1 = osgDB::readNodeFile(part1Path, a);
auto pState1 = Model1->getOrCreateStateSet();
pState1->setMode(GL_LIGHTING, osg::StateAttribute::ON);
pState1->setAttribute(material);
// pState1->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
// pState1->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
// set polygon offset
osg::ref_ptr<osg::PolygonOffset> polyOff = new osg::PolygonOffset();
float mFactor = 1.0, mUnits = 1.0;
polyOff->setFactor(mFactor);
polyOff->setUnits(mUnits);
pState1->setAttributeAndModes(new osg::PolygonOffset(-1.0f,-1.0f),osg::StateAttribute::ON);
material->setEmission(osg::Material::FRONT, osg::Vec4(0.75, 0.80, 0.75, 1.0));
// pState1->setAttributeAndModes(polyOff.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

// auto pGeom1 = Model1->asGeode()->asGeometry();
// pGeom1->getOrCreateStateSet()->setMode(GL_NV_framebuffer_multisample_coverage, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);
TerrainM1->addChild(Model1);
TerrainM1->setName(modelPrefix + "1");


auto Model2 = osgDB::readNodeFile(part2Path, a);
auto pState2 = Model2->getOrCreateStateSet();
pState2->setMode(GL_LIGHTING, osg::StateAttribute::ON);
pState2->setAttribute(material);

pState2->setMode(GL_BLEND, osg::StateAttribute::ON);
TerrainM2->addChild(Model2);
TerrainM2->setName(modelPrefix + "2");



// rotate to make sure the building is standing on the ground
osg::Vec3f center1 = TerrainM1->getBound().center();
float radius1 = TerrainM1->getBound().radius();
osg::Vec3f center2 = TerrainM2->getBound().center();
float radius2 = TerrainM2->getBound().radius();
TerrainM1->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z()) *
osg::Matrix::rotate(osg::DegreesToRadians(180.0), 1, 0, 0) *
osg::Matrix::translate(center1.x(), center1.y(), center1.z()) *
osg::Matrix::translate(0, 0, radius2 * 0.80 + radius1)
);
mRoot->addChild(TerrainM1);
TerrainM2->setMatrix(
osg::Matrix::translate(-center2.x(), -center2.y(), -center2.z()) *
osg::Matrix::rotate(osg::DegreesToRadians(180.0), 1, 0, 0) *
osg::Matrix::translate(center2.x(), center2.y(), center2.z()) *
osg::Matrix::translate(0, 0, 0.5 * radius2)
);
mRoot->addChild(TerrainM2);

// to modify the model's points render value
mRoot->getChild(0)->asTransform()->asMatrixTransform()->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z())
);
mRoot->getChild(1)->asTransform()->asMatrixTransform()->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z())
);
mRoot->getChild(4)->asTransform()->asMatrixTransform()->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z())
);
mRoot->getChild(5)->asTransform()->asMatrixTransform()->setMatrix(
osg::Matrix::translate(-center1.x(), -center1.y(), -center1.z())
);


osgDB::writeNodeFile(*(mRoot->getChild(0)->asNode()), "sc.obj");
osgDB::writeNodeFile(*(mRoot->getChild(1)->asNode()), "de.obj");
osgDB::writeNodeFile(*(mRoot->getChild(4)->asNode()), "par1.obj");
osgDB::writeNodeFile(*(mRoot->getChild(5)->asNode()), "par2.obj");