首页/文章/ 详情

使用PyCharm与RS485串口控制多台步进电机正同步、顺序正反转动

1月前浏览132

 

前文《使用PyCharm与RS485串口控制步进电机正反转动》使用PyCharm运行Python程序,通过交互式输入加减速、转速、时间等参数,控制步进电机正反转。本例在前文基础上,对多台步进电机实现同步转动、先后顺序转动,均可正反转动。

Part-1 步进电机控制系统组成

借用前文的图。系统硬件组成包括:AC电源(220V)、计算机、步进电机(42H247-1684A)、USB转RS485串口、AC转DC电源、驱动器。每个硬件的接口、编号及其之间的连线如下ibd图所示。

上图中仅有1台驱动器、1台步进电机,定义为驱动器1、步进电机1。增加驱动器2、步进电机2,驱动器1的COM2与驱动器1的COM1使用网线连接,驱动器2与步进电机2连接及电源的连接与驱动器1形式相同。

Part-2 Python程序

Python程序定义了3个类、1个主方法。
1)StepperMotor()类用于创建1个步进电机对象并对其进行设置和运行控制。

2)MultiMotorController()类用于创建管理多个步进电机。
3)initSetting()类用于对交互式输出、读取各设置项。

Part-3 程序运行过程的交互设置项

设置端口、波特率、设备序号,输出设备ID及名称,确认是否对设备重命名。

输入各设备的名称、加速度、减速度、转速、运行时间。对电机使能、并进行参数设置。

设置运动模式。同步,即设备同时开始运行,到各设备运行时间后自动停止;顺序,即按照输入设备ID顺序,各设备依次启动、停止。下图为选择同步运动模式。

运行结束后,提示是否继续。若继续,则开始新一轮设置。
下图继续,设置端口号、波特率、设备ID,使用默认设备名称。

设置电机加速度、减速度、转速、时间。

选择设备顺序运动模式,按照设备2、1的顺序运行。结束后不再继续,退出程序。

Part 4 完整Python程序

  # -*- coding: utf-8 -*-
"""
多电机控制
"""

import
 time, keyboard
import
 logging
from
 pymodbus.client import ModbusSerialClient
from
 pymodbus import FramerType

# 0--关闭 pymodbus 的详细日志(可选)

logging.basicConfig()  # 初始化日志系统,使用默认配置
log = logging.getLogger()  # 获取根日志记录器(所有日志的顶层记录器)
log.setLevel(logging.WARNING) #设置日志级别为WARNING,只显示警告及以上级别的日志


class
 StepperMotor:
    """步进电机控制类"""


    # 1--寄存器地址常量

    ADDR_ALARM = 0x0000  # 报警代码
    ADDR_STATUS = 0x0001  # 状态寄存器
    ADDR_JA = 0x002E  # 加速度 JA (40047)
    ADDR_JL = 0x002F  # 减速度 JL (40048)
    ADDR_JS = 0x0030  # 速度 JS (40049)
    ADDR_COMMAND = 0x007C  # 命令操作码 (40125)
    ADDR_ENABLE = 0x008B  # 使能 ME (40140)

    # 2--命令操作码寄存器(40125)数据

    CMD_START = 0x0096  # 启动点动 CJ
    CMD_STOP = 0x00D8  # 停止点动 SJ

    def
 __init__(self, client, slave_id, name):
        """
        初始化电机
        :param client: Modbus 客户端
        :param slave_id: 从站地址 (1-247)
        :param name: 电机名称
        """

        self
.client = client
        self
.slave_id = slave_id
        self
.name = name
        self
.is_enabled = False

    def
 enable(self):
        """使能电机"""

        result = self.client.write_register(
            address=self.ADDR_ENABLE,
            value=0x0001,
            device_id=self.slave_id)
        if
 not hasattr(result, 'isError') or not result.isError():
            self
.is_enabled = True
            print
(f"✓ [{self.name}] 电机已使能 (地址: {self.slave_id})")
        else
:
            print
(f"✗ [{self.name}] 使能失败")
        time.sleep(0.3)

    def
 disable(self):
        """失能电机"""

        self
.client.write_register(
            address=self.ADDR_ENABLE,
            value=0x0000,
            device_id=self.slave_id)
        self
.is_enabled = False
        print
(f"✓ [{self.name}] 电机已失能")

    def
 set_acceleration(self, value=100):
        """
        设置加速度
        :param value: 加速度值 (Rps/sec),范围 1-500
        寄存器值 = value × 6
        """

        reg_value = int(value * 6)
        self
.client.write_register(
            address=self.ADDR_JA,
            value=reg_value,
            device_id=self.slave_id)
        print
(f"  [{self.name}] 加速度: {value} Rps/sec")

    def
 set_deceleration(self, value=100):
        """
        设置减速度
        :param value: 减速度值 (Rps/sec),范围 1-500
        寄存器值 = value × 6
        """

        reg_value = int(value * 6)
        self
