新力川OL57-R 步进电机位置模式控制代码-树莓派

概述

本文介绍了一个基于PyQt5和Modbus协议的电机位置控制系统,该系统能够通过图形界面控制电机运动,包括绝对位置移动、回零操作和返回原点等功能。系统采用Modbus RTU协议与电机驱动器通信,实现了精确的位置控制。

系统架构

该系统主要由以下几个部分组成:

  1. 用户界面层:基于PyQt5的图形界面

  2. 通信层:使用pymodbus库实现Modbus RTU协议通信

  3. 设备管理层:通过pyudev库自动检测USB设备

  4. 业务逻辑层:实现电机控制的核心功能

主要功能

1. 设备自动检测

系统使用pyudev库自动检测连接的USB设备,通过供应商ID和产品ID识别特定的串口设备:

python

def find_usb_device(vendor_id, product_id):
    context = pyudev.Context()
    for device in context.list_devices(subsystem='tty'):
        if device.get('ID_VENDOR_ID') == vendor_id and device.get('ID_MODEL_ID') == product_id:
            return device.device_node
    return None

2. Modbus通信

系统建立了Modbus RTU客户端,配置了串口参数:

python

client = ModbusClient(
    port=usb_port,  # 动态检测的设备端口
    baudrate=38400,
    parity='N',
    stopbits=1,
    bytesize=8,
    timeout=2
)

3. 电机控制功能

系统实现了以下电机控制功能:

  • 绝对位置移动:通过设置目标位置和转速控制电机运动

  • 回零操作:将电机移动到原点位置

  • 返回原点:将电机返回到初始位置

  • 紧急停止:立即停止电机运动

4. 状态监控

系统定时读取并显示PA_006寄存器的值,实时监控电机状态:

python

def update_pa006(self):
    """更新 PA_006 寄存器内容"""
    value = self.read_register(PA_006)
    if value is not None:
        self.pa006_label.setText(f"PA_006 寄存器内容: {value}")
    else:
        self.pa006_label.setText("PA_006 寄存器内容: 读取失败")

技术特点

  1. 自动设备检测:无需手动配置串口号,自动识别设备

  2. 单位转换:支持毫米和脉冲数之间的转换

  3. 异常处理:完善的错误处理和日志记录

  4. 实时状态显示:定时更新电机状态信息

  5. 用户友好的界面:简洁直观的操作界面

import sys
import time
import threading
import logging
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox
)
from PyQt5.QtCore import Qt, QTimer
from pymodbus.client import ModbusSerialClient as ModbusClient
import pyudev

# 配置日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

# 查找 USB 设备
def find_usb_device(vendor_id, product_id):
    context = pyudev.Context()
    for device in context.list_devices(subsystem='tty'):
        if device.get('ID_VENDOR_ID') == vendor_id and device.get('ID_MODEL_ID') == product_id:
            return device.device_node
    return None

# 初始化 Modbus 客户端
usb_port = find_usb_device("0403", "6001")  # 替换为实际的 idVendor 和 idProduct
if not usb_port:
    raise Exception("未找到设备")

# Modbus 客户端
client = ModbusClient(
    port=usb_port,  # 动态检测的设备端口
    baudrate=38400,
    parity='N',
    stopbits=1,
    bytesize=8,
    timeout=2
)

# 电机参数
MOTOR_SLAVE_ID = 7  # 电机从站地址
PULSES_PER_REV = 1000  # 每转脉冲数
MM_PER_REV = 78  # 每转距离(mm)

# 寄存器地址
PA_006 = 0x006  # 需要显示的寄存器
PA_036 = 0x036  # 转速设置
PA_037 = 0x037  # 定位目标低16位
PA_038 = 0x038  # 定位目标高16位
PA_04E = 0x04E  # 控制字
PA_011 = 0x011  # DI0 功能设置为原点开关  
PA_040 = 0x040  # 回零模式
PA_041 = 0x041  # 回零速度
PA_042 = 0x042  # 回零爬行速度
PA_040 = 0x040  # 位置置0

class MotorControlApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("电机位置控制")
        self.setGeometry(100, 100, 400, 400)

        # 主布局
        self.main_widget = QWidget()
        self.setCentralWidget(self.main_widget)
        self.layout = QVBoxLayout(self.main_widget)

        # 目标位置输入
        self.position_label = QLabel("目标位置 (脉冲数),负数表示反向:")
        self.position_input = QLineEdit()
        self.layout.addWidget(self.position_label)
        self.layout.addWidget(self.position_input)

        # 转速输入
        self.speed_label = QLabel("转速 (r/min):")
        self.speed_input = QLineEdit()
        self.layout.addWidget(self.speed_label)
        self.layout.addWidget(self.speed_input)

        # 启动按钮
        self.start_button = QPushButton("启动运动")
        self.start_button.clicked.connect(self.start_motion)
        self.layout.addWidget(self.start_button)

        # 停止按钮
        self.stop_button = QPushButton("停止运动")
        self.stop_button.clicked.connect(self.stop_motion)
        self.layout.addWidget(self.stop_button)

        # 回零按钮
        self.home_button = QPushButton("回零")
        self.home_button.clicked.connect(self.home_motor)
        self.layout.addWidget(self.home_button)

        # 返回按钮
        self.return_button = QPushButton("返回")
        self.return_button.clicked.connect(self.return_to_origin)
        self.layout.addWidget(self.return_button)

        # 显示 PA_006 寄存器内容
        self.pa006_label = QLabel("PA_006 寄存器内容: 未读取")
        self.layout.addWidget(self.pa006_label)

        # 状态显示
        self.status_label = QLabel("状态: 空闲")
        self.layout.addWidget(self.status_label)

        # 初始化 Modbus 连接
        self.connect_modbus()

        # 记录初始位置
        self.initial_position = 0

        # 定时器:定期读取 PA_006 寄存器内容
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_pa006)
        self.timer.start(1000)  # 每 1 秒更新一次

    def connect_modbus(self):
        """连接 Modbus"""
        try:
            if client.connect():
                logging.info("Modbus 连接成功")
            else:
                logging.error("Modbus 连接失败")
        except Exception as e:
            logging.error(f"Modbus 连接异常: {e}")

    def read_register(self, address):
        """读取 Modbus 寄存器"""
        try:
            if client.connect():
                response = client.read_holding_registers(address=address, count=1, slave=MOTOR_SLAVE_ID)
                if not response.isError():
                    return response.registers[0]
                else:
                    logging.error(f"读取寄存器 0x{address:04X} 失败: {response}")
            else:
                logging.error("Modbus 连接丢失")
        except Exception as e:
            logging.error(f"读取寄存器失败: {e}")
        return None

    def write_register(self, address, value):
        """写入 Modbus 寄存器"""
        try:
            if client.connect():
                client.write_register(address=address, value=value, slave=MOTOR_SLAVE_ID)
                logging.info(f"写入寄存器 0x{address:04X},值: {value}")
            else:
                logging.error("Modbus 连接丢失")
        except Exception as e:
            logging.error(f"写入寄存器失败: {e}")

    def update_pa006(self):
        """更新 PA_006 寄存器内容"""
        value = self.read_register(PA_006)
        if value is not None:
            self.pa006_label.setText(f"PA_006 寄存器内容: {value}")
        else:
            self.pa006_label.setText("PA_006 寄存器内容: 读取失败")
    def mm_to_pulses(self, length_mm):
        """将长度(mm)转换为脉冲数"""
        return int((length_mm / MM_PER_REV) * PULSES_PER_REV)

    def start_motion(self):
        """启动绝对运动"""
        try:
            # 设置目标位置(脉冲数)
            # 获取长度输入
            length_mm = float(self.position_input.text())

            # 将长度转换为脉冲数
            target_position = self.mm_to_pulses(length_mm)

            # 将目标位置拆分为高16位和低16位
            high_word = (target_position >> 16) & 0xFFFF  # 高16位
            low_word = target_position & 0xFFFF  # 低16位

            # 写入高16位到 PA_037
            self.write_register(PA_037, high_word)
            # 写入低16位到 PA_038
            self.write_register(PA_038, low_word)

            # 设置转速
            speed = int(self.speed_input.text())
            self.write_register(PA_036, speed)

            # 触发绝对运动
            self.write_register(PA_04E, 0x03)

            self.status_label.setText("状态: 运行中")
            logging.info(f"电机启动,目标位置: {target_position} 脉冲,转速: {speed} r/min")
        except ValueError:
            QMessageBox.warning(self, "输入错误", "请输入有效的数字")
        except Exception as e:
            logging.error(f"启动运动失败: {e}")

    def return_to_origin(self):
        """返回到初始位置"""
        try:

            # 写入高16位到 PA_037
            self.write_register(PA_037, 0)
            # 写入低16位到 PA_038
            self.write_register(PA_038, 0)

            # 设置转速
            speed = int(self.speed_input.text())
            self.write_register(PA_036, speed)

            # 触发相对运动
            self.write_register(PA_04E, 0x03)

            self.status_label.setText("状态: 返回中")
            logging.info(f"电机返回中,目标位置: {self.initial_position} 脉冲,转速: {speed} r/min")
        except Exception as e:
            logging.error(f"返回失败: {e}")

    def stop_motion(self):
        """停止运动"""
        try:
            # 复位控制字
            self.write_register(PA_04E, 0x20)
            self.status_label.setText("状态: 已停止")
            logging.info("电机已停止")
        except Exception as e:
            logging.error(f"停止电机失败: {e}")

    def home_motor(self):
        """回零"""
        try:
             # 1. 设置 DI0 功能为原点开关
            self.write_register(PA_011, 1) # PA-011=1
            logging.info("设置 DI0 功能为原点开关")

            # 2. 设置回零模式为正向原点模式
            self.write_register(PA_040, 24) # PA-040=24
            logging.info("设置回零模式为正向原点模式")

            # 3. 设置回零速度和爬行速度
            self.write_register(PA_041, 40) # 回零速度
            self.write_register(PA_042, 10) # 回零爬行速度
            logging.info("设置回零速度和爬行速度")

            # 4. 触发回零操作(PA-04E 的 bit5 置 1)
            self.write_register(PA_04E, 0x10) # PA-04E=0x10
            logging.info("触发回零操作")

            # 5. 位置置0
            self.write_register(PA_040,35)
            logging.info("原点位置置0")

            self.status_label.setText("状态: 回零中")
            logging.info("电机回零中...")

        except Exception as e:
            logging.error(f"回零失败: {e}")


# 运行程序
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MotorControlApp()
    window.show()
    sys.exit(app.exec_())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值