利用PyQt簡單的實現一個機器人的關節JOG界面

在上一篇文章中如何在Python用Plot畫出一個簡單的機器人模型,我們介紹了如何在Python中畫出一個簡單的機器人3D模型,但是有的時候我們需要通過界面去控制機器人每一個軸的轉動,并實時的顯示出當前機器人的關節位置和末端笛卡爾位姿。
那么要實現上述功能的話,那么我就可以采用 Pyqt5 來實現,具體代碼如下:


import sys
import numpy as np
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QLabel, QFileDialog, QLineEdit, QFormLayout, QGridLayout, QDialog, QSpinBox, QDialogButtonBox
)
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from PyQt5.QtCore import QTimer
from ShowRobot import Robot, ShowRobot
from scipy.spatial.transform import Rotation as R
from functools import partial# 設置全局字體以支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 設置默認字體為黑體
plt.rcParams['axes.unicode_minus'] = False  # 解決負號顯示問題class SettingsDialog(QDialog):"""設置對話框,用于配置數據格式化規則"""def __init__(self, parent=None):super().__init__(parent)self.setWindowTitle("數據格式化設置")self.initUI()def initUI(self):layout = QFormLayout(self)# 添加小數點位數設置self.decimal_spinbox = QSpinBox(self)self.decimal_spinbox.setRange(0, 6)  # 小數點位數范圍self.decimal_spinbox.setValue(2)  # 默認值layout.addRow("小數點位數:", self.decimal_spinbox)# 添加單位設置self.unit_edit = QLineEdit(self)self.unit_edit.setPlaceholderText("例如:° 或 m")layout.addRow("單位:", self.unit_edit)# 添加確認和取消按鈕buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)buttons.accepted.connect(self.accept)buttons.rejected.connect(self.reject)layout.addRow(buttons)def get_settings(self):"""獲取用戶設置的格式化規則"""return {"decimal_places": self.decimal_spinbox.value(),"unit": self.unit_edit.text().strip(),}class RobotControlApp(QWidget):def __init__(self, robot : Robot, show_robot : ShowRobot):super().__init__()self.data_format = {"decimal_places": 3, "unit": "°"}  # 默認數據格式self.robot = robotself.show_robot = show_robot# 初始化定時器self.timer = QTimer(self)self.timer.timeout.connect(self.on_timer_timeout)self.current_axis = None  # 當前運動的軸self.current_direction = None  # 當前運動的方向self.initUI()def initUI(self):# 設置窗口標題和大小self.setWindowTitle('6軸機器人JOG控制')self.setGeometry(100, 100, 1000, 600)# 創建主布局main_layout = QHBoxLayout()# 左側布局:控制面板control_layout = QVBoxLayout()# 創建標簽self.status_label = QLabel('當前狀態: 停止', self)control_layout.addWidget(self.status_label)# 創建6個軸的JOG控制按鈕self.axis_buttons = []for i in range(6):# axis_label = QLabel(f'軸 {i+1}', self)# control_layout.addWidget(axis_label)button_layout = QHBoxLayout()positive_button = QPushButton(f'J{i+1} +', self)negative_button = QPushButton(f'J{i+1} -', self)# 連接按鈕點擊事件,點擊一次運動一次positive_button.clicked.connect(lambda _, axis=i: self.move_axis(axis, '正向'))negative_button.clicked.connect(lambda _, axis=i: self.move_axis(axis, '負向'))# 連接按鈕按下和松開事件, 使用 partial 綁定參數positive_button.pressed.connect(partial(self.start_motion, axis=i, direction='正向'))positive_button.released.connect(self.stop_motion)negative_button.pressed.connect(partial(self.start_motion, axis=i, direction='負向'))negative_button.released.connect(self.stop_motion)button_layout.addWidget(positive_button)button_layout.addWidget(negative_button)control_layout.addLayout(button_layout)# 添加保存按鈕save_button = QPushButton('保存圖像', self)save_button.clicked.connect(self.save_plot)control_layout.addWidget(save_button)# 添加設置按鈕settings_button = QPushButton('數據格式化設置', self)settings_button.clicked.connect(self.open_settings_dialog)control_layout.addWidget(settings_button)# 添加關節角度顯示控件(一行顯示)joint_layout = QHBoxLayout()self.joint_angle_edits = []for i in range(6):edit = QLineEdit(self)edit.setReadOnly(True)  # 設置為只讀edit.setPlaceholderText(f'關節 {i + 1} 角度')joint_layout.addWidget(edit)self.joint_angle_edits.append(edit)# 添加笛卡爾位置顯示控件(一行顯示)cartesian_layout = QHBoxLayout()self.cartesian_position_edits = []for label in ['X', 'Y', 'Z', 'Rx', 'Ry', 'Rz']:edit = QLineEdit(self)edit.setReadOnly(True)  # 設置為只讀edit.setPlaceholderText(f'{label} 位置')cartesian_layout.addWidget(edit)self.cartesian_position_edits.append(edit)# 將關節角度和笛卡爾位置添加到控制面板control_layout.addLayout(joint_layout)control_layout.addLayout(cartesian_layout)# 將控制面板添加到主布局main_layout.addLayout(control_layout)# 右側布局:3D 繪圖窗口self.figure = Figure()self.canvas = FigureCanvas(self.figure)self.ax = self.figure.add_subplot(111, projection='3d')self.ax.set_title("機器人運動軌跡 (3D)")self.ax.set_xlabel("X 軸")self.ax.set_ylabel("Y 軸")self.ax.set_zlabel("Z 軸")self.ax.grid(True)self.show_robot.ax = self.axself.show_robot.robot = self.robot# 初始化繪圖數據T_start = np.identity(4, dtype= float)T_end = np.identity(4, dtype= float)self.show_robot.ShowFrame(T_start, length=500)for joint_index in range(6):T_start = T_endT = self.robot.DH(joint_index, self.show_robot.q_list[joint_index])T_end = T_end * T# print(T_end)self.show_robot.ShowLink(joint_index, T_start, T_end)self.show_robot.ShowFrame(T_end, length=500)joint_angles = self.show_robot.q_listrotation_matrix = T_end[:3, :3]r = R.from_matrix(rotation_matrix)# 將旋轉矩陣轉換為 XYZ 固定角(Roll-Pitch-Yaw 角)roll, pitch, yaw = r.as_euler('xyz', degrees=True)cartesian_positions = [T_end[0, 3], T_end[1, 3], T_end[2, 3], roll, pitch, yaw]# 更新關節角度顯示for i, edit in enumerate(self.joint_angle_edits):edit.setText(f"{joint_angles[i]:.{self.data_format['decimal_places']}f}{self.data_format['unit']}")# 更新笛卡爾位置顯示for i, edit in enumerate(self.cartesian_position_edits):edit.setText(f"{cartesian_positions[i]:.{self.data_format['decimal_places']}f}")self.ax.set_xlim([-1000, 1000])self.ax.set_ylim([-1000, 1000])self.ax.set_zlim([-1000, 1000])# 將繪圖窗口添加到主布局main_layout.addWidget(self.canvas)# 設置主布局self.setLayout(main_layout)def start_motion(self, axis, direction):"""開始運動"""self.current_axis = axisself.current_direction = directionself.timer.start(100)  # 每 100 毫秒觸發一次def stop_motion(self):"""停止運動"""self.timer.stop()self.current_axis = Noneself.current_direction = Noneself.status_label.setText('當前狀態: 停止')def on_timer_timeout(self):"""定時器觸發時的邏輯"""if self.current_axis is not None and self.current_direction is not None:self.move_axis(self.current_axis, self.current_direction)def move_axis(self, axis, direction):# 這里是控制機器人軸運動的邏輯self.status_label.setText(f'軸 {axis+1} {direction}運動')# 模擬機器人運動并更新繪圖self.update_plot(axis, direction)def update_plot(self, axis, direction):self.ax.cla()  # 清除所有軸# 模擬機器人運動數據if direction == '正向':self.show_robot.q_list[axis] += 1.0elif direction == '負向':self.show_robot.q_list[axis] -= 1.0T_start = np.identity(4, dtype= float)T_end = np.identity(4, dtype= float)self.show_robot.ShowFrame(T_start, length=500)for joint_index in range(6):T_start = T_endT = self.robot.DH(joint_index, self.show_robot.q_list[joint_index])T_end = T_end * T# print(T_end)self.show_robot.ShowLink(joint_index, T_start, T_end)self.show_robot.ShowFrame(T_end, length=500)joint_angles = self.show_robot.q_listrotation_matrix = T_end[:3, :3]r = R.from_matrix(rotation_matrix)# 將旋轉矩陣轉換為 XYZ 固定角(Roll-Pitch-Yaw 角)roll, pitch, yaw = r.as_euler('xyz', degrees=True)cartesian_positions = [T_end[0, 3], T_end[1, 3], T_end[2, 3], roll, pitch, yaw]# 更新關節角度顯示for i, edit in enumerate(self.joint_angle_edits):edit.setText(f"{joint_angles[i]:.{self.data_format['decimal_places']}f}{self.data_format['unit']}")# 更新笛卡爾位置顯示for i, edit in enumerate(self.cartesian_position_edits):edit.setText(f"{cartesian_positions[i]:.{self.data_format['decimal_places']}f}")self.ax.set_xlim([-1000, 1000])self.ax.set_ylim([-1000, 1000])self.ax.set_zlim([-1000, 1000])# 重繪圖self.canvas.draw()def save_plot(self):# 打開文件對話框,讓用戶選擇保存路徑和文件格式options = QFileDialog.Options()file_name, selected_filter = QFileDialog.getSaveFileName(self, "保存圖像", "", "PNG 文件 (*.png);;JPG 文件 (*.jpg);;PDF 文件 (*.pdf)", options=options)if file_name:# 根據用戶選擇的文件擴展名保存圖像if selected_filter == "PNG 文件 (*.png)":self.figure.savefig(file_name, format='png')elif selected_filter == "JPG 文件 (*.jpg)":self.figure.savefig(file_name, format='jpg')elif selected_filter == "PDF 文件 (*.pdf)":self.figure.savefig(file_name, format='pdf')# 更新狀態標簽self.status_label.setText(f'圖像已保存為 {file_name}')def open_settings_dialog(self):"""打開數據格式化設置對話框"""dialog = SettingsDialog(self)if dialog.exec_() == QDialog.Accepted:self.data_format = dialog.get_settings()  # 更新數據格式self.update_joint_and_cartesian_display()  # 更新顯示if __name__ == '__main__':app = QApplication(sys.argv)L1 = 388L2 = 50L3 = 330L4 = 50L5 = 332L6 = 96alpha_list = [90, 0, 90, -90, 90, 0]a_list     = [L2, L3, L4, 0, 0, 0]d_list     = [L1, 0, 0, L5, 0, L6]theta_list = [0, 90, 0, 0, 0, 0]robot = Robot()show_robot = ShowRobot()robot.SetDHParamList(alpha_list, a_list, d_list, theta_list)ex = RobotControlApp(robot, show_robot)ex.show()sys.exit(app.exec_())

