深蓝学院自主泊车第6次作业-基于地图的定位

1 题目

对于本次作业,你需要完成自动代客泊车(AVP)系统的定位算法。作业提供了一个从仿真中获取的分割逆透视映射(IPM)图像数据集。你需要完成定位算法的处理过程,并显示AVP定位的最终结果。请完善src/avp_localization.cpp文件中的imageRegistration()函数。

2 解答

该任务的关键步骤为,
(1)使用pcl库中自带的最近邻函数nearestKSearch()完成数据关联。
(2)求两个点云的质心。
(3)求去质心点云。
(4)求矩阵H,
H = ∑ i p s r c i ( p d s t i ) T (1) H = \sum_i p_{src}^i (p_{dst}^i)^T \tag{1} H=ipsrci(pdsti)T(1)
(5)对矩阵H进行奇异值分解,
H = U Σ V T (2) H = U \Sigma V^T \tag{2} H=UΣVT(2)
(6)计算旋转矩阵 R R R
R = V [ 1 0 0 0 1 0 0 0 d e t ( V U T ) ] U T (3) R = V \begin{bmatrix}1 & 0 & 0\\ 0 & 1 & 0 \\ 0 & 0 & det(VU^T) \end{bmatrix} U^T \tag{3} R=V 10001000det(VUT) UT(3)
(7)计算平移向量 t t t
t = p ˉ d s t − R ⋅ p ˉ s r c (4) t = \bar{p}_{dst} - R\cdot \bar{p}_{src} \tag{4} t=pˉdstRpˉsrc(4)

代码如下,

