地理坐标系与UTM坐标系转换并进行gazebo测试

news2024/9/20 16:15:39

地理坐标系与UTM坐标系转换并进行gazebo测试

  • 经纬度到UTM坐标的转换
    • gazebo测试环境

在这里插入图片描述
全球卫星导航系统(Global Navigation Satelite System,GNSS),简称卫星导航,是室外机器人定位的一个主要信息来源。

卫星导航能给机器人提供什么信息?
正常工作时,实际上可以提供机器人所需的所有定位信息,包括:

  • 位置
  • 姿态
  • 速度等物理量

但是仅依靠卫星导航还不足以让机器人在室外完成自主导航任务,主要原因有一下几点:

  • GNSS提供的定位精度不能满足要求,GNSS分多个细分种类,有些GNSS定位方法可以提供很高的精度,但要求物体必须静止一段时间(通常十分钟以上);也有的方法可以提供较好的动态物体定位,但需要事先架设一个或多个基站。
  • GNSS的定位频率不能满足要求,一般在5-10hz
  • GNSS定位可用性存在问题,不能够全天候、全场地使用,稳定性与场景、结构、物体的遮挡关系,甚至和天气有关。

GNSS定位原理:
GNSS通过测量自身与地球周围各卫星的距离来确定自身位置,与卫星的距离主要通过测量时间间隔来确定。一个卫星信号从卫星上发出时,带有一个发送时间,而GNSS接收机接收到它时,有一个接收时间,通过比较时间间隔,就能估算各卫星离我们的距离。GNSS本质上可以看成一种高精度的授时系统。

在这里插入图片描述

经纬度到UTM坐标的转换

一般的单点GNSS或者RTK都会输出接收器测量到的经纬度。

下面通过程序将测量到的经纬度转换为米制的UTM坐标。

有的RTK带有双天线,在测量经纬度的同时,还可以输出方向角,如果不考虑机器人的俯仰和横滚,将它们视为零。 即可以将双天线RTK输出的四自由度坐标,在假设机器人的俯仰和横滚为零的前提下,可以把RTK输出视为六自由度的位置变换,即SE(3)的位姿。

经纬度转UTM的算法比较复杂和琐碎,使用一个开源的转换方法来实现这部分内容。

gazebo测试环境

在飞机上添加一个GPS模块

  <!-- Mount an  Default GPS. -->
  <xacro:default_gps namespace="${namespace}" parent_link="${namespace}/base_link" />

默认的GPS 用的 rotors_simulator的 gps 插件 ,可以设置水平、垂直位置精度,水平垂直速度精度

  <xacro:macro name="default_gps" params="namespace parent_link">
    <!-- Default GPS. -->
    <xacro:gps_plugin_macro
      namespace="${namespace}"
      gps_suffix=""
      parent_link="${parent_link}"
      gps_topic="gps"
      ground_speed_topic="ground_speed"
      mass_gps_sensor="0.015"
      horizontal_pos_std_dev="3.0"
      vertical_pos_std_dev="6.0"
      horizontal_vel_std_dev="0.1"
      vertical_vel_std_dev="0.1">
      <inertia ixx="0.00001" ixy="0.0" ixz="0.0" iyy="0.00001" iyz="0.0" izz="0.00001" />
      <origin xyz="0 0 0" rpy="0 0 0" />
    </xacro:gps_plugin_macro>
  </xacro:macro>

设置好后,启动gazebo

打印gps发出的数据如下

header:
seq: 285
stamp:
secs: 57
nsecs: 200000000
frame_id: “firefly/gps_link”
status:
status: 0
service: 1
latitude: 47.366698580832654
longitude: 8.550017892718087
altitude: 499.89846703596413
position_covariance: [9.0, 0.0, 0.0, 0.0, 9.0, 0.0, 0.0, 0.0, 36.0]
position_covariance_type: 3
在这里插入图片描述
经纬度转UTM坐标

