反光板导航SLAM(四)如何通过两个反光柱估计位姿

news/2024/7/24 13:29:59 标签: 人工智能, 机器人, 机器学习

通常而言确定空间中的一个点的坐标需要三个点,也就是俗称的三点定位。但是在某些时候,场景内可能不一定能够采样到足够多的反光柱,比如只有两个。这个时候看起来似乎参数是不足的,毕竟机器人的参数有三个,两个方程是不够的,但是这个方程其实也不是没有解的方式,具体的解答方式继续往下看:

1、三点定位

对于要确认空间中机器人的位置,首先考虑一个情景:空间中已知有一个障碍物O1,现在根据激光数据测得机器人到A的距离为R1,则机器人可以处于以R1为半径的圆上的任意位置:
在这里插入图片描述
然后机器人再次看到了另外一个障碍物O2,且根据激光数据测得机器人到A的距离为R2,则机器人可以处于以R2为半径的圆上的任意位置,此时机器人的位置就有了两个约束,因此它不能出现在这两个圆上的任意位置了,为了满足条件:机器人位于圆1上又同时位于圆2上,所以它就必须得在两个圆的交点上,也就是图中的A点或者B点:
在这里插入图片描述
此时机器人的位置大致已经确定了,但是会存在双解的问题。但是如果现在机器人又看到了第三个障碍物,那么此时机器人的坐标就可以确定了:
在这里插入图片描述
以上,就是简单的原理介绍。

那么根据上述理论来说,是不是说明整个定位过程中机器人的匹配点要一直保持着三个才可以保证定位呢,其实也不完全是的。其实两个点也是有可能确认出机器人的位姿的。

2、两点定位

前面我们提到了定位空间中的一个坐标需要三个点,但是同样注意到在只有两个观测目标的时候,机器人的位置是只有两种可能性的,这就为机器人确定位置提供了可能性。在已知匹配上两个点的反光柱的时候,机器人可能会存在于地图中的两个位置,但是这两个位置一般间隔还是比较远的,那么根据先验知识我们可以较为准确的判断出哪个点才是机器人实际存在的位置。具体思路如下:

2.1 匹配

匹配作为前置条件,假设我们当前获取到的反光柱点与实际地图上的反光柱点存在有且仅有两个点之间的匹配
[ x 1 , y 1 , x 2 , y 2 ] [ x w 1 , y w 1 , x w 2 , y w 2 ] [x1,y1,x2,y2] \\ [xw1,yw1 ,xw2,yw2] [x1,y1,x2,y2][xw1,yw1,xw2,yw2]

2.2 求角度

求角度的思路如下:
1、首先计算出两个匹配点之间的夹角theta,则该角度是为当前机器人的角度增量,但是它可能是正值也可能是负值,没有关系,先按正值考虑
2、将刚才的角度值回代到机器人,更新机器人的当前角度,重新计算当前帧下两个匹配点的坐标,因为机器人的角度变化了之前的值也就随之改变了。
3、重新计算两组匹配点之间的角度差,因为在第一步中我们计算出了当前机器人的一个角度增量,但是实际上它可能为正值也可能为负值,我们先按正值回代了,如果确实是正值那么目前机器人上两个点的连线方向与与之匹配的两个点的连线的夹角应该是趋于零的。如果变大了则说明刚才的赋值错误了,角度值应为负数。
4、更新角度,在第三步中确认角度增量的方向,然后更新最终的机器人朝向

2.3 求坐标

计算坐标:在上一步中我们知道了机器人的朝向,那么此时只剩下两个未知数,也就是机器人的坐标X、Y,则可以根据投影公式:

A W . x = c o s ( t h e t a R ) ∗ A R . x − s i n ( t h e t a R ) ∗ A R . y + X R A W . y = s i n ( t h e t a R ) ∗ A R . x + c o s ( t h e t a R ) ∗ A R . y + Y R AW.x = cos(thetaR)*AR.x - sin(thetaR)*AR.y + XR\\ AW.y = sin(thetaR)*AR.x +cos(thetaR)*AR.y + YR AW.x=cos(thetaR)AR.xsin(thetaR)AR.y+XRAW.y=sin(thetaR)AR.x+cos(thetaR)AR.y+YR
来确定机器人的坐标(XR,YR)。

简单代码示例如下:

void Reflector::MatchToAngle(vector<pair<pair<double, double>, pair<double, double> > > &match_result,reflector_slam::RobotPose &robot_pose)
{
  double k1,k2,k3;
  double deta_angle;
  geometry_msgs::PointStamped point_robot1,point_robot2;
  pair<double, double> point1,point2;
  point1.first = match_result[0].first.first;
  point1.second = match_result[0].first.second;
  point2.first = match_result[1].first.first;
  point2.second = match_result[1].first.second;
  geometry_msgs::PointStamped point_world1,point_world2;
  reflector_slam::RobotPose old_pose;
  old_pose = last_pose;
  //获取两个点在世界坐标系下的坐标
  point_world1.point.x = point1.first*cos(old_pose.theta)-sin(old_pose.theta)*point1.second+old_pose.x;
  point_world1.point.y = point1.first*sin(old_pose.theta)+cos(old_pose.theta)*point1.second+old_pose.y;
  point_world2.point.x = point2.first*cos(old_pose.theta)-sin(old_pose.theta)*point2.second+old_pose.x;
  point_world2.point.y = point2.first*sin(old_pose.theta)+cos(old_pose.theta)*point2.second+old_pose.y;
  //cout<<"point_world1.x:"<<point_world1.point.x<<"point_world1.y:"<<point_world1.point.y<<endl;
  //cout<<"point_world2.x:"<<point_world2.point.x<<"point_world2.y:"<<point_world2.point.y<<endl;
  //cout<<"word_point1.x:"<<match_result[0].second.first<<"word_point1.y:"<<match_result[0].second.second<<endl;
  //cout<<"word_point2.x:"<<match_result[1].second.first<<"word_point2.y:"<<match_result[1].second.second<<endl;
  if((point_world1.point.x - point_world2.point.x)==0 || (match_result[0].second.first - match_result[1].second.first)==0)
  {
    ROS_INFO("can not calculate k1 or k2");
    return;
  }
  k1 = (point_world1.point.y - point_world2.point.y)/(point_world1.point.x - point_world2.point.x);
  k2 = (match_result[0].second.second - match_result[1].second.second)/(match_result[0].second.first - match_result[1].second.first);
  //cout<<"k1:"<<k1<<" k2:"<<k2<<endl;
  deta_angle = atan(abs(k1-k2)/(1+k1*k2));
  //cout<<"deta_angle"<<deta_angle<<endl;
  geometry_msgs::PointStamped point_world_new1,point_world_new2;
  point_world_new1.point.x = point1.first*cos(old_pose.theta+deta_angle)-sin(old_pose.theta+deta_angle)*point1.second+old_pose.x;
  point_world_new1.point.y = point1.first*sin(old_pose.theta+deta_angle)+cos(old_pose.theta+deta_angle)*point1.second+old_pose.y;
  point_world_new2.point.x = point2.first*cos(old_pose.theta+deta_angle)-sin(old_pose.theta+deta_angle)*point2.second+old_pose.x;
  point_world_new2.point.y = point2.first*sin(old_pose.theta+deta_angle)+cos(old_pose.theta+deta_angle)*point2.second+old_pose.y;
  //cout<<"point_world_new1.x:"<<point_world_new1.point.x<<"point_world_new1.y:"<<point_world_new1.point.y<<endl;
  //cout<<"point_world_new2.x:"<<point_world_new2.point.x<<"point_world_new2.y:"<<point_world_new2.point.y<<endl;
  if((point_world_new1.point.x - point_world_new2.point.x) == 0)
  {
    ROS_INFO("can not calculate k3");
    return;
  }
  k3 = (point_world_new1.point.y - point_world_new2.point.y)/(point_world_new1.point.x - point_world_new2.point.x);
  //cout<<"k3:"<<k3<<endl;
  //cout<<"old_theta:"<<old_pose.theta<<endl;
  //斜率调整到基本一致,则应满足k2-k3趋向于零
  if(abs(k2-k1)>abs(k2-k3))
  {
    old_pose.theta += deta_angle;
  }
  else
    old_pose.theta -= deta_angle;
  //cout<<"new_theta:"<<old_pose.theta<<endl;
  //cout<<"old_pose.x:"<<old_pose.x<<endl;
  //cout<<"old_pose.y:"<<old_pose.y<<endl;
  old_pose.x = match_result[0].second.first - cos(old_pose.theta)*match_result[0].first.first + sin(old_pose.theta)*match_result[0].first.second;
  old_pose.y = match_result[0].second.second - sin(old_pose.theta)*match_result[0].first.first - cos(old_pose.theta)*match_result[0].first.second;
  //cout<<"new_pose.x:"<<old_pose.x<<endl;
  //cout<<"new_pose.y:"<<old_pose.y<<endl;
  robot_pose = old_pose;
}