在界面中,按下 J1+, 關節1軸就會一直正向轉動,松開 J1+按鈕之后關節1軸就會停止運動,其他關節軸也是一樣的。
然后在界面的下方中還是實時的顯示出機器人當前的關節角度和笛卡爾末端位姿。

最終實現的效果如下:
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72268.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72268.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72268.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iOS 使用消息轉發機制實現多代理功能

在iOS開發中,我們有時候會用到多代理功能,比如我們列表的埋點事件,需要我們在列表的某個特定的時機進行埋點上報,我們當然可以用最常見的做法,就是設置代理實現代理方法,然后在對應的代理方法里面進行上報&…

XGBoost和LightGBM機器學習算法對比及實戰

文章目錄 1. XGBoost 原理核心思想關鍵技術點2. LightGBM 原理核心思想關鍵技術點3. XGBoost vs LightGBM 對比4. 適用場景選擇5. 總結1. 數據準備2. XGBoost 示例安裝庫代碼實現3. LightGBM 示例安裝庫代碼實現4. 關鍵參數對比5. 注意事項6. 輸出示例XGBoost 和 LightGBM 是兩…

局域網自動識別機器名和MAC并生成文件的命令

更新版本:添加了MAC 地址 確定了設備唯一性 V1.1 局域網自動識別機器名和MAC并生成文件的批處理命令 echo off setlocal enabledelayedexpansionREM 設置輸出文件 set outputFilenetwork_info.txtREM 清空或創建輸出文件 echo Scanning network from 192.168.20.1…

