gtsam OrientedPlane3及OrientedPlane3Factor的使用

一、OrientedPlane3
构造函数是来自平面的方程系数:ax+by+cz+d=0的(a,b,c,d)
是有方向的。

比如一个平面,平行与xoz平面,在xoy平面的投影线段与x轴平行,处于y轴上方2处,这个平面的法向量可以是(0,1,0):方向指向y轴正方向,也可以是(0,-1,0),方向指向y轴负方向。具体定哪个,要看自己的使用需求。
不同的法向量,得出不同的参数d,比如第一个,其参数d=-2,第二个则是2。
即d的正负号,取决于原点在法向量的那一侧。

二、OrientedPlane3Factor
这个factor,用来描述从某个位姿观察到的一个平面,约束涉及的变量就是pose、plane,约束内容就是这个pose观察到的这个平面的方程。

构造函数的说明:
在这个pose下的平面方程的系数,而不是全局坐标系下的系数。

三、测试
在这里插入图片描述

using symbol_shorthand::X;
using symbol_shorthand::P;

void test_room_plane(){
    OrientedPlane3 p1(0,1,0,0);
    OrientedPlane3 p2(0,-1,0,1);
    OrientedPlane3 p3(-1,0,0,4);

    OrientedPlane3 p4(1,0,0,-3);
    OrientedPlane3 p5(0,1,0,-2);
    OrientedPlane3 p6(1,0,0,-2);

    OrientedPlane3 p7(0,-1,0,3);
    OrientedPlane3 p8(0,1,0,-2);
    OrientedPlane3 p9(-1,0,0,5);

    std::vector<OrientedPlane3> planes={p1,p2,p3,p4,p5,p6,p7,p8,p9};

    //朝x轴正方向
    gtsam::Pose3 pose1(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(0.0, 0.5, 0.0));
    gtsam::Pose3 pose2(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(0.5, 0.5, 0.0));
    gtsam::Pose3 pose3(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(1, 0.5, 0.0));
    gtsam::Pose3 pose4(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(1.5, 0.5, 0.0));
    gtsam::Pose3 pose5(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(2, 0.5, 0.0));
    gtsam::Pose3 pose6(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(2.5, 0.5, 0.0));
    gtsam::Pose3 pose7(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(3, 0.5, 0.0));
    gtsam::Pose3 pose8(gtsam::Rot3::Ypr(0.0, 0.0, 0.0), Point3(3.5, 0.5, 0.0));

    //朝y轴正方向
    gtsam::Pose3 pose9(gtsam::Rot3::Ypr(M_PI/2, 0.0, 0.0), Point3(3.5, 0.5, 0.0));
    gtsam::Pose3 pose10(gtsam::Rot3::Ypr(M_PI/2, 0.0, 0.0), Point3(3.5, 1, 0.0));
    gtsam::Pose3 pose11(gtsam::Rot3::Ypr(M_PI/2, 0.0, 0.0), Point3(3.5, 1.5, 0.0));
    gtsam::Pose3 pose12(gtsam::Rot3::Ypr(M_PI/2, 0.0, 0.0), Point3(3.5, 2, 0.0));
    gtsam::Pose3 pose13(gtsam::Rot3::Ypr(M_PI/2, 0.0, 0.0), Point3(3.5, 2.5, 0.0));

    //朝x轴正方向
    gtsam::Pose3 pose14(gtsam::Rot3::Ypr(0, 0.0, 0.0), Point3(3.5, 2.5, 0.0));
    gtsam::Pose3 pose15(gtsam::Rot3::Ypr(0, 0.0, 0.0), Point3(4, 2.5, 0.0));
    gtsam::Pose3 pose16(gtsam::Rot3::Ypr(0, 0.0, 0.0), Point3(4.5, 2.5, 0.0));
    gtsam::Pose3 pose17(gtsam::Rot3::Ypr(0, 0.0, 0.0), Point3(5, 2.5, 0.0));

    std::vector<gtsam::Pose3> poses={pose1,
                                        pose2,
                                        pose3,
                                        pose4,
                                        pose5,
                                        pose6,
                                        pose7,
                                        pose8,
                                        pose9,
                                        pose10,
                                        pose11,
                                        pose12,
                                        pose13,
                                        pose14,
                                        pose15,
                                        pose16,
                                        pose17};

    //simple check
    auto p4_loc=p4.transform(pose9);
    pose9.print("pose9");
    p4.print("p4");
    p4_loc.print("p4_loc");


    //观测数据关联
    std::map<int,std::vector<int>> pose_id_to_plane_ids;//全部都加了1
    for(int i=1;i<9;i++){
        pose_id_to_plane_ids[i]={1,2,3};
    }
    for(int i=9;i<11;i++){
        pose_id_to_plane_ids[i]={1,3,4};
    }
    for(int i=11;i<13;i++){
        pose_id_to_plane_ids[i]={5,6,7};
    }
    for(int i=13;i<17;i++){
        pose_id_to_plane_ids[i]={6,7,8,9};
    }


    //构建factor
    gtsam::NonlinearFactorGraph graph;
    Values initialEstimate;

    Pose3 delta(Rot3::Ypr(0.0,0.0,0.0),Point3(0.05,-0.10,0.20));
    const auto x_p_noise = noiseModel::Isotropic::Sigma(3, 0.001);
    gtsam::Vector6 sigmas;
    sigmas<<0.3,0.3,0.3,0.1,0.1,0.1;
    auto pose_noise=noiseModel::Diagonal::Sigmas(sigmas);

    for(auto iter=pose_id_to_plane_ids.begin();iter!=pose_id_to_plane_ids.end();iter++){
        int pose_id=iter->first-1;
        std::vector<int> pids=iter->second;
        gtsam::Pose3 po=poses[pose_id];

        //pose加入噪声

        //pose加入graph先验
        graph.emplace_shared<PriorFactor<Pose3>>(X(pose_id), po, pose_noise);
        initialEstimate.insert(X(pose_id), po);

        //转换到pose的相对观测
        for(auto pid:pids){
            int planid=pid-1;

            auto pn=planes[planid];
            auto related_plane=pn.transform(po);

            //orientedplane factor
            Vector4 z=related_plane.planeCoefficients();
            OrientedPlane3Factor opf(z,x_p_noise,X(pose_id),P(planid));

            graph.add(opf);

            if(!initialEstimate.exists(P(planid))){
                initialEstimate.insert(P(planid),pn);
            }
        }   
    }



    GaussNewtonParams params;
    // params.setVerbosity("ERROR");
    params.setMaxIterations(20);
    params.setRelativeErrorTol(-std::numeric_limits<double>::max());
    // params.setErrorTol(-std::numeric_limits<double>::max());
    params.setAbsoluteErrorTol(-std::numeric_limits<double>::max());

    Values result = GaussNewtonOptimizer(graph, initialEstimate, params).optimize();

    std::cout<<"\n\n--------optimized result-----------------\n";
    for(int i=0;i<poses.size();i++){
        if(result.exists(X(i))){
            // std::cout<<"pose"<<i<<",optimized result:\n";
            std::string n="pose"+std::to_string(i+1);
            // result.at(X(i)).print(n);
            auto op=result.at<Pose3>(X(i));
            std::cout<<n<<": \nypr="<<op.rotation().ypr().transpose()<<"\npos="<<op.translation().transpose()<<std::endl;
            std::cout<<"\tdiff ypr="<<(op.rotation().ypr()-poses[i].rotation().ypr()).transpose()<<"\n"
            <<"\tdiff pos ="<<(op.translation()-poses[i].translation()).transpose()<<"\n";
            std::cout<<"\n";
        }
    }
    std::cout<<"\n\n";
    for(int i=0;i<planes.size();i++){
        if(result.exists(P(i))){
            // std::cout<<"pose"<<i<<",optimized result:\n";
            std::string n="plane"+std::to_string(i+1)+"\n";
            auto op=result.at<OrientedPlane3>(P(i));
            result.at(P(i)).print(n);
            std::cout<<"diff coeff = "<<(op.planeCoefficients()-planes[i].planeCoefficients()).transpose()<<std::endl;
            std::cout<<"\n";
        }
    }

}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是使用C++代码实现地面点云后使用GTSAM库完成地平面的残差构造的示例代码: ```c++ // 包含GTSAM库的头文件 #include <gtsam/geometry/Point3.h> #include <gtsam/geometry/Plane3.h> #include <gtsam/inference/Key.h> #include <gtsam/nonlinear/NonlinearFactorGraph.h> #include <gtsam/nonlinear/Values.h> #include <gtsam/slam/PriorFactor.h> #include <gtsam/slam/PointPlaneFactor.h> #include <gtsam/linear/GaussianNoiseModel.h> // 包含PCL库的头文件 #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/filters/extract_indices.h> #include <pcl/filters/voxel_grid.h> int main() { // 读取点云数据 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ>("cloud.pcd", *cloud); // 提取地面点云 pcl::PointCloud<pcl::PointXYZ>::Ptr ground(new pcl::PointCloud<pcl::PointXYZ>); pcl::ExtractIndices<pcl::PointXYZ> extract; pcl::PointIndices::Ptr inliers(new pcl::PointIndices); for (int i = 0; i < cloud->points.size(); i++) { if (cloud->points[i].z < 0.3) { inliers->indices.push_back(i); } } extract.setInputCloud(cloud); extract.setIndices(inliers); extract.setNegative(false); extract.filter(*ground); // 构造地平面的因子图节点 gtsam::Vector3 normal(0, 0, 1); double distance = 0; gtsam::PriorFactor<gtsam::Vector3> planeFactor(1, normal, gtsam::noiseModel::Isotropic::Sigma(3, 0.1)); // 构造地平面的残差 gtsam::Point3 point(0, 0, distance); gtsam::Plane3 plane(normal, distance); gtsam::PointPlaneFactor factor(1, point, plane, gtsam::noiseModel::Isotropic::Sigma(3, 0.1)); // 将因子图节点和残差添加到因子图中 gtsam::NonlinearFactorGraph graph; graph.add(planeFactor); graph.add(factor); // 初始化因子图变量 gtsam::Values initial; initial.insert(1, normal); // 优化因子图 gtsam::Values result = gtsam::LevenbergMarquardtOptimizer(graph, initial).optimize(); // 输出优化结果 std::cout << "Optimized normal: " << result.at<gtsam::Vector3>(1) << std::endl; return 0; } ``` 在上面的代码中,我们首先读取了点云数据,并提取了地面点云。接着,我们使用GTSAM的PriorFactor来创建地平面的因子图节点。然后,我们使用PointPlaneFactor来构造地平面的残差。需要注意的是,我们为地平面的因子图节点和残差都设置了噪声模型。 接下来,我们将因子图节点和残差添加到因子图中,并初始化因子图变量。最后,我们使用GTSAM的LevenbergMarquardtOptimizer来优化因子图,并输出优化结果。 需要注意的是,本代码仅是一个示例,实际应用中还需要根据具体问题进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值