4.3.2 C++ 平面拟合的实现

4.3.2 C++ 平面拟合的实现

参考教程:

gaoxiang12/slam_in_autonomous_driving: 《自动驾驶中的SLAM技术》对应开源代码 (github.com)

Eigen打印输出_打印eigen矩阵-CSDN博客

1. 编写 Plane fitting

1.1 创建文件夹

通过终端创建一个名为Plane_fitting的文件夹以保存我们的VSCode项目,在/Plane_fitting目录下打开vscode

rosnoetic@rosnoetic-VirtualBox:~$ mkdir -p Plane_fitting

rosnoetic@rosnoetic-VirtualBox:~$ cd Plane_fitting/

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting$ code .

1.2 编写平面拟合程序

新建文件linear_fitting.cpp

使用快速 SVD 分解,仅计算 A 矩阵 SVD 结果的最后一列。在计算完成后,将点的具体取值代入本方程,要求它们的平方误差不超过预设的阈值。下面这段测试程序把随机生成的平面参数作为真值,在平面上取若干个点,再加入噪声,做平面拟合。

linear_fitting.cpp粘贴如下代码并保存(Ctrl+S)

// 引入Eigen头文件与常用类型
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <iostream>

Eigen::IOFormat HeavyFmt(Eigen::FullPrecision, 0, ", ", ";\n", "[", "]", "[", "]");

template <typename S>
bool FitPlane(std::vector<Eigen::Matrix<S, 3, 1>>& data, Eigen::Matrix<S, 4, 1>& plane_coeffs, double eps = 1e-2) {
    if (data.size() < 3) {
        return false;
    }

    Eigen::MatrixXd A(data.size(), 4);
    for (int i = 0; i < data.size(); ++i) {
        A.row(i).head<3>() = data[i].transpose();
        A.row(i)[3] = 1.0;
    }

    Eigen::JacobiSVD<Eigen::MatrixXd>  svd(A, Eigen::ComputeThinV);
    plane_coeffs = svd.matrixV().col(3);

    // check error eps
    for (int i = 0; i < data.size(); ++i) {
        double err = plane_coeffs.template head<3>().dot(data[i]) + plane_coeffs[3];
        if (err * err > eps) {
            return false;
        }
    }

    return true;
}

int main(int argc, char argv) {

    Eigen::Vector4d true_plane_coeffs(0.1, 0.2, 0.3, 0.4);
    true_plane_coeffs.normalize();

    std::vector<Eigen::Vector3d> points;

    // 生成平面中的点的数量
    int FLAGS_num_tested_points_plane = 10;
    double FLAGS_noise_sigma = 0.01;

    // 随机生成仿真平面点
    cv::RNG rng;
    for (int i = 0; i < FLAGS_num_tested_points_plane; ++i) {
        // 先生成一个随机点,计算第四维,增加噪声,再归一化
        Eigen::Vector3d p(rng.uniform(0.0, 1.0), rng.uniform(0.0, 1.0), rng.uniform(0.0, 1.0));

        double n4 = -p.dot(true_plane_coeffs.head<3>()) / true_plane_coeffs[3];

        p = p / (n4 + std::numeric_limits<double>::min());  // 防止除零

        p += Eigen::Vector3d(rng.gaussian(FLAGS_noise_sigma),
                                                    rng.gaussian(FLAGS_noise_sigma), 
                                                    rng.gaussian(FLAGS_noise_sigma));

        points.emplace_back(p);

        // 验证在平面上
        printf("res of p: %.2f\n",p.dot(true_plane_coeffs.head<3>()) + true_plane_coeffs[3] );
    }

    Eigen::Vector4d estimated_plane_coeffs;
    FitPlane(points, estimated_plane_coeffs);
    estimated_plane_coeffs = estimated_plane_coeffs.transpose();
    std::cout << estimated_plane_coeffs.format(HeavyFmt) << std::endl;

    return 0;

}

2. 新建 CMakeLists.txt 文件

新建CMakeLists.txt文件

CMakeLists.txt中添加如下内容:

# 声明要求的cmake最低版本
cmake_minimum_required(VERSION 2.8 )

# 声明一个cmake工程
project(Plane_fitting)

# 添加C++ 11支持
set(CMAKE_CXX_FLAGS "-std=c++11")

# 寻找OpenCV库
find_package( OpenCV REQUIRED)
# 添加头文件
include_directories("/usr/include/eigen3"  ${OpenCV_INCLUDE_DIRS})

# 添加一个可执行文件
add_executable(linear_fitting linear_fitting.cpp)
# 链接OpenCV库
target_link_libraries(linear_fitting ${OpenCV_LIBS})

3. 编译并执行

ctrl+alt+T打开终端,执行如下指令进行cmake编译

rosnoetic@rosnoetic-VirtualBox:~$ cd Plane_fitting/

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting$ mkdir build

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting$ cd build/

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting/build$ cmake ..

接着make对工程进行编译

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting/build$ make

进一步的调用可执行文件linear_fitting

rosnoetic@rosnoetic-VirtualBox:~/Plane_fitting/build$ ./linear_fitting 

res of p: -0.00
res of p: -0.00
res of p: -0.01
res of p: 0.00
res of p: 0.00
res of p: -0.00
res of p: 0.01
res of p: 0.00
res of p: 0.01
res of p: 0.00
[[0.186755046854881];
 [0.363655524983479];
 [0.546691644450618];
 [0.730756770417559]]

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用libwebsockets-4.3.2写的C++ Websockets服务端的示例代码: ```c++ #include <libwebsockets.h> #include <iostream> #include <string> using namespace std; struct WebsocketServerData { int counter; }; static int callback_websockets(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { WebsocketServerData *server_data = (WebsocketServerData*)lws_context_user(lws_get_context(wsi)); switch (reason) { case LWS_CALLBACK_ESTABLISHED: cout << "Websocket client connection established" << endl; break; case LWS_CALLBACK_SERVER_WRITEABLE: { string message = "Hello, World! " + to_string(server_data->counter); unsigned char *buffer = new unsigned char[LWS_SEND_BUFFER_PRE_PADDING + message.length() + LWS_SEND_BUFFER_POST_PADDING]; memcpy(buffer + LWS_SEND_BUFFER_PRE_PADDING, message.c_str(), message.length()); lws_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING, message.length(), LWS_WRITE_TEXT); delete[] buffer; server_data->counter++; } break; case LWS_CALLBACK_RECEIVE: cout << "Received message from client: " << string((char*)in, len) << endl; lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); break; case LWS_CALLBACK_CLOSED: cout << "Websocket connection closed" << endl; break; default: break; } return 0; } int main(int argc, char **argv) { struct lws_context_creation_info context_info; struct lws_protocols protocols[] = { { "websockets", callback_websockets, sizeof(WebsocketServerData), 0 }, { NULL, NULL, 0, 0 } }; WebsocketServerData server_data = { 0 }; int port = 8080; int exit_code = 0; memset(&context_info, 0, sizeof(context_info)); context_info.port = port; context_info.protocols = protocols; context_info.user = &server_data; struct lws_context *context = lws_create_context(&context_info); if (!context) { cerr << "Failed to create libwebsocket context" << endl; exit_code = 1; goto cleanup; } while (true) { lws_service(context, 50); } cleanup: if (context) { lws_context_destroy(context); } return exit_code; } ``` 这个示例代码创建了一个Websocket服务端,监听指定的端口,并且每收到一条客户端消息就回复一条消息。你可以根据你的需求调整服务端回复消息的内容和频率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值