基于Python+Vue開發的體育用品商城管理系統源碼+開發文檔+課程作業

項目簡介 該項目是基于PythonVue開發的體育用品商城管理系統(前后端分離),這是一項為大學生課程設計作業而開發的項目。該系統旨在幫助大學生學習并掌握Python編程技能,同時鍛煉他們的項目設計與開發能力。通過學習基于Python的體…

pyQT5簡易教程(一):制作一個可以選擇本地圖片并顯示的桌面應用

可以參考之前的教程安裝 PyQt 和 PyQt Designer https://blog.csdn.net/smx6666668/article/details/145909326?spm=1011.2415.3001.10575&sharefrom=mp_manage_link 一、打開pycharm中的QTdesigner 二、設計界面 和之前一樣,使用 PyQt Designer 來設計界面并保存為 .u…

LeetCode 解題思路 6(Hot 100)

解題思路: 初始化窗口元素: 遍歷前 k 個元素,構建初始單調隊列。若當前索引對應值大于等于隊尾索引對應值,移除隊尾索引,將當前索引加入隊尾。遍歷結束時當前隊頭索引即為當前窗口最大值,將其存入結果數組…

基于redis的位圖實現簽到功能

基于Redis位圖實現簽到功能是一種高效且節省內存的方法。以下是分步實現的詳細方案&#xff1a; 1. 鍵設計策略 采用 sign:<userId>:<YYYYMM> 格式存儲每月簽到數據 # 示例&#xff1a;用戶1001在2023年8月的簽到數據 sign_key "sign:1001:202308"2.…

