什么是欧拉角/姿态角?

本文介绍了欧拉角,它是物体绕坐标系三个坐标轴的旋转角度,有静态和动态之分,其约定多样,有易使用等优点,也有表达不唯一等缺点。还说明了旋转方法,最后对比了欧拉角、四元素法和旋转矩阵法的特点及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


用一句话说,欧拉角就是物体绕坐标系三个坐标轴(x,y,z轴)的旋转角度。

在这里,坐标系可以是世界坐标系,也可以是物体坐标系,旋转顺序也是任意的,可以是xyz,xzy,yxz,zxy,yzx,zyx中的任何一种,甚至可以是xyx,xyy,xzz,zxz等等等等。。。。。。

所以说欧拉角多种多样。欧拉角可分为两种情况:

1,静态:即绕世界坐标系三个轴的旋转,由于物体旋转过程中坐标轴保持静止,所以称为静态。

2,动态:即绕物体坐标系三个轴的旋转,由于物体旋转过程中坐标轴随着物体做相同的转动,所以称为动态。

对于分别绕三个坐标轴旋转的情况,下述定理成立:

物体的任何一种旋转都可分解为分别绕三个轴的旋转,但分解方式不唯一。如:


假设绕y轴旋转为heading,绕x轴旋转为pitch,绕z轴旋转为bank,则先heading45°再pitch90°等价于先pitch90°再bank45°。

 

 

前面曾提到过,heading-pitch-bank系统不是惟一的欧拉角系统,绕任意三个互相垂直轴的任意旋转序列都能定义一个方位。所以,多种选择导致了欧拉角约定的多样性:


     1)heading-pitch-bank系统有两个名称,当然,不同的名字并不代表不同的约定,这其实并不重要,一组常用的术语是roll-pitch-yaw,其中的roll对应与bank,yaw对应于heading,它定义了从物体坐标系到惯性坐标系的旋转顺序


     2)任意三个轴都能作为旋转轴,不一定必须是笛卡尔轴,但是用笛卡尔轴最有意义


     3)也可以选用右手坐标规则


     4)旋转可以以不同的顺序进行


3,优点:1)容易使用;2)表达简洁;3)任意三个角都是合法的


4,缺点:1)给定方位的表达方式不唯一;2)两个角度间求插值非常困难


采用限制欧拉角的方法来避免以上问题的出现:heading限制在+-180,pitch为+-90。


    以上为欧拉角的定义。旋转的方法如下:


从欧拉角矢量转换很容易,困难的部分是转换回来。XNA提供了一个方法可以创建旋转矩阵,但它并没有提供转换回来的方法,因此我们将不得不自己实现。


首先,我们转换为旋转矩阵,Matrix.CreateFromYawPitchRoll()方法可以做到这一点。如果这里使用欧拉角,我们需要以以下顺序提供坐标:


Yaw(偏航):欧拉角向量的y轴
Pitch(俯仰):欧拉角向量的x轴
Roll(翻滚): 欧拉角向量的z轴
想象一下飞机,yaw指水平方向的机头指向,它绕y轴旋转。Pitch指与水平方向的夹角,绕x轴旋转。Roll指飞机的翻滚,绕z轴旋转。

除欧拉角以外,常用的还有四元素法和旋转矩阵法。简而言之,三种方法的特点如下:

1)欧拉角最直观、最容易理解、存储空间少,但是欧拉角存在万向节死锁现象、插值速度不均匀等缺点,而且不可以在计算机中直接运算;

2)四元素不存在万向节死锁问题、利用球面插值可以获得均匀的转速、存储空间也较少,但是不好理解、不直观;

