四元数转换为欧拉角(多解问题)

news2024/11/19 14:46:40

车辆行驶状态估计(4)中车辆横摆角信息在顺时针转向时存在明显的错误,进行记录输出

  • 2023-05-25-aft02.txt
四元数:  -0.0020121
  0.00115721
-0.000596761
    0.999997
欧拉角:3.14039

四元数: -0.00170584
-0.000230032
   -0.499041
    0.866577
欧拉角:2.09661

四元数:-0.00445314
-0.00242893
   -0.87609
    0.48212
欧拉角:1.00616

通过在线转换网站进行转换,检查是否一致

3D Rotation Converter

在这里插入图片描述

发现和用 Eigen 进行四元数转欧拉角存在很大的差别

在Eigen库中,可以使用Quaternion类来表示四元数,并使用Matrix类来表示欧拉角。下面是将四元数转换为欧拉角的示例代码

#include <iostream>
#include <Eigen/Dense>

int main() {
    // 定义四元数
    Eigen::Quaterniond quaternion(0.707, 0.0, 0.707, 0.0); // 示例四元数

    // 将四元数转换为旋转矩阵
    Eigen::Matrix3d rotationMatrix = quaternion.toRotationMatrix();

    // 将旋转矩阵转换为欧拉角
    Eigen::Vector3d eulerAngles = rotationMatrix.eulerAngles(2, 1, 0); // ZYX顺序的欧拉角,可根据需求调整顺序

    // 输出欧拉角
    std::cout << "Roll: " << eulerAngles[2] << std::endl;
    std::cout << "Pitch: " << eulerAngles[1] << std::endl;
    std::cout << "Yaw: " << eulerAngles[0] << std::endl;

    return 0;
}

在上述代码中,使用Quaterniond类创建了一个四元数对象,然后使用toRotationMatrix()函数将其转换为旋转矩阵。最后,使用eulerAngles()函数将旋转矩阵转换为欧拉角,其中参数2、1、0表示按照Z轴、Y轴、X轴的顺序进行转换,即ZYX顺序的欧拉角

在这里插入图片描述

四元数和欧拉角互相转换

在这里插入图片描述

static void toEulerAngle(const Quaterniond& q, double& roll, double& pitch, double& yaw)
{
		// roll (x-axis rotation)
		double sinr_cosp = +2.0 * (q.w() * q.x() + q.y() * q.z());
		double cosr_cosp = +1.0 - 2.0 * (q.x() * q.x() + q.y() * q.y());
		roll = atan2(sinr_cosp, cosr_cosp);

		// pitch (y-axis rotation)
		double sinp = +2.0 * (q.w() * q.y() - q.z() * q.x());
		if (fabs(sinp) >= 1)
			pitch = copysign(M_PI / 2, sinp); // use 90 degrees if out of range
		else
			pitch = asin(sinp);

		// yaw (z-axis rotation)
		double siny_cosp = +2.0 * (q.w() * q.z() + q.x() * q.y());
		double cosy_cosp = +1.0 - 2.0 * (q.y() * q.y() + q.z() * q.z());
		yaw = atan2(siny_cosp, cosy_cosp);
}

编写测试功能包,angle_transform

#include <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
#include <math.h>

using namespace std;
using namespace Eigen;

void toEulerAngle(const Quaterniond &q, Vector3d& eulerAngles)
{
    // roll (x-axis rotation)
    double sinr_cosp = +2.0 * (q.w() * q.x() + q.y() * q.z());
    double cosr_cosp = +1.0 - 2.0 * (q.x() * q.x() + q.y() * q.y());
    eulerAngles[2] = atan2(sinr_cosp, cosr_cosp);

    // pitch (y-axis rotation)
    double sinp = +2.0 * (q.w() * q.y() - q.z() * q.x());
    if (fabs(sinp) >= 1)
        eulerAngles[1] = copysign(M_PI / 2, sinp); // use 90 degrees if out of range
    else
        eulerAngles[1] = asin(sinp);

    // yaw (z-axis rotation)
    double siny_cosp = +2.0 * (q.w() * q.z() + q.x() * q.y());
    double cosy_cosp = +1.0 - 2.0 * (q.y() * q.y() + q.z() * q.z());
    eulerAngles[0] = atan2(siny_cosp, cosy_cosp);
}

