RTK_ROS_导航(1): GNSS里程计

news2024/12/28 5:39:59

目录

  • 1. RTK 配置
  • 2. ROS驱动
  • 3. RTK融合IMU实现里程计
  • 4. 纯RTK的定位信息
  • 5. 即将实现导航,正在更新中,如果遇到问题,欢迎CSDN讨论...

1. RTK 配置

  1. 4G CORS + 4G 网络 + 户外有信号,不能实现RTK,就恢复出厂设置
  2. 输出报文信息包含(一般需要三个同时打开):
    • GAPPA:包含位置信息
    • GPVTG:包含速度信息
    • GPHDT:包含定向的朝向信息

2. ROS驱动

  1. 驱动安装
    sudo apt install ros-noetic-nmea-navsat-driver
    
  2. 运行,需要修改该功能包的启动端口,会自动解析上面的3个RTK语句信息
    roslaunch nmea_navsat_driver nmea_serial_driver.launch
    
    若需要修改USB端口:
    sudo nano /opt/ros/noetic/share/nmea_navsat_driver/launch/nmea_serial_driver.launch
    
    然后运行以下节点,就可以得到ROS中关于报文的解析
    roslaunch nmea_navsat_driver nmea_serial_driver.launch
    

3. RTK融合IMU实现里程计

  1. 依赖
     sudo apt-get install ros-noetic-geographic-* geographiclib-* libgeographic-*
     sudo apt-get install ros-noetic-nmea-navsat-driver
     # 需要查看自己的cmake版本,并修改下面的指令目标文件夹
     sudo ln -s /usr/share/cmake/geographiclib/FindGeographicLib.cmake /usr/share/cmake-3**/Modules/ (* is the version of your cmake)
    
  2. ROS功能包
    mkdir -p GNSS_IMU_ESKF/src
    cd GNSS_IMU_ESKF/src
    git clone https://github.com/zouyajing/imu_gnss_eskf.git
    cd ..
    catkin_make
    
  3. 运行,确保话题信息包含/fix、/imu/data
    roslaunch imu_gnss_eskf imu_gnss_eskf.launch
    