bool LatLon2UTM(const Vec2d& latlon, UTMCoordinate& utm_coor) {
    long zone = 0;
    char char_north = 0;
    long ret = Convert_Geodetic_To_UTM(latlon[0] * math::kDEG2RAD, latlon[1] * math::kDEG2RAD, &zone, &char_north,
                                       &utm_coor.xy_[0], &utm_coor.xy_[1]);
    utm_coor.zone_ = (int)zone;
    utm_coor.north_ = char_north == 'N';

    return ret == 0;
}

输入 向量 Vec2d 就是 Eigen::Vector2d 的经纬度值
输出 UTMCoordinate格式的 utm数据,UTMCoordinate格式定义如下:

/// UTM 坐标
struct UTMCoordinate {
    UTMCoordinate() = default;
    explicit UTMCoordinate(int zone, const Vec2d& xy = Vec2d::Zero(), bool north = true)
        : zone_(zone), xy_(xy), north_(north) {}

    int zone_ = 0;              // utm 区域
    Vec2d xy_ = Vec2d::Zero();  // utm xy
    double z_ = 0;              // z 高度(直接来自于gps)
    bool north_ = true;         // 是否在北半球
};

其中调用了 Convert_Geodetic_To_UTM 函数是调用的 utm_convert库中的函数
参数解释如下

     *    Latitude          : Latitude in radians                 (input)
     *    Longitude         : Longitude in radians                (input)
     *    Zone              : UTM zone                            (output)
     *    Hemisphere        : North or South hemisphere           (output)
     *    Easting           : Easting (X) in meters               (output)
     *    Northing          : Northing (Y) in meters              (output)

输出结果

        std::cout<< "utm zone:  " <<utm_coor.zone_<<std::endl;
        std::cout<< "utm x:  " <<utm_coor.xy_[0]<<std::endl;
        std::cout<< "utm y:  " <<utm_coor.xy_[1]<<std::endl;

其中的一帧转换值

utm zone: 32
utm x: 466024
utm y: 5.24601e+06

也就是说
latitude: 47.366698580832654
longitude: 8.550017892718087
转到UTM坐标系约为
zone: 32
x: 466024
y: 5.24601e+06

可以看到x 和 y的数值很大,不好进行判断,一般把获取第一帧数据位置标为零点,也就是后面的数据都减去起始位置

代码如下:

        sensor_msgs::NavSatFix Gps = *Gps_msg;
        Vec2d gps_latlon;
        UTMCoordinate utm_coor;
        static bool first_gnss_set = false;
        static Vec2d origin = Vec2d::Zero();
        
        gps_latlon[0] = Gps.latitude;
        gps_latlon[1] = Gps.longitude;
        LatLon2UTM(gps_latlon,utm_coor);

        if (!first_gnss_set) {
            origin << utm_coor.xy_[0],utm_coor.xy_[1];
            first_gnss_set = true;
        }
        utm_coor.xy_ = utm_coor.xy_ - origin;

上面是机器人上最简单的获得卫星导航位置的情况,该情况上默认导航的GPS或RTK安装在机器人的正中心位置。

下面介绍复杂一些的情况:GPS的位置与机器人中心有偏差。
在这里插入图片描述

还有另一种情况,RTK可以输出双天线的角度,双天线也不与机器人前向一致。
在这里插入图片描述
上面的图中,红色坐标代表机器人坐标系,蓝色代表GNSS坐标系。

双天线RTK输出的UTM坐标和方向角的读数,可视为 T W G T_{WG} TWG,其中W代表世界坐标系,G代表GNSS坐标系。

机器人需要的导航定位数据为 T W B T_{WB} TWB,其中B为机器人坐标系。

为了将RTK输出的 T W G T_{WG} TWG转为机器人需要的导航定位数据为 T W B T_{WB} TWB,则需进行GNSS接收器与机器人中心的外参,来组成 T B G T_{BG} TBG