int main()
{
    double w = 0.524;
    double x = -0.0048521;
    double y = -0.00167539;
    double z = -0.851703;
    Quaterniond q(w, x, y, z);

    // 使用 Eigen    
    Matrix3d rotMat = q.toRotationMatrix();
    Vector3d eulerAngles = rotMat.eulerAngles(2, 1, 0);
    cout << "使用Eigen库" << endl;
    cout << "旋转矩阵:" << endl;
    cout << rotMat << endl;
    cout << "Roll: " << eulerAngles[2] << endl;
    cout << "Pitch: " << eulerAngles[1] << endl;
    cout << "Yaw: " << eulerAngles[0] << endl;

    // 自己的方法
    Vector3d eulerAngles_def;
    toEulerAngle(q, eulerAngles_def);
    cout << "自定义方法" << endl;
    cout << "Roll: " << eulerAngles_def[2] << endl;
    cout << "Pitch: " << eulerAngles_def[1] << endl;
    cout << "Yaw: " << eulerAngles_def[0] << endl;

    return 0;
}

修改状态估计代码

#include <iostream>
#include <string.h>
#include <ros/ros.h>
#include <nav_msgs/Odometry.h>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
#include <realsense_state_estimate/robot_state.h>

using namespace std;
using namespace Eigen;

ros::Publisher pub;

realsense_state_estimate::robot_state imu_state;

inline double rad2deg(const double& rad)
{
    return rad * 180 / M_PI;
}

inline void toEulerAngle(const Quaterniond& q, Vector3d& eulerAngles)
{
    // roll (x-axis rotation)
    double sinr_cosp = 2.0 * (q.w() * q.x() + q.y() * q.z());
    double cosr_cosp = 1.0 - 2.0 * (q.x() * q.x() + q.y() * q.y());
    eulerAngles[2] = atan2(sinr_cosp, cosr_cosp);

    // pitch (y-axis rotation)
    double sinp = +2.0 * (q.w() * q.y() - q.z() * q.x());
    if (fabs(sinp) >= 1)
        eulerAngles[1] = copysign(M_PI / 2, sinp); // use 90 degrees if out of range
    else
        eulerAngles[1] = asin(sinp);

    // yaw (z-axis rotation)
    double siny_cosp = +2.0 * (q.w() * q.z() + q.x() * q.y());
    double cosy_cosp = +1.0 - 2.0 * (q.y() * q.y() + q.z() * q.z());
    eulerAngles[0] = atan2(siny_cosp, cosy_cosp);
}

void callback(const nav_msgs::Odometry::ConstPtr &msg)
{
    imu_state.X_pos = msg->pose.pose.position.x;
    imu_state.Y_pos = msg->pose.pose.position.y;
    imu_state.x_vel = msg->twist.twist.linear.x;
    imu_state.y_vel = msg->twist.twist.linear.y;
    Quaterniond q(msg->pose.pose.orientation.w, msg->pose.pose.orientation.x, msg->pose.pose.orientation.y, msg->pose.pose.orientation.z);
    Vector3d eulerAngles;
    toEulerAngle(q, eulerAngles);
    imu_state.yaw_angle = eulerAngles[0];
    imu_state.yaw_vel = msg->twist.twist.angular.z;
    cout << imu_state.X_pos << "," << imu_state.Y_pos << "," << imu_state.x_vel << \ 
    "," << imu_state.y_vel << "," << imu_state.yaw_angle << "," << imu_state.yaw_vel << endl;
    pub.publish(imu_state);
}

int main(int argc, char *argv[])
{
    ros::init(argc, argv, "realsense_state");
    ros::NodeHandle nh;

    string subscribe_topic = "/camera/odom/sample";
    string publishe_topic = "/realsense_state_estimate";
    pub = nh.advertise<realsense_state_estimate::robot_state>(publishe_topic, 10);
    ros::Subscriber sub = nh.subscribe(subscribe_topic, 10, callback);

    ros::spin();

    return 0;
}