// Registration frame to map
//返回T_world_vehicle表示vehicle系到world系的变换 
Eigen::Affine3d AvpLocalization::imageRegistration() {
  auto slot_num = curr_frame_.getSemanticElement(SemanticLabel::kSlot).size();
  auto dash_num = slot_num + curr_frame_.getSemanticElement(SemanticLabel::kDashLine).size();
  auto total_num = dash_num + curr_frame_.getSemanticElement(SemanticLabel::kArrowLine).size();
  //total_num表示slot,dashline,arrowline的总点数 

  std::vector<char> correspondences; // 1 indicate relative point is correspondent
  pcl::PointCloud<pcl::PointXYZ> cloud_in;  // points in map 
  //cloud_in表示地图中的点云数据 

  pcl::PointCloud<pcl::PointXYZ> cloud_out, cloud_est; // points in frame
  //cloud_out表示当前帧的点云数据 

  // get points in frame
  cloud_out.reserve(total_num);
  for (const auto &grid :
         curr_frame_.getSemanticElement(SemanticLabel::kSlot)) {
    cloud_out.emplace_back(grid.x(), grid.y(), 0);
  }
  for (const auto &grid :
         curr_frame_.getSemanticElement(SemanticLabel::kDashLine)) {
    cloud_out.emplace_back(grid.x(), grid.y(), 0);
  }
  for (const auto &grid :
         curr_frame_.getSemanticElement(SemanticLabel::kArrowLine)) {
    cloud_out.emplace_back(grid.x(), grid.y(), 0);
  }
  cloud_in.resize(cloud_out.size());

  Eigen::Matrix3f R = Eigen::Matrix3f::Identity();
  Eigen::Vector3f t = Eigen::Vector3f(0, 0, 0);
  const int max_iter = 10;
  for (int n = 0; n < max_iter; n++) {
    Eigen::Affine3f T_est = Eigen::Translation3f(t) * R;
    pcl::transformPointCloud(cloud_out, cloud_est, T_est); 
    //cloud_out为输入点云,表示待变换的点云数据 
    //cloud_est为输出点云,表示变换后的点云数据 

    /  TODO:  finding correspondences by kd-tree nearest neighbor search 
    correspondences.resize(total_num, 0);
    //使用kd-树做最近邻搜索

    //填充cloud_in
    //从地图中avp_map_找到最近的点,将其放入cloud_in中
    for (int i = 0; i < cloud_est.size(); ++i) {
      pcl::PointXYZ pt = cloud_est[i]; 

      int k = 1;
      std::vector<int> pointIdxNKNSearch(k);
      std::vector<float> pointNKNSquaredDistance(k);
      
      if (kdtree_all_.nearestKSearch(pt, k, pointIdxNKNSearch, pointNKNSquaredDistance) > 0) {
        int nearestIdx = pointIdxNKNSearch[0];
        float nearestDistance = pointNKNSquaredDistance[0];
        pcl::PointXYZ nearestPoint = all_cloud_in_->points[nearestIdx];

        //是否需要判断一下最近点之间的距离呢???

        cloud_in[i] = nearestPoint;
        correspondences[i] = 1;
      } 


    }
    //cloud_in为地图中的点 

    /

    // size_t num_pts = 0;
    // Eigen::Vector3f sum_point_in = Eigen::Vector3f::Zero();
    // Eigen::Vector3f sum_point_out = Eigen::Vector3f::Zero();
    // for (size_t i = 0; i < total_num; ++i) {
    //   if (correspondences[i]) {
    //     ++num_pts;
    //     sum_point_in += Eigen::Vector3f(cloud_in[i].x, cloud_in[i].y, cloud_in[i].z);
    //     sum_point_out += Eigen::Vector3f(cloud_out[i].x, cloud_out[i].y, cloud_out[i].z);
    //   }
    // }
    // Eigen::Vector3f u_point_in = sum_point_in / (num_pts + 1e-10);
    // Eigen::Vector3f u_point_out = sum_point_out / (num_pts + 1e-10);

    // std::cout << "\ricp itr " << n + 1 << " / 10, pts: " << num_pts
    //       << ", dYaw: " << GetYaw(Eigen::Quaternionf(R)) * kToDeg
    //       << ", trans: " << t.transpose() * float(kPixelScale);
    // std::cout.flush();

      TODO: calculate close-form R t //
    //初始点云cloud_est,表示当前帧的点
    //目标点云cloud_in,表示地图中的点
    //求cloud_est到cloud_in的变换,也即当前帧到地图的变换 
    
    //(1)求质心坐标
    Eigen::Vector3f average_pt_1 = Eigen::Vector3f::Zero();
    Eigen::Vector3f average_pt_2 = Eigen::Vector3f::Zero();
    int num_pts = 0;
    for (int i = 0; i < cloud_in.size(); ++i) {
      if (correspondences[i]) {
        ++num_pts;
        average_pt_1 += Eigen::Vector3f(cloud_est[i].x, cloud_est[i].y, cloud_est[i].z);
        average_pt_2 += Eigen::Vector3f(cloud_in[i].x, cloud_in[i].y, cloud_in[i].z);
      }
    }
    average_pt_1 /= (num_pts + 1e-10);
    average_pt_2 /= (num_pts + 1e-10);

    //(2)求去质心坐标
    std::vector<Eigen::Vector3f> cloud1, cloud2;
    for (int i = 0; i < cloud_in.size(); ++i) {
      if (correspondences[i]) {
        Eigen::Vector3f pt1 = Eigen::Vector3f(cloud_est[i].x, cloud_est[i].y, cloud_est[i].z);
        pt1 -= average_pt_1;
        cloud1.emplace_back(pt1);
        
        Eigen::Vector3f pt2 = Eigen::Vector3f(cloud_in[i].x, cloud_in[i].y, cloud_in[i].z);
        pt2 -= average_pt_2;
        cloud2.emplace_back(pt2);
      }
    }

    //(3)求3*3矩阵H
    //src * dst^T
    Eigen::Matrix3f H = Eigen::Matrix3f::Zero();
    for (int i = 0; i < cloud1.size(); ++i) {
      H += cloud1[i] * cloud2[i].transpose();
    }

    //对矩阵H做正交分解
    Eigen::JacobiSVD<Eigen::Matrix3f> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
    //获取U矩阵(左奇异向量)
    Eigen::Matrix3f U = svd.matrixU();
    //获取V矩阵(右奇异向量)
    Eigen::Matrix3f V = svd.matrixV();
    float dev1 = (V * U.transpose()).determinant();
    Eigen::Vector3f diag_elements(1.0, 1.0, dev1);
    Eigen::Matrix3f diag_matrix = diag_elements.asDiagonal();

    //(4)得到R1,t1
    Eigen::Matrix3f R1 = V * diag_matrix * U.transpose();
    Eigen::Vector3f t1 = average_pt_2 - R1 * average_pt_1;

    //更新最终的R和t
    R = R1 * R;
    t = R1 * t + t1; 
  //
  }
  std::cout << std::endl;
  t *= float(kPixelScale); // grid scale to world scale

  Eigen::Affine3d dT =
      Eigen::Translation3d(t.cast<double>()) * R.cast<double>();
  Eigen::Affine3d T_world_vehicle =
      dT * curr_frame_.T_world_ipm_ * T_vehicle_ipm_.inverse();
  return T_world_vehicle;
}

