手动实现的与EIGEN3速度相当的矩阵乘法

本文详细介绍了如何使用ARMNEON指令集优化矩阵乘法算法,通过向量化和SIMD技术提高计算效率。代码展示了两种不同步态下的乘法操作,一种是未对齐内存处理,另一种利用了向量单元的特性加速计算。

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

话不多说,直接贴代码如下:

#include<arm_neon.h>
inline void mat_mul(DATA_TYPE *A, DATA_TYPE *B, DATA_TYPE *C, int M, int N, int K)
{
#if 0
	int mStep = 4;
	int MalignedStep = (M / mStep) * mStep;
	int m = 0;
	for (m = 0; m < MalignedStep; m += mStep)
	{
		for (int p = 0; p < N; p++)
		{
			for (int q = 0; q < mStep; q++)
			{
				C[(m + q) * N + p] = 0;
			}
		}

		int kStep = 4;
		int KalignedStep = (K / kStep) * kStep;
		int k = 0;
		for (k = 0; k < KalignedStep; k += kStep)
		{	
			for (int n = 0; n < N; n += 1)
			{
				for (int q = 0; q < mStep; q++)
				{
					for (int p = 0; p < kStep; p++)
					{
						
						C[(m + q) * N + n] += A[(m + q) * K + k + p] * B[(k + p) * N + n];
						
					}
				}
			}
		}
		for (; k < K; k += 1)
		{
			for (int n = 0; n < N; n += 1)
			{
				for (int q = 0; q < mStep; q++)
				{
					C[(m + q) * N + n] += A[(m + q) * K + k] * B[k * N + n];
				}
			}
		}
	}

	for (; m < M; m += 1)
	{
		for (int p = 0; p < N; p++)
		{
			C[m * N + p] = 0;		
		}
		int kStep = 4;
		int KalignedStep = (K / kStep) * kStep;
		int k = 0;
		for (k = 0; k < KalignedStep; k += kStep)
		{
			DATA_TYPE A_data[kStep];
			for (int p = 0; p < kStep; p++)
			{
				A_data[p] = A[m * K + k + p];
			}
			for (int n = 0; n < N; n += 1)
			{
				for (int p = 0; p < kStep; p++)
				{
					C[m * N + n] += A_data[p] * B[(k + p) * N + n];
				}
			}

		}
		for (; k < K; k += 1)
		{
			DATA_TYPE A_data;
			A_data = A[m * K + k];
			for (int n = 0; n < N; n += 1)
			{
				C[m * N + n] += A_data * B[k * N + n];
			}
		}
	}
#else
	int mStep = 4;
	int MalignedStep = (M / mStep) * mStep;
	int m = 0;
	for (m = 0; m < MalignedStep; m += mStep)
	{
		for (int p = 0; p < N; p++)
		{
			C[m * N + p] = 0;
			C[(m + 1) * N + p] = 0;
			C[(m + 2) * N + p] = 0;
			C[(m + 3) * N + p] = 0;
			
		}
		int kStep = 4;
		int KalignedStep = (K / kStep) * kStep;
		int k = 0;
		for (k = 0; k < KalignedStep; k += kStep)
		{
			float32x4_t A_data1 = vdupq_n_f32(A[m * K + k]);
			float32x4_t A_data2 = vdupq_n_f32(A[m * K + k + 1]);
			float32x4_t A_data3 = vdupq_n_f32(A[m * K + k + 2]);
			float32x4_t A_data4 = vdupq_n_f32(A[m * K + k + 3]);
			
			float32x4_t A_data21 = vdupq_n_f32(A[(m + 1) * K + k]);
			float32x4_t A_data22 = vdupq_n_f32(A[(m + 1) * K + k + 1]);
			float32x4_t A_data23 = vdupq_n_f32(A[(m + 1) * K + k + 2]);
			float32x4_t A_data24 = vdupq_n_f32(A[(m + 1) * K + k + 3]);
			
			float32x4_t A_data31 = vdupq_n_f32(A[(m + 2) * K + k]);
			float32x4_t A_data32 = vdupq_n_f32(A[(m + 2) * K + k + 1]);
			float32x4_t A_data33 = vdupq_n_f32(A[(m + 2) * K + k + 2]);
			float32x4_t A_data34 = vdupq_n_f32(A[(m + 2) * K + k + 3]);
			
			float32x4_t A_data41 = vdupq_n_f32(A[(m + 3) * K + k]);
			float32x4_t A_data42 = vdupq_n_f32(A[(m + 3) * K + k + 1]);
			float32x4_t A_data43 = vdupq_n_f32(A[(m + 3) * K + k + 2]);
			float32x4_t A_data44 = vdupq_n_f32(A[(m + 3) * K + k + 3]);
			float *C_ptr1 = C + m * N;
			float *C_ptr2 = C + (m + 1) * N;
			float *C_ptr3 = C + (m + 2) * N;
			float *C_ptr4 = C + (m + 3) * N;
			for (int n = 0; n < N; n += 4)
			{
					float32x4_t B_data1  = vld1q_f32(B + (k + 0) * N + n);
					float32x4_t B_data2  = vld1q_f32(B + (k + 1) * N + n);
					float32x4_t B_data3  = vld1q_f32(B + (k + 2) * N + n);
					float32x4_t B_data4  = vld1q_f32(B + (k + 3) * N + n);
					float32x4_t C_data  = vld1q_f32(C_ptr1 + n);
					float32x4_t C_data2  = vld1q_f32(C_ptr2 + n);
					float32x4_t C_data3  = vld1q_f32(C_ptr3 + n);
					float32x4_t C_data4  = vld1q_f32(C_ptr4 + n);
					C_data = vmlaq_f32(C_data, A_data1, B_data1);
					C_data = vmlaq_f32(C_data, A_data2, B_data2);
					C_data = vmlaq_f32(C_data, A_data3, B_data3);
					C_data = vmlaq_f32(C_data, A_data4, B_data4);
					
					C_data2 = vmlaq_f32(C_data2, A_data21, B_data1);
					C_data2 = vmlaq_f32(C_data2, A_data22, B_data2);
					C_data2 = vmlaq_f32(C_data2, A_data23, B_data3);
					C_data2 = vmlaq_f32(C_data2, A_data24, B_data4);
					
					C_data3 = vmlaq_f32(C_data3, A_data31, B_data1);
					C_data3 = vmlaq_f32(C_data3, A_data32, B_data2);
					C_data3 = vmlaq_f32(C_data3, A_data33, B_data3);
					C_data3 = vmlaq_f32(C_data3, A_data34, B_data4);
					
					C_data4 = vmlaq_f32(C_data4, A_data41, B_data1);
					C_data4 = vmlaq_f32(C_data4, A_data42, B_data2);
					C_data4 = vmlaq_f32(C_data4, A_data43, B_data3);
					C_data4 = vmlaq_f32(C_data4, A_data44, B_data4);
					
					vst1q_f32(C_ptr1 + n, C_data);
					vst1q_f32(C_ptr2 + n, C_data2);
					vst1q_f32(C_ptr3 + n, C_data3);
					vst1q_f32(C_ptr4 + n, C_data4);
			}
		}
		for (; k < K; k += 1)
		{
			DATA_TYPE A_data;
			A_data = A[m * K + k];
			DATA_TYPE A_data2;
			A_data2 = A[(m + 1) * K + k];
			DATA_TYPE A_data3;
			A_data3 = A[(m + 2) * K + k];
			DATA_TYPE A_data4;
			A_data4 = A[(m + 3) * K + k];
			for (int n = 0; n < N; n += 1)
			{
				C[m * N + n] += A_data * B[k * N + n];
				C[(m + 1) * N + n] += A_data2 * B[k * N + n];
				C[(m + 2) * N + n] += A_data3 * B[k * N + n];
				C[(m + 3) * N + n] += A_data4 * B[k * N + n];
			}
		}
	}

	for (; m < M; m += 1)
	{
		for (int p = 0; p < N; p++)
		{
			C[m * N + p] = 0;		
		}
		int kStep = 4;
		int KalignedStep = (K / kStep) * kStep;
		int k = 0;
		for (k = 0; k < KalignedStep; k += kStep)
		{
			float32x4_t A_data1 = vdupq_n_f32(A[m * K + k]);
			float32x4_t A_data2 = vdupq_n_f32(A[m * K + k + 1]);
			float32x4_t A_data3 = vdupq_n_f32(A[m * K + k + 2]);
			float32x4_t A_data4 = vdupq_n_f32(A[m * K + k + 3]);

			float *C_ptr1 = C + m * N;
			for (int n = 0; n < N; n += 4)
			{
					float32x4_t B_data1  = vld1q_f32(B + (k + 0) * N + n);
					float32x4_t B_data2  = vld1q_f32(B + (k + 1) * N + n);
					float32x4_t B_data3  = vld1q_f32(B + (k + 2) * N + n);
					float32x4_t B_data4  = vld1q_f32(B + (k + 3) * N + n);
					float32x4_t C_data  = vld1q_f32(C_ptr1 + n);

					C_data = vmlaq_f32(C_data, A_data1, B_data1);
					C_data = vmlaq_f32(C_data, A_data2, B_data2);
					C_data = vmlaq_f32(C_data, A_data3, B_data3);
					C_data = vmlaq_f32(C_data, A_data4, B_data4);
					
					vst1q_f32(C_ptr1 + n, C_data);
			}
		}
		for (; k < K; k += 1)
		{
			DATA_TYPE A_data;
			A_data = A[m * K + k];
			for (int n = 0; n < N; n += 1)
			{
				C[m * N + n] += A_data * B[k * N + n];
			}
		}
	}
#endif
}