4. 纯RTK的定位信息

  1. Python实现代码,需要注意解算的yaw与车头的关系
    #!/usr/bin/env python
    import rospy
    from sensor_msgs.msg import NavSatFix, Imu
    from geometry_msgs.msg import PointStamped, QuaternionStamped
    from nav_msgs.msg import Odometry
    import numpy as np
    from scipy.spatial.transform import Rotation
    from threading import Lock
    
    class GNSSIMUFusion:
        def __init__(self):
            rospy.init_node('gnss_imu_fusion', anonymous=True)
            
            # 订阅以及发布的话题
            self.gnss_sub = rospy.Subscriber('/fix', NavSatFix, self.gnss_callback)
            self.heading_sub = rospy.Subscriber("/heading", QuaternionStamped, self.gnss_heading_callback)
            self.odom_pub = rospy.Publisher('/gnss_odom', Odometry, queue_size=10)
            self.init_lla_pub = rospy.Publisher("/gnss_init_fix", PointStamped, queue_size=10)
            
            # 设置GNSS 相关
            self.frame_count = 0
            self.gnss_init_fix_msg = None
            self.initial_llat  = np.array([0, 0, 0], dtype=np.float32)
            self.initial_ecef_ = np.array([0, 0, 0], dtype=np.float32)
            
            # 互斥锁
            self.gnss_heading_lock = Lock()
            
            # 缓存区
            self.last_heading_msg = None
            self.last_gnss_msg = None
            
            # 缓冲区
            self.gnss_heading_msg_list = []
    
        def gnss_callback(self, data):
            # print("GNSS calback : ", self.key)
            assert type(data) is NavSatFix
            if data.status.status != 2:
                print("GNSS state:{}, Not Good ....".format(data.status))
                return
            average_num = 1
            
            if  self.frame_count < average_num:
                # x, y, z = self.lla_to_ecef(data.latitude, data.longitude, data.altitude)
    
                self.frame_count += 1
                
                # 传入的是ecef
                self.initial_llat  += np.array([data.latitude, data.longitude, data.altitude], dtype=np.float32)
                print("累积平均值中...")
                return None
            
            if self.frame_count == average_num :
    
                self.initial_llat   /= self.frame_count
                
                self.gnss_init_fix_msg = data
                self.gnss_init_fix_msg.header.stamp = rospy.Time.now()
                self.gnss_init_fix_msg.latitude, self.gnss_init_fix_msg.longitude, self.gnss_init_fix_msg.altitude = self.initial_llat
                
                self.initial_ecef_ = np.asarray(self.lla_to_ecef(self.initial_llat[0], self.initial_llat[1], self.initial_llat[2]), dtype=np.float32)
    
                self.frame_count += 1
                
                current = [self.lla_to_enu(data.latitude, data.longitude, data.altitude)]
                
                print("得到偏移量...: ",current)
                return None
            
            quat   = np.array([self.last_heading_msg.quaternion.x, self.last_heading_msg.quaternion.y, self.last_heading_msg.quaternion.z, self.last_heading_msg.quaternion.w], dtype=np.float32)
            yaw, roll, pitch = Rotation.from_quat(quat).as_euler("ZXY", degrees=False)
            quat = Rotation.from_euler("ZXY", [-yaw + np.pi, 0, 0], degrees=False).as_quat()
            quat_t = self.last_heading_msg.header.stamp.to_sec()
            
            
            current_GNSS_XYZ = np.array(self.lla_to_enu(data.latitude, data.longitude, data.altitude), dtype=np.float32)
            gnss_heading_quat = self.get_cur_gnss_observ_quat(data.header.stamp.to_sec())
            # 判断数据是否可用
            if len(gnss_heading_quat) == 4:
                x, y, z, w = gnss_heading_quat
                current_GNSS_ORI = np.array([x, y, z, w], dtype=np.float32)
    
            
            Vel = np.array([0, 0, 0], dtype=np.float32)
            
            self.pub_imu_odom(current_GNSS_XYZ, Vel, current_GNSS_ORI, cur_IMU__msg=data)
    
            
          
    
       
        def gnss_heading_callback(self, msg):
            assert type(msg) is QuaternionStamped
            self.gnss_heading_lock.acquire()
            
            # 确保队列长度100个消息以内,也就是10s内
            if len(self.gnss_heading_msg_list) > 100:
                self.gnss_heading_msg_list.pop(0)
            self.gnss_heading_msg_list.append(msg)
            
            self.gnss_heading_lock.release()
          
            self.last_heading_msg = msg
    
        # 通过gnss的时间获取最近的gnss观测导航角度
        def get_cur_gnss_observ_quat(self, cur_time):
            # 准备上锁
            self.gnss_heading_lock.acquire()
            
            # 没有数据就返回一个空数据
            if len(self.gnss_heading_msg_list) == 0:
                return np.array([], dtype=np.float32)
            
            # 最大时间偏差为0.5s
            time_bias = []
            for i, msg in enumerate(self.gnss_heading_msg_list) :
                assert type(msg) is QuaternionStamped
                time_bias.append( abs(msg.header.stamp.to_sec() - cur_time) )
                
            min_time_index = time_bias.index(min(time_bias))
            print(min(time_bias))
            min_time_msg   = self.gnss_heading_msg_list[min_time_index]
            for i in range(min_time_index):
                self.gnss_heading_msg_list.pop(0)
            
            # 解锁
            self.gnss_heading_lock.release()
            msg  = min_time_msg
            quat = np.array([msg.quaternion.x, msg.quaternion.y, msg.quaternion.z, msg.quaternion.w],dtype=np.float32)
            
            yaw, roll, pitch = Rotation.from_quat(quat).as_euler("ZXY", degrees=False)
            quat = Rotation.from_euler("ZXY", [-yaw + np.pi, 0, 0], degrees=False).as_quat()
            return quat
    
        def pub_imu_odom(self, P, V, Q, cur_IMU__msg):
            
            msg = cur_IMU__msg
            
            odom_msg = Odometry()
            odom_msg.header.stamp = rospy.Time.now()
            odom_msg.header.frame_id = "map"
            odom_msg.child_frame_id  = msg.header.frame_id
            
            odom_msg.pose.pose.position.x = P[0]
            odom_msg.pose.pose.position.y = P[1]
            odom_msg.pose.pose.position.z = P[2]
            
            odom_msg.pose.pose.orientation.x = Q[0]
            odom_msg.pose.pose.orientation.y = Q[1]
            odom_msg.pose.pose.orientation.z = Q[2]
            odom_msg.pose.pose.orientation.w = Q[3]
            
            odom_msg.twist.twist.linear.x = V[0]
            odom_msg.twist.twist.linear.y = V[1]
            odom_msg.twist.twist.linear.z = V[2]
            
            odom_msg.twist.twist.angular.x = 0
            odom_msg.twist.twist.angular.y = 0
            odom_msg.twist.twist.angular.z = 0
            self.odom_pub.publish(odom_msg)
    
        def lla_to_ecef(self, lat, lon, alt):
            
            equatorial_radius = 6378137.0
            polar_radius = 6356752.31424518
            square_ratio = np.power(polar_radius, 2) / np.power(equatorial_radius, 2)
            
            lat = lat * np.pi / 180
            lon = lon * np.pi / 180
            alt =  alt
        
            N = equatorial_radius / np.sqrt(1 - (1 - square_ratio) * np.power(np.sin(lat), 2))
            z = (square_ratio * N + alt) * np.sin(lat)
            q = (N + alt) * np.cos(lat)
            x = q * np.cos(lon)
            y = q * np.sin(lon)
            
            return x, y, z
        
        def lla_to_enu(self, lat, lon, alt):
            cur_ecef = self.lla_to_ecef(lat, lon, alt)
            r_ecef = cur_ecef - self.initial_ecef_
            
            phi = self.initial_llat[0] * np.pi / 180
            lam = self.initial_llat[1] * np.pi / 180
            
            R = np.asarray([    [-np.sin(lam),                  np.cos(lam),                0],
                                [-np.cos(lam) * np.sin(phi),   -np.sin(lam) * np.sin(phi),  np.cos(phi)],
                                [ np.cos(lam) * np.cos(phi),    np.sin(lam) * np.cos(phi),  np.sin(phi)]], dtype=np.float32)
    
            return R @ r_ecef
    
    
    if __name__ == '__main__':
        try:
            fusion_node = GNSSIMUFusion()
            rospy.spin()
            # fusion_node.run()
        except rospy.ROSInterruptException:
            pass
    
  2. 效果图
    在这里插入图片描述

