[学习] 笛卡尔坐标系的任意移动与旋转详解

笛卡尔坐标系的任意移动与旋转详解

文章目录

      • 笛卡尔坐标系的任意移动与旋转详解
        • **1. 笛卡尔坐标系基础**
        • **2. 坐标变换原理**
          • **2.1 平移变换**
          • **2.2 旋转变换**
        • **3. 组合变换**
      • Python仿真与动态展示
        • **动画说明**:
        • **关键数学原理**:

1. 笛卡尔坐标系基础

笛卡尔坐标系用(x,y)(x,y)(x,y)表示平面内任意点的位置,原点为(0,0)(0,0)(0,0)。几何图形可视为点的集合。

2. 坐标变换原理
2.1 平移变换

将图形整体移动(tx,ty)(t_x, t_y)(tx,ty)
{x′=x+txy′=y+ty\begin{cases} x' = x + t_x \\ y' = y + t_y \end{cases}{x=x+txy=y+ty

矩阵形式
[x′y′]=[xy]+[txty]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} t_x \\ t_y \end{bmatrix} [xy]=[xy]+[txty]

2.2 旋转变换
  • 绕原点旋转(逆时针为正方向):
    {x′=xcos⁡θ−ysin⁡θy′=xsin⁡θ+ycos⁡θ\begin{cases} x' = x \cos \theta - y \sin \theta \\ y' = x \sin \theta + y \cos \theta \end{cases}{x=xcosθysinθy=xsinθ+ycosθ

    矩阵形式
    [x′y′]=[cos⁡θ−sin⁡θsin⁡θcos⁡θ][xy]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} [xy]=[cosθsinθsinθcosθ][xy]

  • 绕任意点(cx,cy)(c_x, c_y)(cx,cy)旋转

    1. 平移使旋转中心到原点
    2. 绕原点旋转
    3. 平移回原位置
      [x′y′]=[cos⁡θ−sin⁡θsin⁡θcos⁡θ][x−cxy−cy]+[cxcy]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix} \begin{bmatrix} x - c_x \\ y - c_y \end{bmatrix} + \begin{bmatrix} c_x \\ c_y \end{bmatrix} [xy]=[cosθsinθsinθcosθ][xcxycy]+[cxcy]
3. 组合变换

复杂变换可分解为平移与旋转的序列操作,矩阵乘法满足结合律:
Ttotal=Ttranslate⋅RrotateT_{\text{total}} = T_{\text{translate}} \cdot R_{\text{rotate}}Ttotal=TtranslateRrotate


Python仿真与动态展示

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.patches import Polygon
import matplotlib# 设置全局字体大小
matplotlib.rcParams.update({'font.size': 12})# 定义三角形顶点 (齐次坐标)
triangle = np.array([[0, 0, 1], [1, 0, 1], [0.5, 1, 1]])# 变换矩阵函数
def translation_matrix(tx, ty):return np.array([[1, 0, tx],[0, 1, ty],[0, 0, 1]])def rotation_matrix(theta):c, s = np.cos(theta), np.sin(theta)return np.array([[c, -s, 0],[s, c, 0],[0, 0, 1]])def rotation_around_point(theta, cx, cy):T1 = translation_matrix(-cx, -cy)R = rotation_matrix(theta)T2 = translation_matrix(cx, cy)return T2 @ R @ T1# 创建三个独立的画布
fig1, ax1 = plt.subplots(figsize=(8, 6))
fig2, ax2 = plt.subplots(figsize=(8, 6))
fig3, ax3 = plt.subplots(figsize=(8, 6))# 设置每个画布的标题和坐标范围
fig1.suptitle('平移变换演示', fontsize=16)
fig2.suptitle('绕原点旋转变换演示', fontsize=16)
fig3.suptitle('绕任意点旋转变换演示', fontsize=16)# 设置坐标范围
for ax in [ax1, ax2, ax3]:ax.set_xlim(-3, 4)ax.set_ylim(-3, 4)ax.grid(True, linestyle='--', alpha=0.7)ax.set_aspect('equal')ax.set_xlabel('X轴')ax.set_ylabel('Y轴')# 初始化每个画布的三角形
triangle1 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)
triangle2 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)
triangle3 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)ax1.add_patch(triangle1)
ax2.add_patch(triangle2)
ax3.add_patch(triangle3)# 添加顶点标签
def add_vertex_labels(ax, vertices):ax.text(vertices[0, 0], vertices[0, 1], 'A', fontsize=14, ha='right', va='bottom', weight='bold', color='darkred', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))ax.text(vertices[1, 0], vertices[1, 1], 'B', fontsize=14, ha='left', va='bottom', weight='bold', color='darkgreen', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))ax.text(vertices[2, 0], vertices[2, 1], 'C', fontsize=14, ha='center', va='top', weight='bold', color='darkblue', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))add_vertex_labels(ax1, triangle)
add_vertex_labels(ax2, triangle)
add_vertex_labels(ax3, triangle)# 创建轨迹线 (每个顶点一种颜色)
trail_length = 50  # 拖尾轨迹长度# 使用渐变色轨迹
traj1_A, = ax1.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj1_B, = ax1.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj1_C, = ax1.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax1.legend(title='顶点轨迹', loc='upper right', fontsize=10)traj2_A, = ax2.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj2_B, = ax2.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj2_C, = ax2.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax2.legend(title='顶点轨迹', loc='upper right', fontsize=10)traj3_A, = ax3.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj3_B, = ax3.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj3_C, = ax3.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax3.legend(title='顶点轨迹', loc='upper right', fontsize=10)# 存储轨迹数据
traj_data1 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}
traj_data2 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}
traj_data3 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}# 平移动画更新函数
def update_translation(frame):# 平移参数tx = frame * 0.1ty = frame * 0.05M = translation_matrix(tx, ty)# 应用变换transformed = (M @ triangle.T).T# 更新三角形triangle1.set_xy(transformed[:, :2])# 更新轨迹数据 - 拖尾方式,保留最近50个点# 顶点Atraj_data1['A']['x'].append(transformed[0, 0])traj_data1['A']['y'].append(transformed[0, 1])if len(traj_data1['A']['x']) > trail_length:traj_data1['A']['x'] = traj_data1['A']['x'][-trail_length:]traj_data1['A']['y'] = traj_data1['A']['y'][-trail_length:]# 顶点Btraj_data1['B']['x'].append(transformed[1, 0])traj_data1['B']['y'].append(transformed[1, 1])if len(traj_data1['B']['x']) > trail_length:traj_data1['B']['x'] = traj_data1['B']['x'][-trail_length:]traj_data1['B']['y'] = traj_data1['B']['y'][-trail_length:]# 顶点Ctraj_data1['C']['x'].append(transformed[2, 0])traj_data1['C']['y'].append(transformed[2, 1])if len(traj_data1['C']['x']) > trail_length:traj_data1['C']['x'] = traj_data1['C']['x'][-trail_length:]traj_data1['C']['y'] = traj_data1['C']['y'][-trail_length:]# 更新轨迹线traj1_A.set_data(traj_data1['A']['x'], traj_data1['A']['y'])traj1_B.set_data(traj_data1['B']['x'], traj_data1['B']['y'])traj1_C.set_data(traj_data1['C']['x'], traj_data1['C']['y'])# 更新顶点标签位置ax1.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax1.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax1.texts[2].set_position((transformed[2, 0], transformed[2, 1]))ax1.set_title(f"平移: tx={tx:.1f}, ty={ty:.1f}", fontsize=14)return triangle1, traj1_A, traj1_B, traj1_C# 绕原点旋转动画更新函数(360°持续旋转)
def update_rotation_origin(frame):# 旋转参数 - 持续旋转theta = frame * 0.05  # 每帧增加0.05弧度# 计算旋转矩阵M = rotation_matrix(theta)# 应用变换transformed = (M @ triangle.T).T# 更新三角形triangle2.set_xy(transformed[:, :2])# 更新轨迹数据 - 拖尾方式,保留最近50个点# 顶点Atraj_data2['A']['x'].append(transformed[0, 0])traj_data2['A']['y'].append(transformed[0, 1])if len(traj_data2['A']['x']) > trail_length:traj_data2['A']['x'] = traj_data2['A']['x'][-trail_length:]traj_data2['A']['y'] = traj_data2['A']['y'][-trail_length:]# 顶点Btraj_data2['B']['x'].append(transformed[1, 0])traj_data2['B']['y'].append(transformed[1, 1])if len(traj_data2['B']['x']) > trail_length:traj_data2['B']['x'] = traj_data2['B']['x'][-trail_length:]traj_data2['B']['y'] = traj_data2['B']['y'][-trail_length:]# 顶点Ctraj_data2['C']['x'].append(transformed[2, 0])traj_data2['C']['y'].append(transformed[2, 1])if len(traj_data2['C']['x']) > trail_length:traj_data2['C']['x'] = traj_data2['C']['x'][-trail_length:]traj_data2['C']['y'] = traj_data2['C']['y'][-trail_length:]# 更新轨迹线traj2_A.set_data(traj_data2['A']['x'], traj_data2['A']['y'])traj2_B.set_data(traj_data2['B']['x'], traj_data2['B']['y'])traj2_C.set_data(traj_data2['C']['x'], traj_data2['C']['y'])# 更新顶点标签位置ax2.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax2.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax2.texts[2].set_position((transformed[2, 0], transformed[2, 1]))# 显示当前角度(转换为度数)degrees = np.degrees(theta) % 360ax2.set_title(f"绕原点旋转: {degrees:.0f}°", fontsize=14)return triangle2, traj2_A, traj2_B, traj2_C# 绕任意点旋转动画更新函数(360°持续旋转)
def update_rotation_arbitrary(frame):# 旋转参数 - 持续旋转theta = frame * 0.05  # 每帧增加0.05弧度cx, cy = 1.5, 0.5  # 旋转中心# 标记旋转中心(只在第一帧标记)if frame == 0:ax3.plot(cx, cy, 'ro', markersize=10, alpha=0.9)ax3.text(cx, cy+0.4, '旋转中心', ha='center', va='bottom', fontsize=12, weight='bold', color='red',bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))# 计算绕任意点旋转的变换矩阵M = rotation_around_point(theta, cx, cy)# 应用变换transformed = (M @ triangle.T).T# 更新三角形triangle3.set_xy(transformed[:, :2])# 更新轨迹数据 - 拖尾方式,保留最近50个点# 顶点Atraj_data3['A']['x'].append(transformed[0, 0])traj_data3['A']['y'].append(transformed[0, 1])if len(traj_data3['A']['x']) > trail_length:traj_data3['A']['x'] = traj_data3['A']['x'][-trail_length:]traj_data3['A']['y'] = traj_data3['A']['y'][-trail_length:]# 顶点Btraj_data3['B']['x'].append(transformed[1, 0])traj_data3['B']['y'].append(transformed[1, 1])if len(traj_data3['B']['x']) > trail_length:traj_data3['B']['x'] = traj_data3['B']['x'][-trail_length:]traj_data3['B']['y'] = traj_data3['B']['y'][-trail_length:]# 顶点Ctraj_data3['C']['x'].append(transformed[2, 0])traj_data3['C']['y'].append(transformed[2, 1])if len(traj_data3['C']['x']) > trail_length:traj_data3['C']['x'] = traj_data3['C']['x'][-trail_length:]traj_data3['C']['y'] = traj_data3['C']['y'][-trail_length:]# 更新轨迹线traj3_A.set_data(traj_data3['A']['x'], traj_data3['A']['y'])traj3_B.set_data(traj_data3['B']['x'], traj_data3['B']['y'])traj3_C.set_data(traj_data3['C']['x'], traj_data3['C']['y'])# 更新顶点标签位置ax3.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax3.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax3.texts[2].set_position((transformed[2, 0], transformed[2, 1]))# 显示当前角度(转换为度数)degrees = np.degrees(theta) % 360ax3.set_title(f"绕点({cx},{cy})旋转: {degrees:.0f}°", fontsize=14)return triangle3, traj3_A, traj3_B, traj3_C# 创建三个独立的动画
ani1 = FuncAnimation(fig1, update_translation, frames=30, interval=50, blit=True)
# 旋转动画设置为无限循环
ani2 = FuncAnimation(fig2, update_rotation_origin, frames=200, interval=50, blit=True, repeat=True)
ani3 = FuncAnimation(fig3, update_rotation_arbitrary, frames=200, interval=50, blit=True, repeat=True)# 调整窗口位置避免重叠
fig1.canvas.manager.window.wm_geometry("+100+100")
fig2.canvas.manager.window.wm_geometry("+600+100")
fig3.canvas.manager.window.wm_geometry("+1100+100")plt.tight_layout()
plt.show()
动画说明
  1. 平移阶段(0-30帧):三角形沿向量(2,1.5)(2,1.5)(2,1.5)移动
    在这里插入图片描述

  2. 绕原点旋转(31-60帧):三角形绕(0,0)(0,0)(0,0)逆时针旋转360°
    在这里插入图片描述

  3. 绕任意点旋转(61-90帧):三角形绕红点(1.5,0.8)(1.5,0.8)(1.5,0.8)逆时针旋转360°
    在这里插入图片描述