<think>嗯,用户问的是如何在PCL中表示一个点的XYZ和RPY角度,并在PCLViewer里绘制。首先,我需要明确问题的各个部分。XYZ坐标应该直接对应到PCL的PointXYZ结构,但RPY是欧拉角,可能需要转换成旋转矩阵或四元数来表示方向。然后,在PCLViewer中显示,可能需要绘制坐标系或者箭头来表示位姿。 首先,确定PCL中的点表示。PCL库中的点类型有很多,比如pcl::PointXYZ,用户给出的XYZ坐标可以直接用这个结构体存储。例如,点的坐标是(x, y, z),可以直接赋值给point.x, point.y, point.z。这部分应该没问题。 接下来是RPY角度,即Roll、Pitch、Yaw。RPY通常用来表示物体的姿态,但在三维空间中,需要将这些欧拉角转换为旋转矩阵或四元数,以便在空间中正确表示方向。用户可能需要将RPY转换为旋转矩阵,然后应用到坐标系或者模型上。这里可能需要使用Eigen库,因为PCL本身依赖Eigen来处理矩阵运算。 转换步骤:将RPY(roll, pitch, yaw)转换为旋转矩阵。欧拉角的旋转顺序可能会影响结果,通常RPY是按照ZYX顺序旋转的,即先绕Z轴转yaw,再绕Y轴转pitch,最后绕X轴转roll。所以需要按照这个顺序生成旋转矩阵。 然后,在PCLViewer中绘制这个位姿。PCLViewer提供了添加坐标系的方法,比如addCoordinateSystem,可以指定位置和方向。但这个方法可能需要四元数或旋转矩阵作为方向参数。或者,用户可能需要手动绘制箭头或三个轴来表示方向。 例如,使用addCoordinateSystem函数,参数包括坐标系的大小、位置(Eigen::Affine3f),或者直接传递旋转矩阵和平移向量。这里可能需要构造一个Affine3f变换,其中平移部分是点的XYZ坐标,旋转部分由RPY转换而来的旋转矩阵。 具体步骤可能是这样的: 1. 将RPY转换为旋转矩阵。使用Eigen::AngleAxisf来分别构造每个轴的旋转,然后将它们组合起来。注意旋转顺序,先Yaw(绕Z),再Pitch(绕Y),最后Roll(绕X)。所以总的旋转矩阵R = R_z(yaw) * R_y(pitch) * R_x(roll)。 2. 构造一个Affine3f变换,设置平移部分为点的XYZ,旋转部分为上述旋转矩阵3. 在PCLViewer中使用addCoordinateSystem函数,传入这个Affine3f变换,以及缩放系数,来显示坐标系。 或者,如果用户想显示一个带有方向的箭头或其他形状,可能需要计算旋转后的方向向量,并绘制相应的线条或箭头。比如,原点的位置是点的XYZ,箭头方向根据旋转后的Z轴或其他轴来绘制。 另外,需要注意的是,PCLViewer中的坐标系默认可能是在原点,所以需要将变换应用到正确的位置。使用addCoordinateSystem时,传入的Affine3f应该正确反映位置和方向。 可能遇到的错误点:欧拉角的旋转顺序是否正确,旋转矩阵是否正确生成,以及Affine3f的构造是否正确。如果顺序错了,方向可能不正确。 总结步骤: 1. 使用PointXYZ存储点的位置。 2. 将RPY转换为旋转矩阵,注意顺序。 3. 构造Affine3f变换,设置平移和旋转。 4. 在Viewer中添加坐标系或者箭头表示位姿。 现在需要验证这些步骤是否正确,特别是旋转矩阵的生成部分。例如,Eigen中的AngleAxis相乘的顺序是否旋转顺序一致。例如,先绕Z轴转yaw,对应的AngleAxisf(yaw, Eigen::Vector3f::ZAxis()),然后绕Y轴转pitch,再绕X轴转roll。总的旋转应该是这三个旋转的组合,但需要注意乘法顺序是否为R = R_z * R_y * R_x,或者相反。在Eigen中,旋转矩阵乘法是左乘,所以变换的顺序是从右到左应用。例如,R = R_z * R_y * R_x 表示先应用R_x,再R_y,最后R_z。但如果是按照ZYX顺序,也就是先绕Z,然后Y,然后X,那可能需要调整顺序? 或者,可能我在这里混淆了。例如,欧拉角通常的顺序是,例如,在ZYX顺序下,分解为三次绕不同的轴旋转:先绕Z轴旋转yaw,然后绕新的Y轴旋转pitch,最后绕新的X轴旋转roll。这时候,对应的旋转矩阵应该是R = R_z * R_y * R_x?或者相反? 这个可能需要查阅Eigen的文档或者欧拉角转换的正确方法。例如,在Eigen中,有一个函数Eigen::AngleAxisf(roll, Eigen::Vector3f::XAxis()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::YAxis()) * Eigen::AngleAxisf(yaw, Eigen::Vector3f::ZAxis()),这样得到的旋转矩阵是否正确? 或者,正确的顺序应该是yaw(Z)、pitch(Y)、roll(X)的组合,所以对应的旋转矩阵是 R = R_z(yaw) * R_y(pitch) * R_x(roll)。即,先绕Z轴旋转yaw,得到R_z,然后绕原来的Y轴旋转pitch,得到R_y,然后绕原来的X轴旋转roll。或者,是否应该按照内旋或外旋的方式来处理? 这里可能需要更仔细的分析。例如,欧拉角的定义有不同方式,比如内旋和外旋,以及轴的选择(固定轴还是旋转轴)。常见的RPY角是相对于固定轴的,即绕固定坐标系的Z,然后Y,然后X轴旋转。这种情况下,旋转矩阵的组合顺序是R = R_x(roll) * R_y(pitch) * R_z(yaw)。因为固定轴的旋转是相反的顺序。例如,如果先绕Z转,然后Y,然后X,在固定坐标系下,旋转矩阵是按X*Y*Z的顺序相乘。这可能容易出错。 例如,假设RPY的旋转顺序是:绕固定坐标系的X轴旋转roll,然后绕固定坐标系的Y轴旋转pitch,最后绕固定坐标系的Z轴旋转yaw。那么总的旋转矩阵是 R = R_z(yaw) * R_y(pitch) * R_x(roll)。或者反过来? 这个问题可能需要仔细查证。例如,在机器人学中,通常RPY对应的是绕固定轴的X(Roll)、Y(Pitch)、Z(Yaw)轴依次旋转,但这里的顺序是否正确? 或者,可能RPY的定义是绕Z轴旋转(Yaw),然后绕新的Y轴旋转(Pitch),然后绕新的X轴旋转(Roll)。这时候,旋转矩阵的组合顺序是R = R_x(roll) * R_y(pitch) * R_z(yaw),因为每次旋转后的轴是新的轴。这时候,在Eigen中的实现可能需要使用不同的顺序。 这部分容易出错,所以需要明确正确的转换方法。或者,是否有现成的函数可以将RPY转换为旋转矩阵? 例如,在Eigen中,可以使用Eigen::AngleAxisf,然后按照正确的顺序相乘。或者,使用Eigen::Matrix3f::Identity() 旋转顺序是否正确? 例如,正确的转换方式可能是: Eigen::Matrix3f rotation_matrix; rotation_matrix = Eigen::AngleAxisf(yaw, Eigen::Vector3f::ZAxis()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::YAxis()) * Eigen::AngleAxisf(roll, Eigen::Vector3f::XAxis()); 或者顺序相反? 假设RPY是Roll(X轴)、Pitch(Y轴)、Yaw(Z轴),按照这个顺序,旋转顺序是否是先绕X轴,再绕Y轴,再绕Z轴?如果是固定轴的旋转,那么组合顺序是相反的。例如,绕固定轴X旋转roll,然后绕固定轴Y旋转pitch,最后绕固定轴Z旋转yaw,对应的旋转矩阵是 R = R_z(yaw) * R_y(pitch) * R_x(roll)。因为固定轴的旋转矩阵是按相反的顺序相乘的。例如,假设物体先绕固定X轴转,再绕固定Y轴转,这相当于相对于初始坐标系的X,然后Y。此时,旋转矩阵是 R = R_Y * R_X。但是,如果物体先绕X转,然后绕新的Y转,此时旋转矩阵是 R = R_X * R_Y。所以,这取决于旋转是绕固定轴还是绕物体自身的轴。 这可能比较复杂。或者,用户可能期望的RPY顺序是绕自身的轴旋转,即:先绕Z轴转yaw,然后绕新的Y轴转pitch,再绕新的X轴转roll。这种情况下,旋转矩阵的顺序是 R = R_z * R_y * R_x。或者,是否是 R_x * R_y * R_z? 这里可能需要查阅欧拉角的标准定义。例如,在航空航天中,常用的顺序是Z-Y-X,即先绕Z轴转yaw,然后绕Y轴(可能新的Y轴)转pitch,然后绕X轴(新的X轴)转roll。这种情况下,旋转矩阵的组合顺序是R = R_z(yaw) * R_y(pitch) * R_x(roll)。或者,是否应该按照相反的顺序相乘? 例如,假设每个旋转是相对于由前一个旋转产生的坐标系,那么组合顺序是R = R_z * R_y&#39; * R_x&#39;&#39;,其中Y&#39;是旋转后的Y轴。在Eigen中,旋转的组合是按从右到左的顺序进行的。比如,旋转矩阵的组合是 R = R_z * R_y * R_x,这样实际上是先应用R_x,然后 R_y,然后 R_z。但如果用户是按照先Z,然后Y,然后X的顺序旋转,那么相乘的顺序应该是 R = R_x * R_y * R_z? 这似乎有点混乱。这时候可能需要测试或者查阅可靠的资料。 例如,根据维基百科,欧拉角的旋转顺序会影响结果。例如,对于Tait-Bryan角(即RPY),常见的顺序是ZYX,对应yaw, pitch, roll。当使用这个顺序时,旋转矩阵的构造是: R = R_z(yaw) * R_y(pitch) * R_x(roll) 或者,是否是: R = R_x(roll) * R_y(pitch) * R_z(yaw) 这里可能需要用具体的例子来验证。 例如,假设yaw=0,pitch=0,roll=90度。此时,绕X轴旋转90度,对应的旋转矩阵是R_x(90)。此时,最终的旋转应该只是绕X轴旋转90度。而如果顺序是ZYX,那么构造的R应该是 R_z(0)*R_y(0)*R_x(90) = R_x(90),正确。所以,当顺序是ZYX时,旋转矩阵的构造顺序是 Z * Y * X,即R_z * R_y * R_x。这可能正确。 因此,在代码中应该先构造R_z,然后R_y,然后R_x,并将它们相乘,得到最终的旋转矩阵。 所以,正确的转换代码应该是: Eigen::Matrix3f R = (Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) * Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX())).matrix(); 这样得到的旋转矩阵是否正确? 假设是这样的话,那么构造Affine3f时,平移部分设置为点的XYZ坐标,旋转部分设置为这个旋转矩阵。 然后在PCLViewer中,可以使用viewer->addCoordinateSystem(0.1, affine); 其中0.1是坐标系的大小,affine是构造的Affine3f变换。 这样,就可以在对应的位置显示坐标系,方向由RPY确定。 另一个方法是,使用PCL的变换矩阵,将坐标系的位姿应用到viewer中。或者,用户可能需要自己绘制箭头,比如用三个线段分别表示X、Y、Z轴的方向,这时候需要根据旋转矩阵计算每个轴的方向向量,然后从原点(XYZ)出发,沿方向向量绘制线段。 但使用addCoordinateSystem可能更方便。 此外,需要注意PCLViewer中的坐标系方向是否符合用户的需求。默认情况下,红色是X轴,绿色是Y轴,蓝色是Z轴,这可能需要注意。 总结步骤: 1. 创建点云点,类型为pcl::PointXYZ,设置其x、y、z坐标。 2. 将RPY转换为旋转矩阵,使用Eigen的AngleAxis,顺序为Z、Y、X,即先yaw,再pitch,再roll,然后将它们相乘得到旋转矩阵3. 创建Eigen::Affine3f变换,设置平移为点的坐标,旋转为旋转矩阵。 4. 在PCLViewer中添加该坐标系,使用addCoordinateSystem函数。 可能的代码示例: pcl::PointXYZ point; point.x = x_coord; point.y = y_coord; point.z = z_coord; float roll = ...; float pitch = ...; float yaw = ...; Eigen::Quaternionf q = Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) * Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX()); Eigen::Affine3f transform = Eigen::Affine3f::Identity(); transform.translation() << point.x, point.y, point.z; transform.rotate(q); viewer->addCoordinateSystem(0.1, transform, "coordinate_system_id"); 或者,也可以将旋转矩阵直接作为旋转部分: Eigen::Matrix3f rotation_matrix = (Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) * Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX())).matrix(); transform.linear() = rotation_matrix; 这样是否正确? 是的,Affine3f的linear()方法设置旋转部分,translation()设置平移部分。 所以,最终的代码大致如此。 另外,用户可能需要将点添加到点云中,并在Viewer中显示点云,但问题可能仅涉及如何表示该点的位姿,所以可能只需要添加坐标系即可。 可能需要注意的问题包括: - 欧拉角的单位是否是弧度,用户提供的角度是否是弧度或者需要转换为弧度。 - 旋转顺序是否正确,如果用户使用的RPY顺序代码中的顺序不同,结果会错误。 - Affine3f的构造是否正确,特别是平移和旋转部分是否设置正确。 - 在Viewer中添加坐标系时,是否使用了正确的变换和参数。 另外,PCLViewer的addCoordinateSystem函数可能有一个重载版本,可以直接接受位置和四元数,但可能更方便的是使用Affine3f。 例如,在PCL的文档中,addCoordinateSystem的原型是: bool pcl::visualization::PCLVisualizer::addCoordinateSystem (double scale, const Eigen::Affine3f & t, const std::string & id = "coordinate", int viewport = 0) 所以,正确的方式是传入构造好的Affine3f变换。 总结,回答用户的问题时,需要分步骤说明如何将XYZ和RPY转换为PCL中的点和旋转表示,并如何在Viewer中绘制。同时,需要给出代码示例,并提醒注意旋转顺序和单位的问题。</think>在PCL中表示一个点的位置(XYZ)和姿态(RPY角),并可视化其坐标系,可以按照以下步骤实现: --- ### **1. 表示XYZ和RPY** #### **点的位置(XYZ)** 使用PCL的`pcl::PointXYZ`结构体直接存储坐标: ```cpp pcl::PointXYZ point; point.x = 1.0; // X坐标 point.y = 2.0; // Y坐标 point.z = 3.0; // Z坐标 ``` #### **姿态(RPY角 → 旋转矩阵)** RPY(Roll, Pitch, Yaw)需要转换为旋转矩阵或四元数。使用Eigen库进行转换: ```cpp #include <Eigen/Geometry> float roll = 0.5; // 绕X轴的旋转(弧度) float pitch = 0.3; // 绕Y轴的旋转(弧度) float yaw = 1.2; // 绕Z轴的旋转(弧度) // 将RPY转换为旋转矩阵(Z-Y-X顺序:先Yaw,再Pitch,最后Roll) Eigen::Matrix3f rotation_matrix = (Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) * Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX())).toRotationMatrix(); ``` --- ### **2. 构造位姿变换矩阵** 将位置和姿态组合成一个`Eigen::Affine3f`变换矩阵: ```cpp Eigen::Affine3f transform = Eigen::Affine3f::Identity(); transform.translation
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值