5. 即将实现导航,正在更新中,如果遇到问题,欢迎CSDN讨论…

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

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

相关文章

kafka底层原理性能优化详解:大案例解析(第29天)

系列文章目录 一、Kafka简介 二、Kafka架构设计 三、消息传递机制 四、实例说明&#xff08;案例解析&#xff09; 五、kafka性能优化(案例解析) 文章目录 系列文章目录前言一、Kafka简介二、Kafka架构设计1. Producer&#xff08;生产者&#xff09;&#xff1a;2. Broker&am…

Check if a fine-tuned OpenAI model was successfully deleted

题意&#xff1a;检查微调后的OpenAI模型是否已成功删除 问题背景&#xff1a; I am doing some work with the OpenAI API with Python. Im working with fine-tuning and I am working on deleting an existing model and starting over again. I want to be able to check …

Hive 高可用分布式部署详细步骤

目录 系统版本说明 hive安装包下载及解压 上传mysql-connector-java的jar包 配置环境变量 进入conf配置文件中&#xff0c;将文件重命名 在hadoop集群上创建文件夹 创建本地目录 修改hive-site.xml文件 同步到其他的节点服务器 修改node02中的配置 hive-site.xml 修改…

上海-LM科技(面经)

上海-LM科技 hr电话面 个人简介 个人信息的询问 是否知道芋道框架 技术面 算法题 14. 最长公共前缀&#xff08;写出来即可&#xff09; 聊一下Docker Docker核心概念总结Docker实战 聊一下AOP Spring AOP详解 聊一下JWT JWT 基础概念详解JWT 身份认证优缺点分析 Spring…

【C++】类和对象(中)--下篇

个人主页~ 类和对象上 类和对象中-上篇 类和对象 五、赋值运算符重载1、运算符重载2、赋值运算符重载3、前置和后置重载 六、const成员七、日期类的实现Date.hDate.cpptest.cpptest1测试结果test2测试结果test3测试结果test4测试结果test5测试结果test6测试结果test7测试结果 八…

【Dison夏令营 Day 12】如何用 Python 构建数独游戏

通过本综合教程&#xff0c;学习如何使用 Pygame 在 Python 中创建自己的数独游戏。本指南涵盖安装、游戏逻辑、用户界面和计时器功能&#xff0c;是希望创建功能性和可扩展性数独益智游戏的爱好者的理想之选。 数独是一种经典的数字谜题&#xff0c;多年来一直吸引着谜题爱好…

【Elasticsearch】Elasticsearch倒排索引详解

文章目录 &#x1f4d1;引言一、倒排索引简介二、倒排索引的基本结构三、Elasticsearch中的倒排索引3.1 索引和文档3.2 创建倒排索引3.3 倒排索引的存储结构3.4 词典和倒排列表的优化 四、倒排索引的查询过程4.1 过程4.2 示例 五、倒排索引的优缺点5.1 优点5.2 缺点 六、倒排索…

阶段三:项目开发---搭建项目前后端系统基础架构:任务9:导入空管基础数据

任务描述 本阶段任务是导入项目的基础数据&#xff0c;包括空管基础数据和离线的实时飞行数据&#xff08;已经脱敏&#xff09;。 任务指导 本阶段任务需要导入两种数据&#xff1a; 1、在MySQL中导入空管基础数据 kongguan.sql空管基础数据表说明&#xff1a; 1告警信息…