.client.write_register(
            address=self.ADDR_JL,
            value=reg_value,
            device_id=self.slave_id)
        print
(f"  [{self.name}] 减速度: {value} Rps/sec")

    def
 set_speed(self, value=10):
        """
        设置速度
        :param value: 速度值 (Rps),范围 0.1-50
        寄存器值 = value × 240
        """

        reg_value = int(value * 240) & 0xFFFF  # 补码,将有符号整数转为无符号16位整数 (0~65535)
        self
.client.write_register(
            address=self.ADDR_JS,
            value=reg_value,
            device_id=self.slave_id)
        direction = "正转" if value > 0 else "反转" if value < 0 else "停止"
        print
(f"  [{self.name}] 速度: {value} Rps {direction}")

    def
 start(self):
        """启动电机转动"""

        self
.client.write_register(
            address=self.ADDR_COMMAND,
            value=self.CMD_START,
            device_id=self.slave_id)
        print
(f"▶ [{self.name}] 启动转动")

    def
 stop(self):
        """停止电机转动"""

        self
.client.write_register(
            address=self.ADDR_COMMAND,
            value=self.CMD_STOP,
            device_id=self.slave_id)
        print
(f"■ [{self.name}] 停止转动")

    def
 read_status(self):
        """读取状态寄存器"""

        result = self.client.read_holding_registers(
            address=self.ADDR_STATUS,
            count=1,
            device_id=self.slave_id)
        if
 hasattr(result, 'registers'):
            status = result.registers[0]
            return
 {
                'enabled'
: bool(status & 0x0001),
                'moving'
: bool(status & 0x0002),
                'alarm'
: bool(status & 0x0004),
                'raw'
: status}
        return
 None

    def
 read_alarm(self):
        """读取报警代码"""

        result = self.client.read_holding_registers(
            address=self.ADDR_ALARM,
            count=1,
            device_id=self.slave_id)
        if
 hasattr(result, 'registers'):
            return
 result.registers[0]
        return
 None

    def
 jog(self, speed_rps=10, duration=2):
        """
        点动控制(启动 -> 运行 -> 自动停止)
        :param speed_rps: 速度 (Rps)
        :param duration: 运行时间 (秒)
        """

        self
.set_speed(speed_rps)
        self
.start()
        time.sleep(duration)
        self
.stop()
        print
(f"  [{self.name}] 完成 {duration} 秒运动")


class
 MultiMotorController():
    """多电机控制器"""


    def
 __init__(self, port, baudrate):
        """
        初始化控制器
        :param port: 串口号
        :param baudrate: 波特率
        """

        self
.port = port
        self
.client = ModbusSerialClient(
            port=port,
            framer=FramerType.RTU,
            baudrate=baudrate,
            timeout=1,
            stopbits=1,
            bytesize=8,
            parity='N')
        self
.motors = {}
        self
._connected = False

    def
 connect(self):
        """连接串口"""

        if
 self.client.connect():
            self
._connected = True
            print
(f"✓ 串口 {self.port} 连接成功")  # 使用保存的端口号
            return
 True
        else
:
            print
(f"✗ 串口 {self.port} 连接失败")
            return
 False

    def
 close(self):
        """关闭串口"""

        self
.client.close()
        print
("串口已关闭")

    def
 add_motor(self, slave_id, name):
        """
        添加电机
        :param slave_id: 从站地址
        :param name: 电机名称
        """

        motor = StepperMotor(self.client, slave_id, name)
        self
.motors[slave_id] = motor
        print
(f"添加电机: {name} (地址: {slave_id})")
        return
 motor


class
 initSetting:

    def
 __init__(self):
        self
.motorsIdName = {}  # 电机ID、Name字典:{Id:Name,...}

    def
 clientSetting(self):
        """输入端口、波特率"""

        port = input("\n请输入端口号(与所用计算机端口号一致,例如COM5):\n").strip()  # 输入端口号供后续使用
        baudrate = int(input("\n请输入波特率(与驱动器设置一致,例如115200):\n").strip())  # 输入端口号供后续使用
        return
 port, baudrate

    def
 exitSetting(self):
        """是否退出"""

        ck = input("yes/no:\n").strip()  # yes,退出;no,继续
        if
 ck == "yes":
            return
 True
        else
:
            return
 False

    def
 inputSlaveIDs(self):
        """输入所用设备ID,例如: 1,2,3"""

        slv_ids = input("\n请输入设备ID(与驱动器设置一致,例如1,2,5):\n").strip().split(',')  # 输入所有电机ID
        slave_ids = [int(i) for i in slv_ids]  #设备ID转为整型
        for
 ids in slave_ids:
            self
.motorsIdName[ids] = 'Motor_' + str(ids)

    def
 inputSlaveName(self):
        """输入所用设备ID对应的名称,默认Motor_1,..."""

        # 1--输出设备ID、Name

        for
 slave_id, name in self.motorsIdName.items():
            print