3)旋转矩阵法是最便于计算机处理的,但不可以直接插值、冗余信息多、费存储空间,同样不直观。所以,在机器人学中,一般人机交互端会用欧拉角,插值等用四元素,正逆运动学运算中用矩阵表示法。
--------------------- 
作者:caimouse 
来源:优快云 
原文:https://blog.youkuaiyun.com/caimouse/article/details/55259669 
版权声明:本文为博主原创文章,转载请附上博文链接!

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_tim.h" #include "stm32f10x_adc.h" #include "stm32f10x_dma.h" #include "stm32f10x_exti.h" #include "misc.h" #include "Delay.h" #include "OLED.h" #include "Servo.h" #include "Key.h" #include <math.h> volatile float TargetAngle = 0.0f; volatile float CurrentAngle = 0.0f; volatile float GyroZ = 0.0f; volatile float IntegralError = 0.0f; volatile float LastError = 0.0f; uint8_t KeyNum; #define KP 2.5f #define KI 0.01f #define KD 0.5f #define GYRO_SENSITIVITY 0.07f #define GYRO_OFFSET 0.0f void System_Init(void); void TIM3_Init(void); void Gyro_Init(void); float Gyro_GetZ(void); float PID_Controller(float target, float current); void Calibrate_Gyro(void); int main(void) { System_Init(); OLED_Init(); Servo_Init(); Key_Init(); TIM3_Init(); Gyro_Init(); Calibrate_Gyro(); OLED_ShowString(1, 1, "Target: "); OLED_ShowString(2, 1, "Current: "); OLED_ShowString(3, 1, "Output: "); OLED_ShowString(4, 1, "GyroZ: "); TargetAngle = 0.0f; OLED_ShowNum(1, 8, (int)TargetAngle, 3); TIM_Cmd(TIM3, ENABLE); while (1) { KeyNum = Key_GetNum(); if (KeyNum == 1) { TargetAngle += 10.0f; if (TargetAngle > 45.0f) TargetAngle = 45.0f; OLED_ShowNum(1, 8, (int)TargetAngle, 3); } if (KeyNum == 2) { TargetAngle -= 10.0f; if (TargetAngle < -45.0f) TargetAngle = -45.0f; OLED_ShowNum(1, 8, (int)TargetAngle, 3); } // ??3:???????0? if (KeyNum == 3) { TargetAngle = 0.0f; OLED_ShowNum(1, 8, (int)TargetAngle, 3); } // ?????? OLED_ShowNum(2, 9, (int)CurrentAngle, 3); // ???????? OLED_ShowNum(4, 8, (int)GyroZ, 4); // ????,??OLED???? Delay_ms(50); } } // ????? void System_Init(void) { // ???RCC?? RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); // ??NVIC????? NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); } // ???3???(????????????PID??) void TIM3_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; // ??TIM3?? RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // ?????:10ms???? TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // ?????? TIM_TimeBaseStructure.TIM_Prescaler = 720 - 1; // ???? (72MHz/720 = 100kHz) TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // ??TIM3???? TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // ??TIM3?? NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // ??????(???????,?????????????) void Gyro_Init(void) { // ??????????I2C?SPI?????????? // ??????,???????????????????? } // ??Z????(????) float Gyro_GetZ(void) { // ?????????????????? // ?????????,??????????????????? static float simulatedGyro = 0.0f; // ??????? if (TargetAngle > CurrentAngle) { simulatedGyro += 0.5f; } else if (TargetAngle < CurrentAngle) { simulatedGyro -= 0.5f; } // ????????????? simulatedGyro += ((rand() % 10) - 5) * 0.1f; return simulatedGyro; } // ????? void Calibrate_Gyro(void) { OLED_ShowString(1, 1, "Calibrating..."); // ?????????????????????? // ?????? Delay_ms(1000); OLED_Clear(); OLED_ShowString(1, 1, "Target: "); OLED_ShowString(2, 1, "Current: "); OLED_ShowString(3, 1, "Output: "); OLED_ShowString(4, 1, "GyroZ: "); } // PID????? float PID_Controller(float target, float current) { float error = target - current; IntegralError += error; if (IntegralError > 100.0f) IntegralError = 100.0f; if (IntegralError < -100.0f) IntegralError = -100.0f; float derivative = error - LastError; LastError = error; float output = KP * error + KI * IntegralError + KD * derivative; if (output > 45.0f) output = 45.0f; if (output < -45.0f) output = -45.0f; return output; } void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 1. ??????? GyroZ = Gyro_GetZ(); // 2. ??????(??? * ??) // ?????0.01?(10ms) CurrentAngle += GyroZ * 0.01f; // 3. ??PID??????????? float output = PID_Controller(TargetAngle, CurrentAngle); // 4. ????(90??????) Servo_SetAngle(90.0f + output); // ??PID??? OLED_ShowNum(3, 8, (int)output, 3); } }将陀螺仪移动角度改成-180-180
最新发布
08-05
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值