什么牌子的头戴式蓝牙耳机好性价比高?

说起性价比高的头戴式蓝牙耳机,就不得不提倍思H1s,作为倍思最新推出的新款,在各项功能上都实现了不错的升级,二字开头的价格,配置却毫不含糊, 倍思H1s的音质表现堪称一流。它采用了40mm天然生物纤维振膜,这种振膜柔韧而有弹性,能够显著提升低音的量感。无论是深沉的低音还是清…

C语言 | Leetcode C语言题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; int maximalSquare(char** matrix, int matrixSize, int* matrixColSize){int dp[301][301]{0};int wid0;if(matrixSize0&&matrixColSize[0]0){return 0;}for(int i0;i<matrixSize;i){for(int j0;j<matrixColSize[0];j){if(m…

linux docker部署(离线.deb)非桌面版

目录 一.查看本机系统 二.下载deb包 三.安装 一.查看本机系统 uname -m二.下载deb包 下载官网&#xff1a;Index of linux/ubuntu/dists/focal/pool/stable/amd64/ (docker.com) 目录说明&#xff1a; 我是ubuntu20.04&#xff0c;选择focal&#xff1a; edge/&#xff…

R语言实战—圆形树状图

话不多说&#xff0c;先看最终效果&#xff1a; 圆形树状图是树状图的一个变型&#xff0c;其实都是层次聚类。 接下来看代码步骤&#xff1a; 首先要先安装两个包&#xff1a; install.packages("ggtree") install.packages("readxl") 咱就别问问什么…

群体优化算法----化学反应优化算法介绍,解决蛋白质-配体对接问题示例

介绍 化学反应优化算法&#xff08;Chemical Reaction Optimization, CRO&#xff09;是一种新兴的基于自然现象的元启发式算法&#xff0c;受化学反应过程中分子碰撞和反应机制的启发而设计。CRO算法模拟了分子在化学反应过程中通过能量转换和分子间相互作用来寻找稳定结构的…

C++ unique_ptr智能指针学习

unique_ptr是一种定义在<memory>中的智能指针(smart pointer)。它持有对对象的独有权——两个unique_ptr不能指向一个对象&#xff0c;不能进行复制操作只能进行移动操作。 如下图&#xff0c;定义p1为unique_ptr类型指针&#xff0c;如果把p1赋给p2&#xff0c;则编译出…

【题解】—— LeetCode一周小结27

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 【题解】—— 每日一道题目栏 上接&#xff1a;【题解】—— LeetCode一周小结26 2024.7 1.最大化一张图中的路径价值 题目链接&#xff1a;…

墨烯的C语言技术栈-C语言基础-008

八.转义字符(转变原来字符的意思) 假如我们要在屏幕上打印一个目录:C:\code\test.c 我们应该如何写代码 int main() { printf("abc\n") // \让n转变 换行作用 return 0; } imt main() { printf("abc\0def") // \0后就是结束了后面打不印了 return 0; }…

C语言指针,的声明、取地址、*解引用、设置值、偏移、自增 的问题举例

C语言指针,的声明、&取地址、※解引用、设置值、偏移、自增 的问题举例代码&#xff1a;int※a,d[]{1,2,4,8};char*f,e[]“Hellow…”;a&b; P1※a; ※a4; P1※(a);f&e[7]; &#xff08;备注&#xff1a;※代表*&#xff0c;转义问题&#xff0c;没显示&#xff09…

Spring Cloud: Nacos配置中心与注册中心的使用

一、配置中心(配置管理) 配置中心是一种集中化管理配置的服务。它的主要作用包括集中管理配置信息&#xff0c;将不同服务的配置信息集中存储和管理&#xff1b;支持动态更新配置&#xff0c;通过操作界面或 API 无需重启服务即可应用最新配置信息&#xff1b;实现配置信息共享…

通过AIS实现船舶追踪与照射

前些天突然接到个紧急的项目&#xff1a;某处需要实现对夜航船只进行追踪并用激光灯照射以保障夜航安全。这个项目紧急到什么程度呢&#xff1f;&#xff01;现场激光灯都安装好了&#xff0c;还有三个星期就要验收了&#xff0c;但上家没搞定就甩给我们了:( 从技术上看&#…

【Python】已解决:xml.parsers.expat.ExpatError: no element found: Line 1, column 0

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;xml.parsers.expat.ExpatError: no element found: Line 1, column 0 一、分析问题背景 在使用Python的xml.parsers.expat模块解析XML文件时&#xff0c;有时会…