C++ Qt OpenGL渲染FFmpeg解碼后的視頻

本篇博客介紹使用OpenGL渲染FFmpeg解碼后的視頻,涉及到QOpenGLWidget、QOpenGLFunctions、OpenGL shader以及紋理相關,播放效果如下: 開發環境:Win11 C++ Qt6.8.1、FFmpeg4.0、x64 ??注意:Qt版本不同時,Qt OpenGL API及用法可能差別比較大,FFmpeg版本不同時API調用可能…

deepseek部署:ELK + Filebeat + Zookeeper + Kafka

## 1. 概述 本文檔旨在指導如何在7臺機器上部署ELK&#xff08;Elasticsearch, Logstash, Kibana&#xff09;堆棧、Filebeat、Zookeeper和Kafka。該部署方案適用于日志收集、處理和可視化場景。 ## 2. 環境準備 ### 2.1 機器分配 | 機器編號 | 主機名 | IP地址 | 部署組件 |-…

2.數據結構:1.Tire 字符串統計

1.Tire 字符串統計 #include<algorithm> #include<cstring> #include<iostream>using namespace std;const int N100010; int son[N][26];//至多 N 層&#xff0c;每一層至多 26 個節點&#xff08;字母&#xff09; int cnt[N];//字符串至多 N 個&#xff…

算法(四)——位運算與位圖

