当前位置: 首页 > news >正文

C++/Python:罗德里格斯旋转矩阵

背景:给定两个向量V1,V2,怎么求之间的转换矩阵?

[羽量级公司发月饼了,比去年强,手提式的了\dog]


1、求旋转轴

通过V1和V2的叉积,可以知道旋转轴Vcross(垂直屏幕指向外侧或内侧的向量)

vcross = np.cross(v1, v2)

这里还可以将旋转轴向量单位化,方便后面使用

vcross_normalize = 1 / np.linalg.norm(vcross) * vcross

 2、求旋转角度

还可以知道两向量的夹角(这个夹角是沿着旋转轴的角度,因为两向量确定了一个平面,而旋转轴是垂直平面的)

v1\cdot v2=\left | v1 \right |\left | v2 \right |cos(\theta )

\theta =arccos[(v1\cdot v2)/\left | v1 \right |\left | v2 \right |]

theta = math.acos(v1.dot(v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)))

 3、求旋转矩阵

知道了旋转轴以及旋转角度,就可以祭出罗德里格斯(Rodriguez)公式了,可以直接代入得出旋转矩阵

R_{vcrossnorm}(\theta )=\begin{bmatrix} cos(\theta )+v_{x}^{2} (1-cos(\theta ))&v_{x}v_{y}(1-cos(\theta))-v_{z}sin(\theta) &v_{y}sin(\theta)+v_{x}v_{z}(1-cos(\theta)) \\ v_{z}sin(\theta)+v_{x}v_{y}(1-cos(\theta))&cos(\theta )+v_{y}^{2} (1-cos(\theta )) & -v_{x}sin(\theta)+v_{y}v_{z}(1-cos(\theta)) \\ -v_{y}sin(\theta)+v_{x}v_{z}(1-cos(\theta)) &v_{x}sin(\theta)+v_{y}v_{z}(1-cos(\theta)) & cos(\theta )+v_{z}^{2} (1-cos(\theta )) \end{bmatrix}

 上式中的v_{x}, v_{y},v_{z}分别指单位化后的旋转轴向量vcross_normalize的xyz

Rot = np.zeros((3, 3), dtype=np.float32)
Rot[0][0] = math.cos(theta)+(1-math.cos(theta))*(vcross_normalize[0]**2)
Rot[0][1] = vcross_normalize[0]*vcross_normalize[1]*(1-math.cos(theta))-vcross_normalize[2]*math.sin(theta)
Rot[0][2] = vcross_normalize[1]*math.sin(theta)+vcross_normalize[0]*vcross_normalize[2]*(1-math.cos(theta))
Rot[1][0] = vcross_normalize[2]*math.sin(theta)+vcross_normalize[0]*vcross_normalize[1]*(1-math.cos(theta))
Rot[1][1] = math.cos(theta)+(1-math.cos(theta))*(vcross_normalize[1]**2)
Rot[1][2] = -vcross_normalize[0] * math.sin(theta) + vcross_normalize[1] * vcross_normalize[2] * (
            1 - math.cos(theta))
Rot[2][0] = -vcross_normalize[1] * math.sin(theta) + vcross_normalize[0] * vcross_normalize[2] * (
        1 - math.cos(theta))
Rot[2][1] = vcross_normalize[0] * math.sin(theta) + vcross_normalize[1] * vcross_normalize[2] * (
        1 - math.cos(theta))
Rot[2][2] = math.cos(theta) + (1 - math.cos(theta)) * (vcross_normalize[2] ** 2)

R = np.matrix(Rot)  # 转为矩阵方便后续运算

这样,就求出了旋转矩阵R。 

4、验证

毕竟这里没有推导,拿过来就用,多少有点犯嘀咕,那就验证一下吧。

旋转后的点=R*旋转前的点

旋转前的点 = R^{^{-1}}*旋转后的点

预期效果:

 假如在水平方向有一条线,所有点的y坐标都相同,顺时针旋转45度后,所有点的y坐标应该是一个等差的,斜率为-1。(此时的两个向量v1,v2分别取(0,1,0)和(1,1,0),刚好是45度)

测试结果:

import matplotlib.pyplot as plt
import mpl_toolkits.axisartist as axisartist
import cv2
import math
import numpy as np

norm_wall = np.array([0, 1, 0],dtype=np.float32)
norm_pro = np.array([1,1,0],dtype=np.float32)

theta = math.acos(norm_wall.dot(norm_pro)/(np.linalg.norm(norm_wall)*np.linalg.norm(norm_pro)))
vcross = np.cross(norm_wall, norm_pro)
print(vcross)
vcross_normalize = 1 / np.linalg.norm(vcross) * vcross
print(vcross_normalize)
Rot = np.zeros((3, 3), dtype=np.float32)
Rot[0][0] = math.cos(theta)+(1-math.cos(theta))*(vcross_normalize[0]**2)
Rot[0][1] = vcross_normalize[0]*vcross_normalize[1]*(1-math.cos(theta))-vcross_normalize[2]*math.sin(theta)
Rot[0][2] = vcross_normalize[1]*math.sin(theta)+vcross_normalize[0]*vcross_normalize[2]*(1-math.cos(theta))
Rot[1][0] = vcross_normalize[2]*math.sin(theta)+vcross_normalize[0]*vcross_normalize[1]*(1-math.cos(theta))
Rot[1][1] = math.cos(theta)+(1-math.cos(theta))*(vcross_normalize[1]**2)
Rot[1][2] = -vcross_normalize[0] * math.sin(theta) + vcross_normalize[1] * vcross_normalize[2] * (
            1 - math.cos(theta))
