Python初学者的伺服电机控制程序实践指南

入门级Python应用程序伺服电机控制

v信@宝德,百度@baode_w

前言

本文只适合像作者这样的入门级小白,大虾级别的请忽略。

由于作者水平有限,不当之处,欢迎批评指正。

本文所述应用程序只考虑功能实现,不考虑程序优化。

一、应用环境

系统环境:Window 10;

Python版本:3.7;

IDE:PyCharm;

界面设计:PYQT5;

电机:光毓机电RMD-S系列电机;

控制端口:RS-485,使用USB转485模块。

二、准备环境

软件安装及环境变量设置方法网上较多,请自行查阅,这里不再赘述。

双击打开PyCharm,新建一个New Project,注意添加项目的环境依赖venv。配置下Python3.7的依赖环境库venv library root,External Libraries—>Python 3.7(venv) —>venv library root—>pyvenv.cfg,将值改成“true”,如下图。

为PyCharm添加外部工具,FileàSettingsàToolsàExternal Toolsà “+”。

以PYQT5为例,单击“+”,弹出新建外部工具对话框,如下图所示。

对话框中Name是在IDE中的显示名字,可以自定义填写。Tool Settings需填写两项,Program项单击浏览Python3.7根目录“Python37\Lib\site-packages\pyqt5_tools\designer.exe”(当然,你得先安装有PYQT5,>_<),Working directory项填入“$ProjectFileDir$”,其他选项默认即可,单击“OK”保存。

其他外部工具如PYUIC、PYRCC等,添加过程类似。

三、编写程序

在项目中新建两个py文件,其中一个py文件(PTContrl.py)关联QT界面的信号(Signal)和槽(Slot),另外一个py文件(PTMainPRO.py)用来编写主程序,如此便于分离QT界面和逻辑程序,避免修改界面时对主程序存在干扰,当修改QT界面时,QT只会更新PTContrl.py,不会更新主程序。

主程序中需要包含PTContrl完成调用,以及主程序需要加载一些要用到的模块,如sys、serial、Qtimer等,如下图所示。

主程序功能包括,串口端口检测、波特率设置、打开端口,串口实时发送两个电机的角度值、速度值、转动方向,定时从串口接收两个电机编码器数据,并解码显示角度值。

主程序PTMainPRO.py完整代码如下:

import sys
import serial
import binascii
import serial.tools.list_ports
#from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication,QMainWindow,QFileDialog
from PyQt5.QtWidgets import QMessageBox
from time import sleep
import PTContrl
#import PDial
 #from ActiveMain import RulerProgress