在参数标定中,可以规定如下:
指定安装偏移量 t t t,为 O B O_{B} OB指向 O G O_{G} OG的矢量,在B系中取坐标,这就是 T B G T_{BG} TBG的平移分量。
指定安装偏角 θ \theta θ,为B系的x轴转向G系x轴之间的转角,这就是 T B G T_{BG} TBG的旋转分量。
得到转换矩阵 T B G T_{BG} TBG为:
T B G = [ R Z ( θ ) t 0 1 ] T_{BG}=\begin{bmatrix} R_{Z}(\theta ) & t\\ 0 &1 \end{bmatrix} TBG=[RZ(θ)0t1]

机器人坐标系到世界坐标系的变化矩阵 T W B T_{WB} TWB,最终由RTK的读数和标定的外参计算可得为:
T W B = T W G T G B T_{WB}=T_{WG}T_{GB} TWB=TWGTGB
分开写为:
R W B = R W G R G B , t W B = R W G t G B + t W G R_{WB}=R_{WG}R_{GB} , t_{WB}=R_{WG}t_{GB}+t_{WG} RWB=RWGRGB,tWB=RWGtGB+tWG

代码如下:
将方法构造成一个函数
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

bool ConvertGps2UTM(GNSS& gps_msg, const Vec2d& antenna_pos, const double& antenna_angle, const Vec3d& map_origin) 

其中 第一个参数 GNSS 其结构体定义如下:

/// 一个GNSS读数结构
struct GNSS {
    GNSS() = default;
    GNSS(double unix_time, int status, const Vec3d& lat_lon_alt, double heading, bool heading_valid)
        : unix_time_(unix_time), lat_lon_alt_(lat_lon_alt), heading_(heading), heading_valid_(heading_valid) {
        status_ = GpsStatusType(status);
    }

    double unix_time_ = 0;                                  // unix系统时间
    GpsStatusType status_ = GpsStatusType::GNSS_NOT_EXIST;  // GNSS 状态位
    Vec3d lat_lon_alt_ = Vec3d::Zero();                     // 经度、纬度、高度,前二者单位为度
    double heading_ = 0.0;                                  // 双天线读到的方位角,单位为度
    bool heading_valid_ = false;                            // 方位角是否有效

    UTMCoordinate utm_;       // UTM 坐标(区域之类的也在内)
    bool utm_valid_ = false;  // UTM 坐标是否已经计算(若经纬度给出错误数值,此处也为false)

    SE3 utm_pose_;  // 用于后处理的6DoF Pose
};

第二个参数是安装偏移量,第三个参数是安装偏移角度,第四个参数是地图原点。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
经纬高转换为UTM

    UTMCoordinate utm_rtk;
    if (!LatLon2UTM(gps_msg.lat_lon_alt_.head<2>(), utm_rtk)) {
        return false;
    }
    utm_rtk.z_ = gps_msg.lat_lon_alt_[2];

用之前介绍的的LatLon2UTM函数
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
如果 卫星导航方案是 双天线RTK则数据中带有方位角信息 ,根据方位角信息是否效,来设置rtk方位角
有的rtk的方位角是北东地坐标系,如果转成东北天坐标系则需要进行一个转换
一个北东地坐标系的方位角h,转换到东北天坐标系下的角度 h ’ h^{’} h的转换公式为:
h ’ = π / 2 − h h^{’} =\pi /2-h h=π/2h

    /// RTK heading 转成弧度
    double heading = 0;
    if (gps_msg.heading_valid_) {
        heading = (90 - gps_msg.heading_) * math::kDEG2RAD;  // 北东地转到东北天
    }

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
构建卫星导航坐标系到机器人坐标系的转换矩阵,根据外参的测量结果

此时存在两种情况,第一种双天线RTK,存在安装偏移量与安装偏移角度。

    SE3 TBG(SO3::rotZ(antenna_angle * math::kDEG2RAD), Vec3d(antenna_pos[0], antenna_pos[1], 0));
    SE3 TGB = TBG.inverse();