关键数学原理
  • 平移是向量加法
  • 旋转是矩阵乘法
  • 绕任意点旋转 = 平移至原点 → 旋转 → 平移回原位

通过组合基本变换,可实现复杂刚体运动仿真,广泛应用于机器人学、计算机图形学等领域。


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


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

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

相关文章

论文笔记:Parameter Competition Balancing for Model Merging

neurips 20241 intro近年来,模型融合(model merging)技术迅速发展,使得可以将多个分别针对不同任务微调后的模型直接集成为一个统一模型,从而实现多任务处理能力,而无需重新访问原始训练数据。然而&#xf…

逆向难度真相:仅用IDA静态分析的极限挑战

逆向难度真相:仅用IDA静态分析的极限挑战 纯IDA逆向难度重排(从难到易) Python > Go > Java > E语言 > CPython (地狱级难度) IDA困境: 主逻辑完全封装在PYZ/PYC资源中,IDA无法解析字节码结构字符串表只显…

vxe-table 通过配置 ajax 方式自动请求数据,适用于简单场景的列表

vxe-table 通过配置 ajax 方式自动请求数据,适用于简单场景的列表 当系统中很多页面都是简单列表时,每次都要手动去请求接口后再赋值,过程就会比较冗余繁琐。解决方式一般就是将封装一下。本章的方式是通过 vxe-grid 配置 ajax 来实现自动请求…