定位结果展示图如下,

在这里插入图片描述

内容概要:本文档《ccnp_300-430.pdf》涵盖了与Cisco无线网络配置相关的多个选择题及其答案解析。文档详细探讨了FlexConnect AP在不同模式下的行为、AP模式和子模式的选择、客户端特征配置、图像传输优化、Cisco OEAP配置、QoS设置、多播配置、安全措施(如入侵保护、恶意AP检测)、位置服务配置以及BYOD策略实施等内容。文档不仅提供了具体的配置命令和选项,还解释了每种配置背后的逻辑和技术原理。 适合人群:具备一定网络基础知识,特别是对Cisco无线网络设备有一定了解的技术人员,包括但不限于网络管理员、无线网络工程师和CCNP认证考生。 使用场景及目标: ① 为无线网络工程师提供实际操作指导,确保在不同场景下正确配置Cisco无线设备; ② 帮助CCNP认证考生复习并掌握相关知识点; ③ 协助IT管理员解决日常无线网络管理中的常见问题,如连接不稳定、性能不佳或安全性问题; ④ 支持企业IT部门制定和实施BYOD策略,确保员工个人设备接入公司网络的安全性和效率。 阅读建议:由于文档内容较为专业且技术性强,建议读者首先熟悉Cisco无线网络的基本概念和术语。在阅读过程中,应结合具体的工作环境和需求进行理解,并尝试将所学知识应用到实际工作中。对于不熟悉的术语或配置命令,可以通过查阅官方文档或在线资源进一步学习。此外,通过模拟环境练习配置也是巩固知识的有效方法。
内容概要:本文详细介绍了如何利用C语言在Simulink环境中构建逆变器的重复控制系统,旨在将逆变器的总谐波畸变率(THD)降低至0.47%。文中首先展示了核心的C语言结构体和函数,如RepetitiveController结构体用于封装延迟存储器、零相位滤波器和低通滤波器,repetitive_control函数则实现了核心算法。接着,文章解释了离散化处理的方法,包括主电路和控制部分的不同步长运行机制,以及多速率仿真的应用。此外,还讨论了陷波器的具体实现及其参数调整,强调了双线性变换在滤波器设计中的重要性。最后,文章提到了代码的高效移植性,指出通过这种方式可以在仿真阶段就为后续的实际硬件部署做好准备,大大减少了调试时间和复杂度。 适合人群:从事电力电子领域的工程师和技术人员,尤其是对逆变器控制和信号处理有一定了解的人群。 使用场景及目标:适用于需要精确控制逆变器输出质量的应用场合,如光伏逆变器、UPS电源等。主要目标是通过高效的算法设计和优化,确保逆变器输出的THD达到极低水平,同时提高代码的可移植性和易维护性。 其他说明:本文不仅提供了详细的理论背景和技术细节,还分享了许多实践经验,如环形缓冲区的使用、陷波器的参数选择等,对于理解和实施逆变器控制具有很高的参考价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YMWM_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值