实车测试,原地顺时针转向

  • 2023-05-25-aft03.csv

车辆位置信息

在这里插入图片描述

车辆横摆角信息

在这里插入图片描述

车辆横摆角速度信息

在这里插入图片描述

车辆横摆角变化符合预期,验证四元数到欧拉角转换方法的正确性

存在的问题是由于IMU并未处于车辆质心位置,因此其估计值并不能直接使用,可能需要经过坐标变换

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

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

相关文章

RabbitMQ(2)、MQ的问题、消息可靠性

一、MQ的问题 基于上篇存在的问题 1. 问题说明 MQ在分布式项目中是非常重要的&#xff0c; 它可以实现异步、削峰、解耦&#xff0c;但是在项目中引入MQ也会带来一系列的问题。 今天我们要解决以下几个常见的问题&#xff1a; 消息可靠性问题&#xff1a;如何确保消息被成功送…

MySQL — SQL 优化

文章目录 SQL 优化一、插入数据二、主键优化2.1 数据组织方式2.2 页分裂2.3 页合并2.4 主键设计原则 三、 Order by 优化3.0 排序方式讲解3.1 升序/降序联合索引结构图示3.2 总结 四、Group by优化五、limit优化六、 count优化七、update优化七、update优化 SQL 优化 一、插入…

【MySQL 数据库】5、存储引擎

目录 一、MySQL 体系结构二、存储引擎简介三、InnoDB 存储引擎四、MyISAM五、Memory六、三大存储引擎比较七、存储引擎的选择 一、MySQL 体系结构 连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主…

07:MYSQL----多表查询

目录 1:多表查询概述 2:多表查询分类 3:内连接 3:外连接 4:自连接 5:联合查询-union&#xff0c;union all 6:子查询 1:多表查询概述 select * from emp , dept; emp:表中有6条数据, dept表中有5条数据只查询出来的数据为:30条 概述:指从多张表中查询数据 笛卡尔积…

在vite或者vue-cli中使用.env[mode]环境变量

在项目中总会遇到一些默认的配置,需要我们配置到静态文件中方便我们去获取,这时候就可以用到这个.env环境变量文件,在cli创建的项目中顶层的nodejs会有一个process对象,这个对象可以根据不同的环境获取不同的环境配置文件,但是vite中获取变量的方式不一样。 创建变量文件.env.…

如何编写接口自动化框架系列之requests详解(三)

目录 1.http协议 2.requests介绍 3.requests的主要功能 3.requests的主要功能 3.1 场景1-常用方法 3.2 场景2-通用方法 3.3 场景3-cookies认证方式 4.requests 在项目中的实践 4.1 在接口层实现一个接口 4.2 在测试用例层调用 4.3 项目总结 本文是接口自动化测试框架…

IOC初始化 IOC启动阶段 (Spring容器的启动流程)

[toc](IOC初始化 IOC启动阶段 (Spring容器的启动流程)) IOC初始化 IOC启动阶段 (Spring容器的启动流程) Resource定位过程&#xff1a;这个过程是指定位BeanDefinition的资源&#xff0c;也就是配置文件&#xff08;如xml&#xff09;的位置&#xff0c;并将其封装成Resource对…

Makefile基础教程(make的隐式规则)

文章目录 前言一、什么是make的隐式规则二、makefile中出现同名目标时三、一些常见的隐式规则四、查看隐式规则五、隐式规则缺点六、禁用隐式规则1.全局禁用2.局部禁用 总结 前言 本篇文章将给大家介绍make的隐式规则。 一、什么是make的隐式规则 Make 的隐式规则是指 Make …

css选择器及其权重

1. 类型选择器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

【ZYNQ】裸机 PS + PL 双网口实现之 ZYNQ 配置