Zabbix 企业级分布式监控系统深度解析

一、监控系统核心认知1.1 监控的本质与价值监控(Monitoring)的核心是 “检测与预防”,在 IT 运维中占据约 30% 的权重。其核心价值体现在:风险预判:通过实时监测指标异常,提前发现潜在故障(如服…

使用 .NET 6.0 的简单 WebSocket 客户端和服务器应用程序

几个月前,有同事来找我,问能否用 .NET 创建一个简单的 WebSocket 服务器(以及之后的客户端)。据我了解,他想用它来控制对方电脑上的进程。或许对其他人也有用,所以我把它发布在这里。让我们从服务器开始。我…

【ASP.NET Core】ASP.NET Core中Redis分布式缓存的应用

系列文章目录 链接: 【ASP.NET Core】REST与RESTful详解,从理论到实现 链接: 【ASP.NET Core】深入理解Controller的工作机制 链接: 【ASP.NET Core】内存缓存(MemoryCache)原理、应用及常见问题解析 文章目录系列文章目录前言一、Redis1.1 …

5.6 指令流水线 (答案见原书 P267)

5.6 指令流水线 (答案见原书 P267) 01. 下列关于流水CPU基本概念的描述中,正确的是( D )。 题目原文 下列关于流水CPU基本概念的描述中,正确的是( )。 A. 流水CPU是以空间并行性为原理构造的处理器 B. 流水…

NIO简单介绍和运用

NIO简单介 NIO 非阻塞IO模型,基于缓冲区(Buffer)读写数据,读写后的数据通过通道(Channel)进行传输,采用选择器(Selector)管理多个通道从而实现高并发。 核心组件:1. Buffer 为一个内存数组作为数据容器,代替传统的Inpu…

LeetCode 658.找到K个最接近的元素

给定一个 排序好 的数组 arr &#xff0c;两个整数 k 和 x &#xff0c;从数组中找到最靠近 x&#xff08;两数之差最小&#xff09;的 k 个数。返回的结果必须要是按升序排好的。 整数 a 比整数 b 更接近 x 需要满足&#xff1a; |a - x| < |b - x| 或者 |a - x| |b - x| …

制作一款打飞机游戏83:炸弹机制

游戏中的炸弹系统&#xff0c;包括以下核心功能&#xff1a;炸弹爆炸效果与动画实现炸弹伤害范围判定机制子弹转化为能量道具的系统炸弹使用时的无敌帧处理各种边界情况的修复与优化技术实现细节1. 炸弹基础系统‌炸弹动画状态机‌&#xff1a; 我们采用三阶段状态机控制炸弹效…

Linux CentOS 虚拟机升级内核至4.x以上版本

1、安装组件 yum install -y wget && yum install -y net-tools yum groupinstall “Development Tools” yum install ncurses-devel bc openssl-devel elfutils-libelf-devel yum install -y ncurses-devel yum install -y elfutils-libelf-devel yum install -y ope…

QT跨平台应用程序开发框架(11)—— Qt系统相关

目录 一&#xff0c;事件 1.1 关于事件 1.2 处理事件 1.3 处理鼠标事件 1.3.1 点击事件 1.3.2 释放事件 1.3.3 双击事件 1.3.4 滚轮事件 1.3.5 注意事项 1.4 处理键盘事件 1.5 定时器事件 1.6 窗口移动和大小改变事件 二&#xff0c;文件操作 2.1 文件操作概述 2.2 QFile 介绍…

sqli-labs通关笔记-第11关 POST字符型注入(单引号闭合 手工注入+脚本注入两种方法)

目录 一、字符型注入 二、limit函数 三、GET方法与POST方法 四、源码分析 1、代码审计 2、SQL注入安全分析 五、渗透实战 1、进入靶场 2、注入点分析 &#xff08;1&#xff09;SQL语句 &#xff08;2&#xff09;万能密码登录 3、手工注入 &#xff08;1&#xf…

网络安全基础作业三

回顾web前端的代码<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用户登录</title><st…

基于单片机的温湿度报警系统设计与实现

摘 要 本项研究对温湿度警报系统的需求进行了详尽分析&#xff0c;并成功研制出一套以单片机为技术核心的温湿度警报系统。该系统由硬件搭建和软件编程两大模块构成。在硬件搭建方面&#xff0c;系统整合了STM32主控芯片、DS18B20温度传感器、湿敏电阻、按键组件、OLED显示屏、…

(八)复习(拆分微服务)

文章目录项目地址一、Ticketing模块拆分1.1 创建web api1. 添加引用2. 添加需要的包和配置3. program.cs4. docker-compose修改项目地址 教程作者&#xff1a;教程地址&#xff1a; 代码仓库地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow一、Ticketing模块拆…

DearMom以“新生儿安全系统”重塑婴儿车价值,揽获CBME双项大奖

7月16日&#xff0c;在刚刚开幕的2025 CBME中国孕婴童展上&#xff0c;备受瞩目的CBME中国孕婴童产业奖正式揭晓。深耕婴儿车品类的专业品牌DearMom&#xff0c;凭借其卓越的创新实力与对新生儿安全出行的深刻洞察&#xff0c;一举摘得重量级奖项——“杰出品牌创新奖”。同时&…

瀚高数据库开启Oracle兼容模块

文章目录环境症状问题原因解决方案环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 症状 不能使用Oracle兼容&#xff1b; 问题原因 在瀚高数据库V45中oracle兼容模块需要单独开启默认是关闭状态。 解决方案 使用sysdba执行修改…

final修饰符不可变的底层

final修饰符的底层原理在 Java 中&#xff0c;final 修饰符的底层实现涉及 编译器优化 和 JVM 字节码层面的约束其核心目标是保证被修饰元素的【不可变性】或 【不可重写 / 继承性】一、final 修饰类&#xff1a;禁止继承的底层约束当一个类被 final 修饰时&#xff0c;例如 St…

如何排查服务器 CPU 飙高

服务器 CPU 飙高&#xff08;CPU 使用率持续超过 80% 甚至接近 100%&#xff09;是典型的性能瓶颈问题&#xff0c;可能由应用逻辑缺陷、资源竞争、外部压力或硬件/系统异常引起。以下是系统化的排查步骤&#xff0c;覆盖从现象确认到根因定位的全流程。​一、确认 CPU 飙高的现…