其中 antenna_angle 就是安装偏移角度,是机器人坐标系x轴转向卫星导航坐标系x轴之间的转角
antenna_pos 就是安装偏移量,是机器人坐标系原点指向卫星导航坐标系原点的矢量,在机器人坐标系中取坐标

然后就可以求得 T W B T_{WB} TWB

        // 若指明地图原点,则减去地图原点
        double x = utm_rtk.xy_[0] - map_origin[0];
        double y = utm_rtk.xy_[1] - map_origin[1];
        double z = utm_rtk.z_ - map_origin[2];
        SE3 TWG(SO3::rotZ(heading), Vec3d(x, y, z));
        TWB = TWG * TGB;

另一种情况,单点GPS,存在安装偏移量,但是单点GPS不输出方位角,没有卫星导航坐标系,仅有机器人坐标系到GPS中心的角度
需要通过状态估计算法得到的机器人姿态的方位角直接构成 R W B R_{WB} RWB,此时还需要设置一个 R B G R_{BG} RBG,因为没有偏移角度,所以说叫设置一个,比如就认为B系和G系的方向一致,那么 R B G = I R_{BG} = I RBG=I,然后再得到 R W G R_{WG} RWG

        // 单点gps方案情况
        float uav_yaw;
        SO3 RWB = SO3::rotZ(uav_yaw * math::kDEG2RAD);
        SO3 RBG = SO3::rotZ(0 * math::kDEG2RAD);
        SO3 RWG = RWB*RBG;
        // 若指明地图原点,则减去地图原点
        double x = utm_rtk.xy_[0] - map_origin[0];
        double y = utm_rtk.xy_[1] - map_origin[1];
        double z = utm_rtk.z_ - map_origin[2];
        SE3 TWG(RWG, Vec3d(x, y, z));
        SE3 TBG(RBG,Vec3d(antenna_pos[0], antenna_pos[1], 0));
        SE3 TGB = TBG.inverse();
        TWB = TWG * TGB;

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
然后则对gps结构体中utm的坐标进行赋值即可

    gps_msg.utm_valid_ = true;
    gps_msg.utm_.xy_[0] = TWB.translation().x();
    gps_msg.utm_.xy_[1] = TWB.translation().y();
    gps_msg.utm_.z_ = TWB.translation().z();
    
    if (gps_msg.heading_valid_) {
        // 组装为带旋转的位姿
        gps_msg.utm_pose_ = TWB;
    } else {
        // 组装为仅有平移的SE3
        // 注意当安装偏移存在时,并不能实际推出车辆位姿
        gps_msg.utm_pose_ = SE3(SO3(), TWB.translation());
    }

在gazebo中,设置gps在中心位置,那么则可以设置偏移量和偏移角度为0,然后设置起点位置为原点。

添加路径显示

        geometry_msgs::PoseStamped pose_stamped;

        pose_stamped.header.stamp = ros::Time::now();
        pose_stamped.header.frame_id = "/local_frame";
        pose_stamped.pose.position.x = gps_msg.utm_.xy_[0];
        pose_stamped.pose.position.y = gps_msg.utm_.xy_[1];
        pose_stamped.pose.position.z = gps_msg.utm_.z_;

        pose_stamped.pose.orientation.x = 0;
        pose_stamped.pose.orientation.y = 0;
        pose_stamped.pose.orientation.z = 0;
        pose_stamped.pose.orientation.w = 1;

        UavGpsPath_.poses.push_back(pose_stamped);

        UavGpsPath_.header.stamp = ros::Time::now();
        UavGpsPath_.header.frame_id = "/local_frame";

        GPS_Path_pub_.publish(UavGpsPath_);