#主程序
class MainControl(QMainWindow,PTContrl.Ui_SFmotorcontrol):
    def __init__(self):
        QMainWindow.__init__(self)
        PTContrl.Ui_SFmotorcontrol.__init__(self)
        self.setupUi(self)
        self.ser = serial.Serial()
        self.data_num_sended = 0
        self.engle1 = (self.dial_engle1.value()-18000)/100
        self.engle2 = (self.dial_engle2.value() – 9000) / 100
        self.speed1_H = 1#电机1速度高8位
        self.speed1_L = 244  # 电机1速度,0x1F4,5dps
        self.speed2_H = 1#电机2速度5dps高8位
        self.speed2_L = 244  # 电机2速度5dps,低8位
        self.engle1_zero = 0#水平电机零位0
        self.engle2_zero = 0#垂直电机零位0

        self.BaudSelect_box.addItem("115200")
        self.BaudSelect_box.addItem("19200")
        self.BaudSelect_box.addItem("57600")
        self.BaudSelect_box.addItem("9600")
        self.engle1_postvalue = 0
        self.engle2_postvalue = 0
        self.motor_dir = 0
        self.motor_dir1 = 0
        self.address1 = 1
        self.address2 = 2
        self.dir_left = 0
        self.dir_right = 1
        self.dir_up = 0
        self.dir_down = 1
        self.ask_flag = 0
        self.auto_flag = 0
        self.auto_stop = 0
        self.motor_auto_act =0
        self.timer1 = QTimer(self)
        self.timer1_timeout = 250#timer1溢出时间250ms
        #for addr in range(1,33,1):
         #   self.AddrSelect1.addItem(str(addr))
          #  self.AddrSelect2.addItem(str(addr))

        self.port_check()
        self.Connect.clicked.connect(self.on_Connect)#按钮点击事件链接到自定义对象
        #self.dial_engle1.valueChanged.connect(self.engle1_change)#拨盘值改变事件链接到自定义对象
        #self.dial_engle2.valueChanged.connect(self.engle1_change)
        #self.dial_engle1.sliderMoved.connect(self.engle1_change)
        #self.port_check_button.setVisible(False)
        self.port_check_button.clicked.connect(self.port_check)
        #self.dial_engle1.sliderReleased.connect(self.data_send)
        #self.dial_engle2.sliderReleased.connect(self.data_send)
        #self.H_speed_Slider.valueChanged.connect(self.speed_change)
        #self.V_speed_Slider.valueChanged.connect(self.speed_change)
        self.left_Button.pressed.connect(self.roll_send1)
        self.right_Button.pressed.connect(self.roll_send2)
        self.up_Button.pressed.connect(self.pitch_send1)
        self.down_Button.pressed.connect(self.pitch_send2)
        self.left_Button.released.connect(self.roll_stop)
        self.right_Button.released.connect(self.roll_stop)
        self.up_Button.released.connect(self.pitch_stop)
        self.down_Button.released.connect(self.pitch_stop)
        self.auto_Button1.clicked.connect(self.auto_send)
        self.timer1.timeout.connect(self.ask_send)
        self.Serialselect_box.activated.connect(self.port_check)
        self.dial_engle1.setValue(18000)
        #self.dial_engle1.setEnabled(True)
        self.left_Button.setEnabled(False)
        self.right_Button.setEnabled(False)
        self.up_Button.setEnabled(False)
        self.down_Button.setEnabled(False)
        self.auto_Button1.setEnabled(False)

        self.auto_Button1.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;")
        self.left_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.right_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.up_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.down_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.dial_engle1.setStyleSheet("border-color:rgb(0,0,255);gridline-color:rgb(0,0,255);color: rgb(0,170,0);border-left-color: rgb(255, 0, 0);")
        self.Connect.setStyleSheet(
            "background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.port_check_button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        #self.SetupConnect.setStyleSheet("border:2px groove gray;border-radius:10px;")
        #self.dial_engle1.setStyleSheet("handle{background:blue;}")
    #border:0px solid transparent;
    #border-radius:2px;}  ")

        #while True:
#在下面编写自己的子程序
    #端口打开或关闭
    def on_Connect(self):

        if self.ser.isOpen():
            self.ser.close()
            self.timer1.stop()
            self.Connect.setText("连  接")
            self.Connect.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font: 12pt;")
            self.left_Button.setEnabled(False)
            self.right_Button.setEnabled(False)
            self.up_Button.setEnabled(False)
            self.down_Button.setEnabled(False)
            self.auto_Button1.setEnabled(False)
            if self.auto_Button1.text() == "停止":
                self.auto_send()
        else:
            self.ser.port = self.Serialselect_box.currentText()
            self.ser.baudrate = int(self.BaudSelect_box.currentText())
            self.ser.bytesize = 8
            self.ser.stopbits = 1
            self.ser.parity = "N"
            self.ser.readable = True
            self.ser.timeout = 2

            try:
                self.ser.open()
                if self.ser.is_open:
                    self.Connect.setText("断  开")
                    self.Connect.setStyleSheet(
                        "background-color:rgb(200,0,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font: 12pt;")
                    self.timer1.start(self.timer1_timeout)#启动定时器
                    self.left_Button.setEnabled(True)
                    self.right_Button.setEnabled(True)
                    self.up_Button.setEnabled(True)
                    self.down_Button.setEnabled(True)
                    self.auto_Button1.setEnabled(True)
                    #self.thread_read.start()
            except:
                QMessageBox.critical(self, "Port Error", "此串口不能被打开!")
                return None

        #self.lineEdit_engle1.text = self.dial_engle1.value()
        #self.textEdit.setText(str(self.dial_engle1.value()/100))# =  #self.dial_engle1.value()
        #print(self.textEdit)

#水平转电机停止
    def roll_stop(self):
        self.left_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.right_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        order_sum = 62 + 166 + 8 + self.address1
        order_sum = int(hex(order_sum)[-2:], 16)
        self.input_s = [62, 166, self.address1, 8, order_sum, 0, 60, 140, 0, 0, 0, 0, 0, 200]  # 发送位置359度,速度0dps,顺时针,停止命令
        self.data_send()
        #self.engle1_label.setText(str(self.dial_engle1.value()/100)+"度")
        #self.engle2_label.setText(str(self.dial_engle2.value() / 100) + "度")
#俯仰电机停止
    def pitch_stop(self):
        self.up_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        self.down_Button.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        order_sum = 62 + 166 + 8 + self.address2
        order_sum = int(hex(order_sum)[-2:], 16)
        self.input_s = [62, 166, self.address2, 8, order_sum, 0, 40, 35, 0, 0, 0, 0, 0, 75]  # 发送位置90度,速度0dps,顺时针
        self.data_send()
        #self.lineEdit_speed1.setText(str(self.H_speed_Slider.value()/100)+"dps")
        #self.lineEdit_speed2.setText(str(self.V_speed_Slider.value() / 100)+"dps")
#串口端口检测
    def port_check(self):
         # 检测所有存在的串口,将信息存储在字典中
        self.Com_Dict = {}
        port_list = list(serial.tools.list_ports.comports())
        self.Serialselect_box.clear()
        for port in port_list:
            self.Com_Dict["%s" % port[0]] = "%s" % port[1]
            self.Serialselect_box.addItem(port[0])
        if len(self.Com_Dict) == 0:
            self.statu_label.setText(" 无可用串口")
            #self.statusbar.setText(" 检测无可用串口")

            # 发送数据

    def data_send(self):

             if self.ser.isOpen():

                 if self.input_s != "":
                     # 非空字符串

                    self.label_send.setText("发送:" + str(self.input_s))#print(send_list)
                    input = bytes(self.input_s)
                     #else:
                         # ascii发送
                         #input_s = (input_s + '\r\n').encode('utf-8')

                    num = self.ser.write(input)#发送数据
                    self.data_num_sended += num
                    #self.label_send.setText("发送:" + hex(input_s))
             else:
                 QMessageBox.critical(self, "Port Error", "串口未打开!")
                 pass

    def auto_send(self):
        if self.auto_flag == 0:
            self.auto_flag = 1
            self.auto_Button1.setText("停止")
            self.auto_Button1.setStyleSheet("background-color:rgb(200,0,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        else:
            self.auto_flag = 0
            self.motor_auto_act = 0
            self.auto_Button1.setText("自动")
            self.auto_Button1.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
            self.auto_stop = 1
            pass

    def ask_send(self):
        if self.ser.isOpen():
            ask_list1 = [0x3e,0x90,0x01,0x00,0xcf]
            ask_list2 = [0x3e,0x90,0x02,0x00,0xd0]
            if self.ask_flag:
                self.ser.write(ask_list1)
                self.ask_flag = 0
            else:
                self.ser.write(ask_list2)
                self.ask_flag = 1
        #else:
            sleep(0.02)#线程挂起0.05秒
            rec_data = ""
            #rec_hex = ""
            n = self.ser.inWaiting()
            if n :
                rec_data = str(binascii.b2a_hex(self.ser.read(n)))[2:-1]
            #for rec_list in rec_hex:
            #    rec_data = rec_data + str(rec_list) #= rec_data + str(self.ser.readline())
                #print(rec_data)
            if len(rec_data) > 15:
                #print(rec_data)
                #print(len(rec_data))
                self.label_send.setText("接收:" + rec_data + ",长度:" + str(len(rec_data)))  # print(send_list)
                #rec_data = rec_data[0:8]
                #head = rec_data[:2]
                #order = rec_data[3:4]
                addr = int((rec_data[5:6]),16)
                engle_data1 = rec_data[10:12]
                engle_data2 = rec_data[12:14]
                engle_data = int((engle_data2 + engle_data1),16)
                #engle_data = engle_data[::-1]#倒置
                #engle_sum = engle_data[1] * 0x100 + engle_data[2]
                #engle_sum = int(str(engle_sum),16)

                if addr == self.address1:#电机1返回数据
                    engle_data = (engle_data – self.engle1_zero) * 36000 // 4096#精度360/4096,*100
                    self.engle1_postvalue = engle_data
                    self.engle1_label.setText(str('%.1f'%(engle_data/100)))#保留1位小数点
                    engle_data = engle_data + 18000

                    if engle_data > 36000:
                        engle_data = engle_data – 36000
                    self.dial_engle1.setValue(engle_data)
                if addr == self.address2:#电机2返回数据
                    engle_data = (engle_data – self.engle2_zero) * 36000 // 4096  # 精度360/4096,*100
                    self.engle2_postvalue = engle_data
                    if engle_data > 10000:
                        self.engle2_label.setText("-" + str('%.1f' % ((36000-engle_data )/ 100)))  # 保留1位小数点
                    else:
                        self.engle2_label.setText(str('%.1f' % (engle_data / 100)))  # 保留1位小数点
                    engle_data = engle_data + 9000
                    #self.engle2_label.setText(str('%.1f'%(engle_data/100)))#保留1位小数点
                    self.dial_engle2.setValue(engle_data)

                if self.auto_flag:
                    if self.motor_auto_act ==1:#自动翻转
                        if self.engle1_postvalue > 35500:
                            self.roll_send1()#send 0deg
                            self.motor_auto_act = 1
                        if self.engle1_postvalue < 400:
                            self.roll_send2()#send 359 deg
                            self.motor_auto_act = 1
                    else:#初次自动运行
                        if self.engle1_postvalue > 18000 or self.engle1_postvalue == 18000 :
                            self.roll_send2()#send 359 deg
                            self.motor_auto_act = 1
                        if self.engle1_postvalue < 18000:
                            self.roll_send1()#send 0deg
                            self.data_send()
                            self.motor_auto_act = 1
                #自动停止
                if self.auto_stop == 1:
                    self.auto_stop = 0
                    self.roll_stop()#send auto stop

                #print(rec_data)
            #QMessageBox.critical(self, "Port Error", "串口未打开!")
           # pass

#手动与自动之间的切换?手动点击时停止自动

#水平电机顺时针转动
    def roll_send1(self):
        if self.left_Button.isDown() and self.auto_flag ==1:
            self.auto_send()
        if self.auto_flag ==0:
            self.left_Button.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")

        order_sum = 62 + 166 + 8 + self.address1
        order_sum = int(hex(order_sum)[-2:], 16)
        data_sum = int(hex(1 + self.speed1_H + self.speed1_L)[-2:] ,16)
        self.input_s = [62,166,self.address1,8,order_sum,1,0,0,0,self.speed1_L,self.speed1_H,0,0,data_sum]#发送位置0度,速度2dps,顺时针
        #input_s = bytes(input_s)
        #self.ser.write(input_s)
        self.data_send()
 

#水平电机逆时针转动
    def roll_send2(self):
        if self.right_Button.isDown() and self.auto_flag ==1:
            self.auto_send()
        if self.auto_flag == 0:
            self.right_Button.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")

        order_sum = 62 + 166 + 8 + self.address1
        order_sum = int(hex(order_sum)[-2:], 16)
        data_sum = int(hex(60+140 + self.speed1_H + self.speed1_L)[-2:], 16)
        self.input_s = [62,166,self.address1,8,order_sum,0,60,140,0,self.speed1_L,self.speed1_H,0,0,data_sum]#发送位置359度,速度2dps,逆时针
        #input_s = bytes(input_s)
        #self.ser.write(input_s)
        self.data_send()
 

#俯仰电机顺时针转动
    def pitch_send1(self):
        self.up_Button.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        order_sum = 62 + 166 + 8 + self.address2
        order_sum = int(hex(order_sum)[-2:], 16)
        data_sum = int(hex(40 + 35 + self.speed2_H + self.speed2_L)[-2:], 16)
        self.input_s = [62,166,self.address2,8,order_sum,0,40,35,0,self.speed2_L,self.speed2_H,0,0,data_sum]#发送位置90度,速度2dps,顺时针
        #input_s = bytes(input_s)
        #self.ser.write(input_s)
        self.data_send()
 

#俯仰电机逆时针转动
    def pitch_send2(self):
        self.down_Button.setStyleSheet("background-color:rgb(0,200,0);border:2px groove gray;border-radius:10px;padding:2px 4px;font:12pt;")
        order_sum = 62 + 166 + 8 + self.address2
        order_sum = int(hex(order_sum)[-2:], 16)
        data_sum = int(hex(1+120 + 105 + self.speed2_H + self.speed2_L)[-2:], 16)
        self.input_s = [62,166,self.address2,8,order_sum,1,120,105,0,self.speed2_L,self.speed2_H,0,0,data_sum]#发送位置270度,速度2dps,逆时针
        #input_s = bytes(input_s)
        #self.ser.write(input_s)
        self.data_send()
#程序启动关联
if __name__ == "__main__":
    app=QApplication(sys.argv)  # 创建一个QApplication,也就是你要开发的软件app
    #MainWindow = QtWidgets.QMainWindow()    # 创建一个QMainWindow,用来装载你需要的各种组件、控件
    ui = MainControl()                    # ui是Ui_SFmotorcontrol类的实例化对象
   #ui.setupUi(MainWindow)                  # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
    ui.show()                       # 执行QMainWindow的show()方法,显示这个QMainWindow
    sys.exit(app.exec_())                   # 使用exit()或者点击关闭按钮退出QApplication

四、设计QT界面

在PyCharm软件中启动外部工具PYQT5,Tools—>External Tools—>QT5,如下图所示。

启动PYQT5的设计界面,进入创建对话框,如下图,创建一个新的Main Window。

修改Main Window的对象名称ObjectName为“SFmotorcontrol”(也可以自己定义),但要记住,在关联py文件里面要用到。QT设计界面左侧有许多控件,将需要的控件直接往建好的空白Main Window里拖,简单排布一下,统一修改好记的对象名称,具体设计方法自行参阅QT使用方法,设计好的界面如下图所示。

编辑好后保存成ui文件(PTContrl.ui),并添加到PyCharm项目中,使用PyCharm的外部工具PYUIC将做好的ui文件转换成py文件(就是前文所说的界面关联py文件),得到PTContrl.py,完整的PTContrl.py代码如下。

QT界面关联py文件PTContrl.py完整代码:

# -*- coding: utf-8 -*-



# Form implementation generated from reading ui file 'PTContrl.ui'

#

# Created by: PyQt5 UI code generator 5.11.3

#

# WARNING! All changes made in this file will be lost!



from PyQt5 import QtCore, QtGui, QtWidgets



class Ui_SFmotorcontrol(object):

    def setupUi(self, SFmotorcontrol):

        SFmotorcontrol.setObjectName("SFmotorcontrol")

        SFmotorcontrol.resize(640, 580)

        SFmotorcontrol.setMinimumSize(QtCore.QSize(640, 580))

        SFmotorcontrol.setMaximumSize(QtCore.QSize(640, 580))

        font = QtGui.QFont()

        font.setFamily("宋体")

        font.setPointSize(12)

        SFmotorcontrol.setFont(font)

        icon = QtGui.QIcon()

        icon.addPixmap(QtGui.QPixmap("LOGO.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)

        SFmotorcontrol.setWindowIcon(icon)

        self.centralwidget = QtWidgets.QWidget(SFmotorcontrol)

        self.centralwidget.setObjectName("centralwidget")

        self.SetupConnect = QtWidgets.QGroupBox(self.centralwidget)

        self.SetupConnect.setGeometry(QtCore.QRect(10, 410, 621, 131))

        self.SetupConnect.setFlat(False)

        self.SetupConnect.setCheckable(False)

        self.SetupConnect.setObjectName("SetupConnect")

        self.Connect = QtWidgets.QPushButton(self.SetupConnect)

        self.Connect.setGeometry(QtCore.QRect(500, 50, 111, 41))

        self.Connect.setObjectName("Connect")

        self.Serialselect_box = QtWidgets.QComboBox(self.SetupConnect)

        self.Serialselect_box.setGeometry(QtCore.QRect(70, 60, 69, 22))

        self.Serialselect_box.setEditable(True)

        self.Serialselect_box.setCurrentText("")

        self.Serialselect_box.setObjectName("Serialselect_box")

        self.BaudSelect_box = QtWidgets.QComboBox(self.SetupConnect)

        self.BaudSelect_box.setGeometry(QtCore.QRect(350, 60, 71, 22))

        self.BaudSelect_box.setEditable(True)

        self.BaudSelect_box.setObjectName("BaudSelect_box")

        self.label = QtWidgets.QLabel(self.SetupConnect)

        self.label.setGeometry(QtCore.QRect(20, 60, 54, 21))

        self.label.setObjectName("label")

        self.label_3 = QtWidgets.QLabel(self.SetupConnect)

        self.label_3.setGeometry(QtCore.QRect(280, 55, 71, 31))

        self.label_3.setObjectName("label_3")

        self.port_check_button = QtWidgets.QPushButton(self.SetupConnect)

        self.port_check_button.setGeometry(QtCore.QRect(150, 50, 81, 41))

        self.port_check_button.setObjectName("port_check_button")

        self.label_send = QtWidgets.QLabel(self.SetupConnect)

        self.label_send.setGeometry(QtCore.QRect(20, 100, 571, 21))

        self.label_send.setText("")

        self.label_send.setObjectName("label_send")

        self.dial_engle1 = QtWidgets.QDial(self.centralwidget)

        self.dial_engle1.setEnabled(False)

        self.dial_engle1.setGeometry(QtCore.QRect(50, 36, 181, 181))

        self.dial_engle1.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

        self.dial_engle1.setStyleSheet("border-color: rgb(0, 0, 255);\n"

"gridline-color: rgb(0, 0, 255);\n"

"color: rgb(0, 170, 0);")

        self.dial_engle1.setMaximum(36000)

        self.dial_engle1.setPageStep(100)

        self.dial_engle1.setProperty("value", 18000)

        self.dial_engle1.setSliderPosition(18000)

        self.dial_engle1.setTracking(True)

        self.dial_engle1.setWrapping(True)

        self.dial_engle1.setNotchTarget(10.0)

        self.dial_engle1.setNotchesVisible(True)

        self.dial_engle1.setObjectName("dial_engle1")

        self.dial_engle2 = QtWidgets.QDial(self.centralwidget)

        self.dial_engle2.setEnabled(False)

        self.dial_engle2.setGeometry(QtCore.QRect(380, 36, 181, 181))

        self.dial_engle2.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))

        self.dial_engle2.setMinimum(0)

        self.dial_engle2.setMaximum(36000)

        self.dial_engle2.setSingleStep(1)

        self.dial_engle2.setPageStep(100)

        self.dial_engle2.setProperty("value", 9000)

        self.dial_engle2.setSliderPosition(9000)

        self.dial_engle2.setTracking(True)

        self.dial_engle2.setOrientation(QtCore.Qt.Vertical)

        self.dial_engle2.setInvertedAppearance(False)

        self.dial_engle2.setInvertedControls(True)

        self.dial_engle2.setWrapping(True)

        self.dial_engle2.setNotchTarget(10.0)

        self.dial_engle2.setNotchesVisible(True)

        self.dial_engle2.setObjectName("dial_engle2")

        self.label_4 = QtWidgets.QLabel(self.centralwidget)

        self.label_4.setGeometry(QtCore.QRect(40, 320, 91, 31))

        font = QtGui.QFont()

        font.setPointSize(16)

        font.setBold(True)

        font.setWeight(75)

        self.label_4.setFont(font)

        self.label_4.setObjectName("label_4")

        self.label_5 = QtWidgets.QLabel(self.centralwidget)

        self.label_5.setGeometry(QtCore.QRect(480, 320, 91, 31))

        font = QtGui.QFont()

        font.setPointSize(16)

        font.setBold(True)

        font.setWeight(75)

        self.label_5.setFont(font)

        self.label_5.setObjectName("label_5")

        self.label_9 = QtWidgets.QLabel(self.centralwidget)

        self.label_9.setGeometry(QtCore.QRect(129, 225, 54, 12))

        self.label_9.setObjectName("label_9")

        self.label_10 = QtWidgets.QLabel(self.centralwidget)

        self.label_10.setGeometry(QtCore.QRect(463, 15, 54, 12))

        self.label_10.setObjectName("label_10")

        self.label_11 = QtWidgets.QLabel(self.centralwidget)

        self.label_11.setGeometry(QtCore.QRect(235, 120, 54, 12))

        self.label_11.setObjectName("label_11")

        self.label_12 = QtWidgets.QLabel(self.centralwidget)

        self.label_12.setGeometry(QtCore.QRect(360, 120, 54, 12))

        self.label_12.setTextFormat(QtCore.Qt.AutoText)

        self.label_12.setObjectName("label_12")

        self.label_13 = QtWidgets.QLabel(self.centralwidget)

        self.label_13.setGeometry(QtCore.QRect(136, 14, 54, 12))

        self.label_13.setObjectName("label_13")

        self.label_14 = QtWidgets.QLabel(self.centralwidget)

        self.label_14.setGeometry(QtCore.QRect(454, 222, 54, 12))

        self.label_14.setObjectName("label_14")

        self.label_15 = QtWidgets.QLabel(self.centralwidget)

        self.label_15.setGeometry(QtCore.QRect(20, 120, 54, 12))

        self.label_15.setObjectName("label_15")

        self.statu_label = QtWidgets.QLabel(self.centralwidget)

        self.statu_label.setGeometry(QtCore.QRect(20, 511, 131, 21))

        self.statu_label.setText("")

        self.statu_label.setObjectName("statu_label")

        self.engle1_label = QtWidgets.QLabel(self.centralwidget)

        self.engle1_label.setGeometry(QtCore.QRect(105, 110, 71, 31))

        font = QtGui.QFont()

        font.setFamily("Times New Roman")

        font.setPointSize(16)

        font.setBold(True)

        font.setWeight(75)

        self.engle1_label.setFont(font)

        self.engle1_label.setAlignment(QtCore.Qt.AlignCenter)

        self.engle1_label.setObjectName("engle1_label")

        self.auto_Button1 = QtWidgets.QPushButton(self.centralwidget)

        self.auto_Button1.setGeometry(QtCore.QRect(280, 270, 50, 50))

        font = QtGui.QFont()

        font.setPointSize(12)

        self.auto_Button1.setFont(font)

        self.auto_Button1.setObjectName("auto_Button1")

        self.engle2_label = QtWidgets.QLabel(self.centralwidget)

        self.engle2_label.setGeometry(QtCore.QRect(436, 113, 71, 31))

        font = QtGui.QFont()

        font.setFamily("Times New Roman")

        font.setPointSize(16)

        font.setBold(True)

        font.setWeight(75)

        self.engle2_label.setFont(font)

        self.engle2_label.setAlignment(QtCore.Qt.AlignCenter)

        self.engle2_label.setObjectName("engle2_label")

        self.widget = QtWidgets.QWidget(self.centralwidget)

        self.widget.setGeometry(QtCore.QRect(474, 30, 120, 191))

        self.widget.setToolTip("")

        self.widget.setStatusTip("")

        self.widget.setAutoFillBackground(True)

        self.widget.setObjectName("widget")

        self.up_Button = QtWidgets.QPushButton(self.centralwidget)

        self.up_Button.setGeometry(QtCore.QRect(280, 210, 50, 50))

        self.up_Button.setObjectName("up_Button")

        self.right_Button = QtWidgets.QPushButton(self.centralwidget)

        self.right_Button.setGeometry(QtCore.QRect(340, 270, 50, 50))

        self.right_Button.setObjectName("right_Button")

        self.left_Button = QtWidgets.QPushButton(self.centralwidget)

        self.left_Button.setGeometry(QtCore.QRect(220, 270, 50, 50))

        self.left_Button.setObjectName("left_Button")

        self.down_Button = QtWidgets.QPushButton(self.centralwidget)

        self.down_Button.setGeometry(QtCore.QRect(280, 330, 50, 50))

        self.down_Button.setObjectName("down_Button")

        self.SetupConnect.raise_()

        self.dial_engle1.raise_()

        self.dial_engle2.raise_()

        self.label_4.raise_()

        self.label_5.raise_()

        self.label_9.raise_()

        self.label_10.raise_()

        self.label_11.raise_()

        self.label_12.raise_()

        self.label_13.raise_()

        self.label_15.raise_()

        self.statu_label.raise_()

        self.engle1_label.raise_()

        self.auto_Button1.raise_()

        self.widget.raise_()

        self.label_14.raise_()

        self.engle2_label.raise_()

        self.up_Button.raise_()

        self.right_Button.raise_()

        self.left_Button.raise_()

        self.down_Button.raise_()

        SFmotorcontrol.setCentralWidget(self.centralwidget)

        self.menubar = QtWidgets.QMenuBar(SFmotorcontrol)

        self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 23))

        self.menubar.setObjectName("menubar")

        SFmotorcontrol.setMenuBar(self.menubar)

        self.statusbar = QtWidgets.QStatusBar(SFmotorcontrol)

        self.statusbar.setObjectName("statusbar")

        SFmotorcontrol.setStatusBar(self.statusbar)



        self.retranslateUi(SFmotorcontrol)

        QtCore.QMetaObject.connectSlotsByName(SFmotorcontrol)



    def retranslateUi(self, SFmotorcontrol):

        _translate = QtCore.QCoreApplication.translate

        SFmotorcontrol.setWindowTitle(_translate("SFmotorcontrol", "云台控制软件"))

        self.SetupConnect.setTitle(_translate("SFmotorcontrol", "连接设置"))

        self.Connect.setText(_translate("SFmotorcontrol", "连   接"))

        self.BaudSelect_box.setCurrentText(_translate("SFmotorcontrol", "115200"))

        self.label.setText(_translate("SFmotorcontrol", "串口:"))

        self.label_3.setText(_translate("SFmotorcontrol", "波特率:"))

        self.port_check_button.setText(_translate("SFmotorcontrol", "串口检测"))

        self.dial_engle1.setToolTip(_translate("SFmotorcontrol", "<html><head/><body><p><span style=\" font-size:10pt;\">使用鼠标拖动</span></p></body></html>"))

        self.label_4.setText(_translate("SFmotorcontrol", "水平旋转"))

        self.label_5.setText(_translate("SFmotorcontrol", "垂直俯仰"))

        self.label_9.setText(_translate("SFmotorcontrol", "180"))

        self.label_10.setText(_translate("SFmotorcontrol", "90"))

        self.label_11.setText(_translate("SFmotorcontrol", "90"))

        self.label_12.setText(_translate("SFmotorcontrol", "0"))

        self.label_13.setText(_translate("SFmotorcontrol", "0"))

        self.label_14.setText(_translate("SFmotorcontrol", "-90"))

        self.label_15.setText(_translate("SFmotorcontrol", "270"))

        self.engle1_label.setText(_translate("SFmotorcontrol", "0.0"))

        self.auto_Button1.setText(_translate("SFmotorcontrol", "自动"))

        self.engle2_label.setText(_translate("SFmotorcontrol", "0.0"))

        self.up_Button.setText(_translate("SFmotorcontrol", "上"))

        self.right_Button.setText(_translate("SFmotorcontrol", "右"))

        self.left_Button.setText(_translate("SFmotorcontrol", "左"))

        self.down_Button.setText(_translate("SFmotorcontrol", "下"))

