You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Scipy四元数的ROV六自由度旋转轴一致性问题咨询

遥控水下机器人(ROV)模拟器旋转轴一致性问题

背景与已解决问题

我正在开发一款简易ROV模拟器,计划用scipy的四元数实现旋转逻辑,替代之前手动用三角函数构造旋转矩阵的方案(即通用3D旋转矩阵实现方式)。

此前已解决一个问题:代码初期运行正常,但多次变换后旋转不再绕本体轴进行,推测是手动构造矩阵的误差累积导致,改用scipy四元数后该问题解决。

当前待解决问题

当前存在旋转轴不一致的问题:偏航(yaw)旋转围绕全局Z轴进行,而横滚(roll)、俯仰(pitch)围绕ROV本体X、Y轴进行,如何实现所有旋转都围绕本体轴进行,保证旋转轴的一致性?

实现代码

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.widgets import Slider
import numpy as np
from scipy.spatial.transform import Rotation

class RovTemp(object):
    def __init__(self):
        # 全局坐标系下的旋转矩阵
        self.vehicleAxes = np.eye(3)
        # 当前横滚、俯仰、偏航角度
        self.rotation_angles = np.zeros(3)
        # 拆解本体坐标系的三个单位向量,方便调用
        self.iHat, self.jHat, self.kHat = self.getCoordSystem()

    def getCoordSystem(self):
        # 返回iHat, jHat, kHat
        return self.vehicleAxes.T

    def computeRollPitchYaw(self):
        # 计算全局坐标系下的横滚、俯仰、偏航角度
        roll = np.arctan2(self.kHat[1], self.kHat[2])
        pitch = np.arctan2(-self.kHat[0], np.sqrt(self.kHat[1]**2 + self.kHat[2]**2))
        yaw = np.arctan2(self.jHat[0], self.iHat[0])
        return np.array([roll, pitch, yaw])

    def updateMovingCoordSystem(self, rotation_angles):
        # 存储当前姿态角度
        self.rotation_angles = rotation_angles
        # 从(横滚、俯仰、偏航)角度创建四元数并转换为旋转矩阵
        self.vehicleAxes = Rotation.from_euler('xyz', rotation_angles, degrees=False).as_matrix()
        # 更新本体坐标系向量
        self.iHat, self.jHat, self.kHat = self.getCoordSystem()

rov = RovTemp()

# 绘制姿态
fig  = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_aspect("equal")
plt.subplots_adjust(top=0.95, bottom=0.15)
lim = 0.5
ax.set_xlim((-lim, lim))
ax.set_ylim((-lim, lim))
ax.set_zlim((-lim, lim))

def plotCoordSystem(ax, iHat, jHat, kHat, x0=np.zeros(3), ds=0.45, ls="-"):
    x1 = x0 + iHat*ds
    x2 = x0 + jHat*ds
    x3 = x0 + kHat*ds
    lns = ax.plot([x0[0], x1[0]], [x0[1], x1[1]], [x0[2], x1[2]], "r", ls=ls, lw=2)
    lns += ax.plot([x0[0], x2[0]], [x0[1], x2[1]], [x0[2], x2[2]], "g", ls=ls, lw=2)
    lns += ax.plot([x0[0], x3[0]], [x0[1], x3[1]], [x0[2], x3[2]], "b", ls=ls, lw=2)
    return lns

# 绘制两次坐标系:一次作为参考(虚线),一次用于更新(实线)
plotCoordSystem(ax, rov.iHat, rov.jHat, rov.kHat, ls="--")
lns = plotCoordSystem(ax, rov.iHat, rov.jHat, rov.kHat)

sldr_ax1 = fig.add_axes([0.15, 0.01, 0.7, 0.025])
sldr_ax2 = fig.add_axes([0.15, 0.05, 0.7, 0.025])
sldr_ax3 = fig.add_axes([0.15, 0.09, 0.7, 0.025])
sldrLim = 180
sldr1 = Slider(sldr_ax1, 'phi', -sldrLim, sldrLim, valinit=0, valfmt="%.1f deg")
sldr2 = Slider(sldr_ax2, 'theta', -sldrLim, sldrLim, valinit=0, valfmt="%.1f deg")
sldr3 = Slider(sldr_ax3, 'psi', -sldrLim, sldrLim, valinit=0, valfmt="%.1f deg")

def onChanged(val):
    global rov, lns, ax
    angles = np.array([sldr1.val, sldr2.val, sldr3.val])/180.*np.pi
    rov.updateMovingCoordSystem(angles)
    for l in lns:
        l.remove()
    lns = plotCoordSystem(ax, rov.iHat, rov.jHat, rov.kHat)
    ax.set_title(
        "roll, pitch, yaw = "+", ".join(['{:.1f} deg'.format(v) for v in rov.computeRollPitchYaw()/np.pi*180.]))
    return lns

sldr1.on_changed(onChanged)
sldr2.on_changed(onChanged)
sldr3.on_changed(onChanged)

plt.show()

问题现象

滑块设置的旋转角度与程序计算并显示的角度不一致,如图所示:
滑块指定的旋转角度与后续计算的角度不一致

内容的提问来源于stack exchange,提问作者Artur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:17:32