http://www.niftyadmin.cn/n/182300.html

相关文章

个人练习-Leetcode-89. Gray Code

题目链接&#xff1a;https://leetcode.cn/problems/gray-code/submissions/ 题目大意&#xff1a;给出位数n&#xff0c;求一个合法的n位格雷码排列。 思路&#xff1a;低位数的格雷码是容易得出的&#xff0c;考虑从n-1位的格雷码得到n位格雷码。设现有Gn−1G_{n-1}Gn−1​…

关于前端工具layui的一些使用总结

说明&#xff1a; 最近小组在做一个自研的字节码增强工具。因为是预研性质&#xff0c;所以前端的操作界面就自己上手写了。使用了比较亲和后端开发的layui框架。后端采用springbootthymeleaf 后端配置静态资源和thymeleaf&#xff1a; application.yml&#xff1a; 静态资源…

并发编程(九)-ScheduledExecutorService源码分析

一、ScheduledExceutorService简介 ScheduledExecutorService 是 Java 并发包中提供的一个接口&#xff0c;继承ExecutorService接口&#xff0c;是Executor框架的一个扩展。它可以用于调度任务在指定的时间或周期性地执行。相比于 Timer 和 TimerTask&#xff0c;ScheduledExe…

【LeetCode刷题-Python】有序数组的平方

977.有序数组的平方 977.有序数组的平方 题目 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平…

TryHackMe-Madeye‘s Castle(boot2root)

Madeye’s Castle 一个boot2root盒子&#xff0c;由Runcode.ninja团队在CuCTF中使用的盒子修改而来 祝你冲进麦德耶的城堡玩得开心&#xff01;在这个房间里&#xff0c;你需要完全枚举系统&#xff0c;站稳脚跟&#xff0c;然后转向几个不同的用户。 端口扫描 循例nmap SMB…

HTML5 Web 存储

HTML5 Web 存储 在HTML5之前&#xff0c;主要是使用cookies存储&#xff0c;cookies的缺点有&#xff1a;需要在请求头上带着数据&#xff0c;存储大小不过&#xff0c;在4k之内。本节&#xff0c; HTML5 web 存储&#xff0c;一个比cookie更好的本地存储方式。 什么是 HTML5 …

2023美赛春季赛F题思路数据代码论文分享

文章目录赛题思路赛题详情参赛建议&#xff08;个人见解&#xff09;选择队友及任务分配问题&#xff08;重要程度&#xff1a;5星&#xff09;2023美赛春季赛F题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片&#xff0c;加入获取…

环形链表 II(力扣142)(快慢指针)

142.环形链表—力扣 题目描述&#xff1a; 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给…