文章目錄 位運算、位圖位運算基本位運算異或運算交換兩個數無比較返回最大值缺失的數字唯一出現奇數次的數唯二出現奇數次的數唯一出現次數少于m次的數 位運算進階判斷一個整數是不是2的冪判斷一個整數是不是3的冪大于等于n的最小的2的冪[left, right]內所有數字&的結果反轉…

本地部署deepseek大模型后使用c# winform調用(可離線)

介于最近deepseek的大火&#xff0c;我就在想能不能用winform也玩一玩本地部署&#xff0c;于是經過查閱資料&#xff0c;然后了解到ollama部署deepseek,最后用ollama sharp NUGet包來實現winform調用ollama 部署的deepseek。 本項目使用Vs2022和.net 8.0開發&#xff0c;ollam…

SpringBoot原理-02.自動配置-概述

一.自動配置 所謂自動配置&#xff0c;就是Spring容器啟動后&#xff0c;一些配置類、bean對象就自動存入了IOC容器當中&#xff0c;而不需要我們手動聲明&#xff0c;直接從IOC容器中引入即可。省去了繁瑣的配置操作。 我們可以首先將spring項目啟動起來&#xff0c;里面有一…

P10265 [GESP樣題 七級] 迷宮統計

題目描述 在神秘的幻想?陸中&#xff0c;存在著 n 個古老而神奇的迷宮&#xff0c;迷宮編號從 1 到 n。有的迷宮之間可以直接往返&#xff0c;有的可以?到別的迷宮&#xff0c;但是不能?回來。玩家小楊想挑戰?下不同的迷宮&#xff0c;他決定從 m 號迷宮出發。現在&#x…

Spring框架中的工廠模式

在Spring框架里&#xff0c;工廠模式的運用十分廣泛&#xff0c;它主要幫助我們創建和管理對象&#xff0c;讓對象的創建和使用分離&#xff0c;提高代碼的可維護性和可擴展性。下面為你詳細介紹Spring框架中工廠模式的具體體現和示例&#xff1a; 1. BeanFactory 作為工廠模式…

音視頻-WAV格式

1. WAV格式說明&#xff1a; 2. 格式說明&#xff1a; chunkId&#xff1a;通常是 “RIFF” 四個字節&#xff0c;用于標識文件類型。&#xff08;wav文件格式表示&#xff09;chunkSize&#xff1a;表示整個文件除了chunkId和chunkSize這 8 個字節外的其余部分的大小。Forma…

SQL Server Management Studio的使用

之前在https://blog.csdn.net//article/details/140961550介紹了在Windows10上安裝SQL Server 2022 Express和SSMS&#xff0c;這里整理下SSMS的簡單使用&#xff1a; SQL Server Management Studio(SSMS)是一種集成環境&#xff0c;提供用于配置、監視和管理SQL Server和數據…

數據集筆記:NUSMods API

1 介紹 NUSMods API 包含用于渲染 NUSMods 的數據。這些數據包括新加坡國立大學&#xff08;NUS&#xff09;提供的課程以及課程表的信息&#xff0c;還包括上課地點的詳細信息。 可以使用并實驗這些數據&#xff0c;它們是從教務處提供的官方 API 中提取的。 該 API 由靜態的…

劍指 Offer II 031. 最近最少使用緩存

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20031.%20%E6%9C%80%E8%BF%91%E6%9C%80%E5%B0%91%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98/README.md 劍指 Offer II 031. 最近最少使用緩存 題目描述 運用所掌握的…

uniapp 測試 IPA 包安裝到測試 iPhone

將uniapp測試IPA包安裝到測試iPhone有以下幾種方法&#xff1a; 使用Xcode安裝 確保計算機上安裝了Xcode&#xff0c;并將iOS設備通過數據線連接到計算機。打開Xcode&#xff0c;在菜單欄中選擇Window->Devices and Simulators&#xff0c;在設備列表中找到要安裝的iPhone…