模拟场gps定位精度好,rtk的水平定位精度可以到达2cm,垂直6cm
设置gps的定位噪声如下:

      horizontal_pos_std_dev="0.02"
      vertical_pos_std_dev="0.06"

在这里插入图片描述
随便飞了一个轨迹
在这里插入图片描述
前面场景下的gps定位效果好的情况下,如果gps定位的精度不高,则会出现下面的情况

      horizontal_pos_std_dev="0.2"
      vertical_pos_std_dev="0.4"

在这里插入图片描述

在这里插入图片描述
由于GPS输出的频率低,并且在信号不好的情况下,会出现较大的噪声,所以并不能直接用于机器人导航,一般会和IMU进行融合,实现组合导航。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1537382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Matplotlib 画图中文无法正常显示的问题(显示方框)

解决Matplotlib 画图中文无法正常显示的问题&#xff08;显示方框&#xff09; 错误描述解决方案一&#xff08;暂时解决&#xff09;解决方法二&#xff08;永久解决&#xff09;测试代码 错误描述 这个错误消息来自于使用 Python 的 IPython 环境&#xff0c;特别是在尝试输出…

基于 Google MediaPipe 进行人体姿势估计演示

用于人体姿势估计的 MediaPipe 演示 MediaPipe简介 MediaPipe是一个开源框架&#xff0c;用于构建跨平台、多模式应用机器学习管道。它由 Google 开发&#xff0c;旨在促进基于机器学习的功能的快速开发和部署&#xff0c;特别关注音频、视频和时间序列数据。 我可以将 MediaPi…

高标准农业四情监测系统的应用范围

高标准农业四情监测系统的应用范围【TH-Q1】随着科技的不断进步&#xff0c;高标准农业四情监测系统已经逐渐成为现代农业的重要组成部分。这一系统能够实时监测土壤、气候、作物生长和病虫害情况&#xff0c;为农业生产提供精准的数据支持&#xff0c;从而提高农作物的产量和质…

小红书离线数仓提效新思路,提升百倍回刷性能

数据处理效率一直是大数据时代的核心话题&#xff0c;它推动着各类数据执行引擎持续迭代产品。从早期的 MapReduce&#xff0c;到今天的 Spark&#xff0c;各行业正不断演进其离线数仓技术架构。 现有以 Spark 为核心的数仓架构在处理大规模数据回刷方面已取得进展&#xff0c;…

第十节HarmonyOS 常用容器组件2-Counter

1、描述 计数器组件&#xff0c;提供相应的增加或者减少的计数操作。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 2、子组件 可以包含子组件。 3、接口 Counter() 从API version 9开始…

项目实战-开发工具入门/基本框架搭建/项目初始化/引入组件库

上周更新完了之前vue3的shopping项目&#xff0c;接下来&#xff0c;将会开启一个新的项目&#xff0c;效果是类似于移动端的一个伙伴匹配项目&#xff0c;今天这篇文章从需求分析到架构设计再到项目初始化&#xff0c;基本框架搭建几个部分来为大家详细介绍。 从这个项目开始…

java面试:常见的限流算法有哪些

1 什么是限流算法 限流算法是一种用于限制流量请求的频率或速率的算法&#xff0c;其目的是在高并发或大流量请求的情况下&#xff0c;保护系统服务的安全性和可用性。限流算法可以应对热点业务带来的突发请求、调用方bug导致的突发请求以及恶意攻击请求等情况。是一种系统保护…

linux之sed编辑器指令练习

目录 一、sed编辑器 二、sed使用案例 1.1 s命令&#xff08;substitute替换&#xff09; 一、sed编辑器 sed编辑器比交互式编辑器快的多&#xff0c;可以简化数据处理任务,sed编辑器并不会修改文件&#xff0c;只会将修改后的数据&#xff0c;输出。 二、sed使用案例 首先…

【国家计算机二级C语言】高分笔记