目前&#xff0c;在 ZYNQ 中进行以太网开发的方案&#xff0c;大部分都是基于通过 PS 的 MIO 以 RGMII 接口连接外部 PHY 芯片的方式。但是&#xff0c;由于使用 PS 的 MIO 只能以 RGMII 接口连接外部 PHY 芯片&#xff0c;这就限制了支持其他接口 PHY 芯片的使用&#xff0c;如…

分文件实现温湿度数据管理系统项目

目标&#xff1a; 了解分文件的概念&#xff0c;要依次从C语言的函数声明、变量的存储类别、C语言编译预处理&#xff0c;说起。这些知识点我们之前或多或少接触过&#xff0c;这里做个总结与拓展。经过总结&#xff0c;最后我们归纳出一个实现C语言模块化编程的技巧&#xff…

03-bootstrap-响应式布局-栅格系统

一、概述 1、栅格系统是 Bootstrap 中响应式布局的重要组成部分&#xff0c;旨在实现页面元素的自适应排版。Bootstrap 栅格系统将屏幕宽度分为 12 列&#xff0c;通过在 HTML 元素上添加相应的类名&#xff0c;可以让元素占据指定数量的列数&#xff0c;从而实现灵活的布局效…

5种易实现的Linux和 Windows VPS速度提升方法

​  无论是Linux VPS还是Windows VPS&#xff0c;网站速度的提高都是非常重要的。它们在提高网站速度方面都有很多的优化方法。下面我们将介绍 5 种提高网站速度的方法。 1.通过缓存加速 缓存通常是用来加快商业网站加载时间的技术&#xff0c;因此它也可以用在 VPS 上。没有…

车架号查车辆信息-vin查车辆信息api接口

接口地址&#xff1a; https://登录后显示/pyi/88/264(支持:http/https)) 在线查询&#xff1a;https://www.wapi.cn/car_vin.html 网站地址&#xff1a;https://www.wapi.cn 返回格式&#xff1a;json,xml 请求方式&#xff1a;GET,POST 请求说明&#xff1a; Md5验证方式-…

字符串、字符串列表,倒序生成字典。

带数字的字符串以数字为key倒序生成字典&#xff0c;字符串列表按其元素索引为key倒序生成字典。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简…

【MySQL】-- 表的操作

目录 表的操作 创建表 创建表案例 查看表结构 查看表结构案例 查看历史上表的创建语句 修改表 修改表实例 新增列属性 修改列属性 删除列属性 修改列名 修改表名 删除表 表的操作 创建表 语法&#xff1a; CREATE TABLE (if not exists) table_name (fie…

【MyBatisPlus框架】

文章目录 MyBatisPlus1.概述1.1 简介1.2特性1.3支持数据库1.4框架结构 2.入门案例2.1 创建数据库以及表2.2 创建工程2.2.1引入依赖 2.3编写代码 3.基本CRUD3.1BaseMapper3.2插入3.3删除3.4修改3.5查询3.6通用Service 4.常用注解4.1TableName4.1.1问题4.1.2通过TableName解决上述…

简述springmvc的流程

4、SpringMVC的执行流程 用户向服务器发送请求&#xff0c;请求被SpringMVC 前端控制器 DispatcherServlet捕获。 DispatcherServlet对请求URL进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;&#xff0c;判断请求URI对应的映射&#xff1a; a) 不存在 …

day05 java_Spring IoC 和 DI

为什么使用spring框架 1.解耦代码(每次使用都要new一个对象) 2.解决事务繁琐问题(创建对象----初始化----调用方法销毁对象) 3.使用第三方框架麻烦的问题 总结:spring是一个轻量级的Ioc,Di和AOP容器 轻量级:简洁,高效,低依赖 **容器:**创建对象并将对象存储对象,同时管理…

高矿化度矿井水深度除氟装置CH-87技术解析

高矿化度矿井水是指含有高浓度溶解性矿物质的废水&#xff0c;通常指的是含有高浓度钠、钙、镁、铁、铝、钾等离子的废水。这些离子通常来自于废水所处的环境、工业或生产过程中使用的原材料和化学品。高矿化度的废水通常具有高盐度、高电导率、高硬度等特征&#xff0c;对环境…