在PyCharm中运行主程序,点击“RUN”—>RUN‘PTMainPRO’,无错则显示设计好的界面,如下图所示,按钮在程序中做了些美化,形状和颜色和前文所述的有所不同。

五、应用程序打包

程序编好后,最后一步当然是打包成可执行的exe文件啦,如此才能摆脱程序对IDE环境的依赖,能够在其他电脑上运行。在这里,我们使用pyinstaller来进行程序打包,当然还有其他方法来完成这件事情,感兴趣的可以自行尝试一哈。

首先,需要安装pyinstaller,懒人可在cmd中pip安装,输入“pip install pyinstaller”回车,然后等待即可。

在执行pyinstaller之前,需要确认所有关于应用程序所需的资源(包含图片以及依赖的.py文件都放进dist下的项目目录中),否则会打包不成功。

为确保打包成功,项目路径中最好不要出现中文(大虾指导的,具体为什么,额,不清楚,包括前文所述的环境搭建时也有这个坑,爬过的才知道)。

管理员运行cmd,cd到项目路径下,执行“pyinstaller PTMainPRO.py”回车,执行结果如下图。

如此表明打包成功,来到我们的项目目录下,会发现在dist文件夹下多了一个文件夹PTMainPRO,这就是我们的应用程序所有需要的文件,当然包括可执行文件,如下图所示。

当然,也可以打包成只有一个exe文件,有兴趣的朋友可以深入研究下pyinstaller的执行参数。

至此,一个简单的基于Python+QT的PC应用程序就完成了,感谢大家的耐心阅读,若有不当之处,请批评指正。

作者:X_boat

物联沃分享整理
物联沃-IOTWORD物联网 » Python初学者的伺服电机控制程序实践指南

发表回复