二叉树 参考 http://t.csdnimg.cn/ozVwT 数据库 SQL程序语言有四种类型&#xff0c;对数据库的基本操作都属于这四类&#xff0c;它们分别为&#xff1b;数据定义语言(DDL)、数据查询语言&#xff08;DQL&#xff09;、数据操纵语言&#xff08;DML&#xff09;、数据控制语言…

【数据结构与算法】(18):树形选择排序:按照锦标赛的思想进行排序

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

yaml 语法和在线解析工具

文章目录 在线解析工具1. 简介2. 语法规则3. 数据类型3.1 数组&#xff1a;3.2对象&#xff1a;3.3 标量3.4 复合结构3.5 锚点3.5.1 单个锚点3.5.6 多个锚点 3.6 引号 参考 在线解析工具 工具1 工具2 1. 简介 Yaml是一种可读性高的数据标记语言&#xff0c;Yaml文件是一种配…

6 修改主机名和HOSTS文件

后期我们会配置多台服务器&#xff0c;那么每台服务器我们都会给定一个主机名&#xff0c;方便后期通过主机名进行访问。主机名的修改我们可以在安装操作系统时对其修改&#xff0c;如果忘记了&#xff0c;就可以修改配置文件完成&#xff0c;像后期我们进行虚拟机克隆后&#…

Docker常用命令练习

文章目录 Docker常用命令练习1.docker 基础命令2.镜像命令3.保存镜像4.加载镜像5.容器命令6.环境变量7. --rm8. --networkhost Docker常用命令练习 1.docker 基础命令 安装docker yum install docker启动docker systemctl start docker关闭docker systemctl stop docker重…

LeetCode-热题100:17.电话号码的字母组合

题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a; digits “23” 输出&a…

2024年【P气瓶充装】复审模拟考试及P气瓶充装操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 P气瓶充装复审模拟考试根据新P气瓶充装考试大纲要求&#xff0c;安全生产模拟考试一点通将P气瓶充装模拟考试试题进行汇编&#xff0c;组成一套P气瓶充装全真模拟考试试题&#xff0c;学员可通过P气瓶充装操作证考试全…

【阿里云物联网】上报设备数据

前言 MQTT客户端上传数据到阿里云服务端&#xff0c;并且能将数据显示出来。在此之前&#xff0c;我们先要懂得阿里云给设备管理划分的概念。首先是产品&#xff0c;所以在产品里要配置内容&#xff0c;产品下的设备才可以使用&#xff0c;比如主题大类都是在产品里面就可以查…

使用 Amazon SageMaker 微调 Llama 2 模型

本篇文章主要介绍如何使用 Amazon SageMaker 进行 Llama 2 模型微调的示例。 这个示例主要包括: Llama 2 总体介绍Llama 2 微调介绍Llama 2 环境设置Llama 2 微调训练 前言 随着生成式 AI 的热度逐渐升高&#xff0c;国内外各种基座大语言竞相出炉&#xff0c;在其基础上衍生出…

I2C芯片24C02/4/8/16(EEPROM)解读

一.原理图 24C01的硬件连接图如下&#xff1a; 二.24C0x系列芯片规格 三.24C0x芯片结构 下面简述EEPROM内部存储结构。 3.1 内部存储结构 根据24C02芯片的Datasheet描述&#xff0c;其内部存储结构应该如下图所示。 其它容量的EEPROM内部结构依此类推。 3.2 地址 3.2.1 器件…

BitMap介绍与应用

文章目录 BitMapBitMap介绍BitMap 结构RoaringBitmap 常见BitMapJava中的BitSetRedis中的BitMapClickHouse中的BitMap BitMap应用案例人群圈选 BitMap 场景一&#xff1a;(大部分开发面试都会遇到的一个问题&#xff09; 有10亿个用户id (int类型)&#xff0c;判断用户是否登…

Vue el-table 合并单元格

一般常见的就是下图这种的单列&#xff0c;上下重复进行合并。 有时候可能也会需要多行多列的合并。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&qu…