(f"设备ID:{slave_id},设备名称:{name}")
        # 2--设备名称选项

        print
("是否采用默认设备名称?")
        ck = self.exitSetting()
        if
 ck:
            return

        # 3--输入设备名称

        for
 slave_id in self.motorsIdName.keys():
            slave_Name = input(f"\n请输入设备{slave_id}的名称:\n").strip()  # 输入设备id的名称
            self
.motorsIdName[slave_id] = slave_Name

    def
 inputAccDecSpeedTime(self):
        """设置各设备的加速度(Rps/sec)、减速度(Rps/sec)、转速(Rps)、时间(s)"""

        setVals = {}  #字典:{id:[JA,JL,JS,t],...}
        print
('请为以下设备设置加速度(Rps/sec)、减速度(Rps/sec)、转速(Rps)、时间(s):')
        # 1--输出设备ID、Name

        for
 slave_id, name in self.motorsIdName.items():
            print
(f"设备ID:{slave_id},设备名称:{name}")
        # 2--输入设备ID、Name

        for
 slave_id in self.motorsIdName.keys():
            setVal = input(f"请输入设备{slave_id}的加速度(Rps/sec),减速度(Rps/sec),转速(Rps),时间(s):\n").strip().split(',')
            setVals[slave_id] = [float(v) for v in setVal]  #字符串转换为整型
        return
 setVals

    def
 inputSequence(self):
        """输入设备转动顺序"""

        print
('请设置设备运动模式:\n'
              '1  同步\n'

              '2  顺序\n'
)
        while
 True:
            try
:
                vMod=input("请输入选项:").strip()
            except
 KeyboardInterrupt:
                break

            if
 vMod == "1":
                return
 0
            else
:
                vSeq = input("\n请按照设备运行顺序输入设备ID:").strip().split(',')
                vSeq=[int(v) for v in vSeq] #字符串转整型
                return
 vSeq


# ============================================

# 应用主程序main()

# ============================================

def
 main():
    # 1--输入端口,波特率,直至连接成功

    inputSetting = initSetting()
    while
 True:
        port, baudrate = inputSetting.clientSetting()  # 输入端口、波特率
        xMotorController = MultiMotorController(port, baudrate)  # 创建多电机控制器
        xMotorController.connect()  # 连接客户端
        if
 xMotorController._connected:
            break

        else
:
            print
('是否退出?')
            ck = inputSetting.exitSetting()  # 是否退出
            if
 ck:
                break


    # 2--输入电机ID、Name,添加电机

    inputSetting.inputSlaveIDs()  # 输入所有电机ID
    inputSetting.inputSlaveName()  # 输入所有电机Name
    for
 slave_id, name in inputSetting.motorsIdName.items():
        xMotorController.add_motor(slave_id, name)

    # 3--设置加速度、减速度、转速

    jogVals = inputSetting.inputAccDecSpeedTime()
    list_IdTime=[]# [(id, time),]各设备ID及运行时间
    for
 slave_id, jogVal in jogVals.items():  #设置每台设备参数
        xMotorController.motors[slave_id].enable() #电机使能
        xMotorController.motors[slave_id].set_acceleration(jogVal[0])  #设置加速度
        xMotorController.motors[slave_id].set_deceleration(jogVal[1])  #设置减速度
        xMotorController.motors[slave_id].set_speed(jogVal[2])  #设置速度
        list_IdTime.append((slave_id, jogVal[3])) #设备(ID,Time)加入列表
    vals_IdTime=sorted(list_IdTime, key=lambda x: x[1])

    # 4--设置电机转动顺序

    vMod = inputSetting.inputSequence() # 0:同步,[]:设备id列表顺序
    if
 vMod == 0: #同步
        t=0 #计时
        for
 vIdTm in vals_IdTime:
            slave_id=vIdTm[0] # 设备ID
            xMotorController.motors[slave_id].start()
        for
 vIdTm in vals_IdTime:
            slave_id=vIdTm[0] # 设备ID
            vTime=vIdTm[1] # 设备运行时间
            t=vTime-t # 运行时间差
            time.sleep(t)
            xMotorController.motors[slave_id].stop()#设备停止
            xMotorController.motors[slave_id].disable() #设备失能
    else
:
        for
 vIdTm in vals_IdTime:
            slave_id=vIdTm[0]
            vTime=vIdTm[1]
            xMotorController.motors[slave_id].start()
            time.sleep(vTime)
            xMotorController.motors[slave_id].stop()  # 设备停止
            xMotorController.motors[slave_id].disable()  # 设备失能
    xMotorController.close()#关闭客户端

if
 __name__ == '__main__':
    while
 True:
        main()
        ck=input('是否继续yes/no:').strip()
        if
 ck == "no":
            break

 


来源:AbaPY
电源python电机控制
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2026-07-01
最近编辑:1月前
KongXH
博士 专注于有限元分析领域,联合创作...
获赞 55粉丝 119文章 100课程 1
点赞
收藏
作者推荐
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