Rot[2][0] = -vcross_normalize[1] * math.sin(theta) + vcross_normalize[0] * vcross_normalize[2] * (
        1 - math.cos(theta))
Rot[2][1] = vcross_normalize[0] * math.sin(theta) + vcross_normalize[1] * vcross_normalize[2] * (
        1 - math.cos(theta))
Rot[2][2] = math.cos(theta) + (1 - math.cos(theta)) * (vcross_normalize[2] ** 2)

R = np.matrix(Rot)

p_arr = []
for i in range(20):
    x = i-10
    y = 10
    p_arr.append([x,y,0])

p_out = []
for p in p_arr:
    tmp = R*np.matrix(p).T
    p_out.append(tmp)

################
fig = plt.figure(figsize=(80, 80))  # 创建画布
#使用axisartist.Subplot方法创建一个绘图区对象ax
ax = axisartist.Subplot(fig, 111)  # 111 代表1行1列的第1个,subplot()可以用于绘制多个子图
fig.add_axes(ax)  # 将绘图区对象添加到画布中
# ----------2. 绘制带箭头的x-y坐标轴#通过set_visible方法设置绘图区所有坐标轴隐藏-------
ax.axis[:].set_visible(False)  # 隐藏了四周的方框
#ax.new_floating_axis代表添加新的坐标轴
ax.axis["x"] = ax.new_floating_axis(0,0)
ax.axis["x"].set_axisline_style("->", size = 1.0)  # 给x坐标轴加上箭头
ax.axis["y"] = ax.new_floating_axis(1,0)  # 添加y坐标轴,且加上箭头
ax.axis["y"].set_axisline_style("-|>", size = 1.0)
#设置x、y轴上刻度显示方向
ax.axis["x"].set_axis_direction("top")
ax.axis["y"].set_axis_direction("right")
##################

##画原始点
p_arr = np.array(p_arr)
plt.plot(p_arr[:,0],p_arr[:,1],color='red')
''' 设置x轴的刻度:plt.xlim() '''
plt.xlim(-20,20)   # 设置x轴的刻度从-2到12
''' 设置y轴的刻度:plt.ylim() '''
plt.ylim(-2,20)    # 设置x轴的刻度从2到10
##画旋转后的点
p_out = np.array(p_out)
plt.plot(p_out[:,0],p_out[:,1],color='blue')
plt.show()

 5、验证2

上面是正向验证,在反向验证一下,给一些斜线上的点比如y=-0.5x+10,将其旋转到水平(即y轴相等),可以设置V1=(0,1,0),V2=(0.5,1,0),计算的R旋转矩阵还是从V1向V2旋转,此时想将旋转后的点变换到旋转前,需要使用R的逆(这里旋转矩阵应该是正交矩阵,转置即为逆)

旋转后的点=R*旋转前的点

旋转前的点 = R^{^{-1}}*旋转后的点

 预期效果:

测试结果:

p_out = []
for p in p_arr:
    tmp = R.I*np.matrix(p).T
    p_out.append(tmp)

最后贴一个根据两个向量计算它们之间的旋转矩阵 - 朔月の流光 - 博客园

 


 

--end--

相关文章:

  • c++征途 --- STL初识
  • 学习编程的第二十三天
  • 上交所技术——2020春招应用开发工程师(Java)笔试
  • 猿创征文|时间序列分析算法之二次指数平滑法和三次指数平滑法详解+Python代码实现
  • 基于人工兔优化算法的函数寻优和工程优化
  • 网络安全无小事, 所有艾思运维人员, 在nginx中必须对thinkphp的目录做以下安全设置, 未尽目录请自行添加
  • Shiro 权限绕过漏洞(CVE-2020-1957)
  • 【python脚本】用于生成简单握手接口与自测环境的gen_uvm_agent脚本
  • Java多线程下——各类锁的详解
  • vue——VM对象和基础指令
  • 手把手带你刷好题(牛客刷题②)
  • 【web-攻击用户】(9.7.1)本地隐私攻击:持久性cookie、缓存Web内容、浏览历史记录、Flash本地共享对象……
  • Linux shell 内建命令
  • 计算机网络 --- TCP与UDP协议
  • Redis缓存的连环炮面试题
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • canvas 五子棋游戏
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • golang中接口赋值与方法集
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 开源SQL-on-Hadoop系统一览
  • 聊聊flink的BlobWriter
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何设计一个比特币钱包服务
  • 数组的操作
  • 用jquery写贪吃蛇
  • ​ssh免密码登录设置及问题总结
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (39)STM32——FLASH闪存
  • (WSI分类)WSI分类文献小综述 2024
  • (待修改)PyG安装步骤
  • (过滤器)Filter和(监听器)listener
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)Linq学习笔记
  • (转)Scala的“=”符号简介
  • (转)母版页和相对路径
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .Net MVC + EF搭建学生管理系统
  • .net MySql
  • .NET 命令行参数包含应用程序路径吗?
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .NET 中 GetProcess 相关方法的性能
  • .Net6使用WebSocket与前端进行通信
  • .NetCore部署微服务(二)
  • .net连接oracle数据库
  • .Net通用分页类(存储过程分页版,可以选择页码的显示样式,且有中英选择)