SLAM相关手撕代码题

参考:

1.最小二乘拟合直线

  • 单纯使用C++:
std::vector<double> LineFit(const std::vector<double>& x, const std::vector<double>& y)
{
    double k = 1, b = 1;
    const double eps = 1e-5;

    while(true)
    {
        double h11 = 0, h12 = 0, h21 = 0, h22 = 0;
        double je1 = 0, je2 = 0;
        for(int i = 0; i < x.size(); i++)
        {
            double res = x[i] * k + b - y[i];
            double dk = x[i];
            double db = 1;
            h11 += dk * dk;
            h12 += dk * db;
            h21 += db * dk;
            h22 += db * db;
            je1 += dk * res;
            je2 += db * res;
        }
        double detH = h11 * h22 - h12 * h21;
        double delta_k = - (h22 * je1 - h12 * je2) / detH;
        double delta_b = - (h11 * je2 - h21 * je1) / detH;
        k += delta_k;
        b += delta_b;
        if(abs(delta_k) < eps && abs(delta_b) < eps)
            break;
    }

    return {k, b};
}
  • 使用 Eigen:
std::vector<double> LineFit(const std::vector<double>& x, const std::vector<double>& y)
{
    Eigen::Vector2d X = Eigen::Vector2d::Zero();

    const double eps = 1e-5;

    while(true)
    {
        Eigen::Vector2d b = Eigen::Vector2d::Zero();
        Eigen::Matrix2d H = Eigen::Matrix2d::Zero();

        for(int i = 0; i < x.size(); i++)
        {
            double res = x[i] * X(0) + X(1) - y[i];
            Eigen::Vector2d J;
            J << x[i], 1;
            H += J * J.transpose();
            b += res * J;
        }
        Eigen::Vector2d delta_X = -H.inverse() * b;
        X += delta_X;
        if(delta_X.norm() < eps)
            break;
    }

    return {X(0), X(1)};
}

2.RANSAC 最小二乘拟合直线

cv::Mat dataFit(const vector<double> &x, const vector<double> &y)
{
    int dataSize = x.size();
    cout << "x.size = " << x.size() << ", y.size = " << y.size() << endl;
    cv::Mat A = cv::Mat::zeros(dataSize, 2, CV_64FC1);
    cv::Mat b = cv::Mat::zeros(dataSize, 1, CV_64FC1);

    // 构造矩阵A和b
    for(int i = 0; i < dataSize; i++)
    {
        A.at<double>(i, 0) = 1;
        A.at<double>(i, 1) = x[i];
        b.at<double>(i, 0) = y[i];      // 注意这个只有第0列
    }
    cv::Mat X;
    // SVD分解求解 Ax = b线性最小二乘
    solve(A, b, X, DECOMP_SVD);
//    cout << "A = " << endl;
//    cout << A << endl;
//    cout << "b = " << endl;
//    cout << b << endl;
//    cout << endl << "estimate result : " << endl;
//    cout << "ae = " << X << endl << endl;

    return X;
}

/**
 * 1.随机选择两个点,拟合一条直线
 * 2.计算其他所有点到这个点的距离,距离小于一定的值认为是内点,统计所有内点的个数
 * 3.重复上述过程M次,寻找内点数最大的模型
 * 4.利用所有的内点重新进行直线拟合
 * @return  估计得到的直线参数矩阵
 */
 // threshold:判断为内点的距离阈值;  M: 重复寻找模型的次数
cv::Mat RansacFit(double threshold, int M, const vector<double> &x_data, const vector<double> &y_data)
{
    int dataSize = x_data.size();
    int maxInlineNum = 0;
    vector<vector<bool>> inlineIndexes;   // 所有模型的内点索引
    int bestModelIndex  = 0;             // 最好模型的索引
    double best_k, best_b;

    for(int i = 0; i < M; i++)
    {
        int num1 = rand() % dataSize;       // 第1个点
        int num2 = rand() % dataSize;       // 第2个点
        double x1 = x_data[num1];
        double x2 = x_data[num2];
        double y1 = y_data[num1];
        double y2 = y_data[num2];
        double k = (y2 - y1) / (x2 - x1);
        double b = y1 - k * x1;
        double inlineNum = 0;       // 内点个数
        vector<bool> inlineIndex;    // 当前模型的内点索引

        cout << "第 " << i << " 次模型寻找," << "num1 = " << num1 << ", num2 = " << num2 << endl;
        cout << "k = " << k << ", b = " << b << endl;

        // 遍历所有的数据点,寻找内点
        for(int j = 0; j < dataSize; j++)
        {
            double error = k * x_data[j] + b - y_data[j];
            cout << " error " << j << " = " << error << ", ";
            if(myabs(error) < threshold)     // 内点
            {
                inlineNum++;
                inlineIndex.push_back(true);  // 当前模型下是否属于内点
            }
            else
            {
                inlineIndex.push_back(false);
            }
        }
        inlineIndexes.push_back(inlineIndex);      // 存储当前模型的内点
        cout << "内点个数 : " << inlineNum << endl;
        if(inlineNum > maxInlineNum)
        {
            maxInlineNum = inlineNum;
            bestModelIndex = i;
            best_k = k;
            best_b = b;
        }
    }
    cout << "最佳模型参数:" << "第 " << bestModelIndex << " 次寻找到" << endl;
    cout << "best_k = " << best_k << ", best_b = " << best_b << ", 最大内点数:" << maxInlineNum << endl;

    vector<bool> bestIndex = inlineIndexes[bestModelIndex];    // 最佳模型的索引
    vector<double> inlineX_data, inlineY_data;
    for(int i = 0; i < dataSize; i++)   // 遍历最好的模型的所有内点,组成新的数据集
    {
        if(bestIndex[i])  // 这个点属于内点
        {
            inlineX_data.push_back(x_data[i]);
            inlineY_data.push_back(y_data[i]);
        }
    }

    return dataFit(inlineX_data, inlineY_data);
}

3.开根号

3.1.高斯牛顿法

double MySqrt(double a)
{
    const double eps = 1e-5;
    double x = a;
    while(true)
    {
        // 开2次根号
        // double J = 2 * x;      
        // double b = x * x - a;
        
        // 开3次根号
        double J = 3 * x * x;
        double b = x * x * x - a;
        
        double JTJ = J * J;
        double JTb = J * b;
        double delta_x = -JTb / JTJ;
        if(abs(delta_x) < eps)
            break;
        x += delta_x;
    }

    return x;
}

3.2.二分法

double MySqrt(double a)
{
    const double eps = 1e-5;
    double left = 0;
    double right = a;
    double x = 0;
    while(left <= right)
    {
        if(abs(left - right) < eps)
            break;
        x = (left + right) / 2;
        // 开二次根号
        double res = x * x;
        if(res > a)
            right = x;
        else
            left = x;
    }

    return x;
}

4.高斯牛顿法拟合曲线

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值