2026-01-05 18:11:56 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
2026-02-06 15:16:17 +08:00
|
|
|
|
"""
|
|
|
|
|
|
# @Time : 2026/1/4 19:13
|
2026-01-05 18:11:56 +08:00
|
|
|
|
# @Author : reenrr
|
2026-02-06 15:16:17 +08:00
|
|
|
|
# @File : stepper_motor_test1.py
|
|
|
|
|
|
# @Desc : 线条厂控制步进电机测试 应该不会丢步
|
|
|
|
|
|
"""
|
2026-01-05 18:11:56 +08:00
|
|
|
|
import time
|
2026-02-06 15:16:17 +08:00
|
|
|
|
|
2026-01-05 18:11:56 +08:00
|
|
|
|
from periphery import GPIO
|
|
|
|
|
|
|
|
|
|
|
|
# ------------参数配置-------------
|
|
|
|
|
|
# 1. 脉冲(PUL)引脚配置 → GPIO32
|
|
|
|
|
|
PUL_Pin = 32
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 方向(DIR)引脚配置 → GPIO33
|
|
|
|
|
|
DIR_Pin = 33
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 驱动器参数(根据拨码调整,默认不变)
|
|
|
|
|
|
PULSES_PER_ROUND = 400 # 每圈脉冲数(SW5~SW8拨码,默认400)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
PULSE_FREQUENCY = 2500 # 脉冲频率(Hz,新手建议500~2000,最大200KHz)
|
|
|
|
|
|
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
class StepperMotor:
|
|
|
|
|
|
"""新力川MA860H驱动器步进电机控制类"""
|
|
|
|
|
|
def __init__(self,
|
|
|
|
|
|
pul_pin: int = PUL_Pin,
|
|
|
|
|
|
dir_pin: int = DIR_Pin,
|
|
|
|
|
|
pulses_per_round: int = PULSES_PER_ROUND,
|
|
|
|
|
|
clockwise_level: bool = True,
|
|
|
|
|
|
counter_clockwise_level: bool = False):
|
|
|
|
|
|
"""
|
|
|
|
|
|
初始化步进电机控制器
|
|
|
|
|
|
:param pul_pin: 脉冲引脚
|
|
|
|
|
|
:param dir_pin: 方向引脚
|
|
|
|
|
|
:param pulses_per_round: 每圈脉冲数(SW5~SW8拨码,默认400)
|
|
|
|
|
|
:param clockwise_level: 顺时针对应的DIR电平
|
|
|
|
|
|
:param counter_clockwise_level: 逆时针对应的DIR电平
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 硬件配置参数
|
|
|
|
|
|
self.pul_pin = pul_pin
|
|
|
|
|
|
self.dir_pin = dir_pin
|
|
|
|
|
|
|
|
|
|
|
|
# 驱动器参数
|
|
|
|
|
|
self.pulses_per_round = pulses_per_round
|
|
|
|
|
|
self.clockwise_level = clockwise_level
|
|
|
|
|
|
self.counter_clockwise_level = counter_clockwise_level
|
|
|
|
|
|
|
|
|
|
|
|
# GPIO对象初始化
|
|
|
|
|
|
self.pul_gpio = None
|
|
|
|
|
|
self.dir_gpio = None
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化GPIO
|
|
|
|
|
|
self._init_gpio()
|
|
|
|
|
|
|
|
|
|
|
|
def _init_gpio(self):
|
2026-02-06 15:16:17 +08:00
|
|
|
|
"""初始化PUL和DIR引脚(内部方法)"""
|
2026-01-05 18:11:56 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 初始化脉冲引脚(输出模式)
|
|
|
|
|
|
self.pul_gpio = GPIO(self.pul_pin, "out")
|
|
|
|
|
|
# 初始化方向引脚(输出模式)
|
|
|
|
|
|
self.dir_gpio = GPIO(self.dir_pin, "out")
|
|
|
|
|
|
|
|
|
|
|
|
# 初始电平置低(避免电机误动作)
|
|
|
|
|
|
self.pul_gpio.write(False)
|
|
|
|
|
|
self.dir_gpio.write(False)
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print(f"✅ PUL引脚初始化完成:{self.pul_pin} 引脚")
|
|
|
|
|
|
print(f"✅ DIR引脚初始化完成:{self.dir_pin} 引脚")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
except PermissionError:
|
|
|
|
|
|
raise RuntimeError("权限不足!请用sudo运行程序(sudo python xxx.py)")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise RuntimeError(f"GPIO初始化失败:{str(e)}") from e
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
def _validate_params(self, rounds: float, direction: int) -> bool:
|
2026-01-05 18:11:56 +08:00
|
|
|
|
if rounds <= 0:
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("圈数必须为正数")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
return False
|
2026-02-06 15:16:17 +08:00
|
|
|
|
if direction not in (0, 1):
|
|
|
|
|
|
print("方向必须为0(逆时针)或1(顺时针)")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
def rotate(self, pulse_frequency: int, rounds: float, direction: int):
|
2026-01-05 18:11:56 +08:00
|
|
|
|
"""
|
|
|
|
|
|
控制电机旋转(支持正反转)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
:param pulse_frequency: 脉冲频率(hz)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
:param rounds: 旋转圈数(可小数,如0.5=半圈)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
:param direction: 方向(1=顺时针,0=逆时针)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
"""
|
|
|
|
|
|
# 参数验证
|
2026-02-06 15:16:17 +08:00
|
|
|
|
if not self._validate_params(rounds, direction):
|
2026-01-05 18:11:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 设置旋转方向(DIR电平)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
if direction == 1: # 顺时针
|
2026-01-05 18:11:56 +08:00
|
|
|
|
self.dir_gpio.write(self.clockwise_level)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print(f"\n=== 顺时针旋转 {rounds} 圈 ===")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
else: # 逆时针
|
|
|
|
|
|
self.dir_gpio.write(self.counter_clockwise_level)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print(f"\n=== 逆时针旋转 {rounds} 圈 ===")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
# 计算总脉冲数和时序(精准控制,避免丢步)
|
|
|
|
|
|
total_pulses = int(rounds * self.pulses_per_round)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
pulse_period = 1.0 / pulse_frequency # 脉冲周期(秒)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
half_period = pulse_period / 2 # 占空比50%(MA860H最优)
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print(f"总脉冲数:{total_pulses} | 频率:{pulse_frequency}Hz | 周期:{pulse_period * 1000:.2f}ms")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
start_time = time.perf_counter() # 高精度计时(避免丢步)
|
|
|
|
|
|
|
|
|
|
|
|
# 发送脉冲序列(核心:占空比50%的方波)
|
|
|
|
|
|
for _ in range(total_pulses):
|
|
|
|
|
|
# 高电平
|
|
|
|
|
|
self.pul_gpio.write(True)
|
|
|
|
|
|
# 精准延时(比time.sleep稳定,适配高频脉冲)
|
|
|
|
|
|
while time.perf_counter() - start_time < half_period:
|
|
|
|
|
|
pass
|
|
|
|
|
|
# 低电平
|
|
|
|
|
|
self.pul_gpio.write(False)
|
|
|
|
|
|
# 更新下一个脉冲的起始时间
|
|
|
|
|
|
start_time += pulse_period
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("✅ 旋转完成")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
|
|
"""紧急停止(置低脉冲引脚)"""
|
|
|
|
|
|
if self.pul_gpio:
|
|
|
|
|
|
self.pul_gpio.write(False)
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("🛑 电机已停止")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
|
|
"""释放GPIO资源"""
|
|
|
|
|
|
# 安全释放GPIO资源(关键:避免引脚电平残留)
|
|
|
|
|
|
if self.pul_gpio:
|
|
|
|
|
|
self.pul_gpio.write(False) # 脉冲引脚置低
|
|
|
|
|
|
self.pul_gpio.close()
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("\n✅ PUL引脚已关闭(电平置低)")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
if self.dir_gpio:
|
|
|
|
|
|
self.dir_gpio.write(False) # 方向引脚置低
|
|
|
|
|
|
self.dir_gpio.close()
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("✅ DIR引脚已关闭(电平置低)")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
# 重置GPIO对象
|
|
|
|
|
|
self.pul_gpio = None
|
|
|
|
|
|
self.dir_gpio = None
|
|
|
|
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
|
|
"""析构函数:确保资源释放"""
|
|
|
|
|
|
self.close()
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
# ------全局实例-------
|
|
|
|
|
|
GLOBAL_MOTOR = StepperMotor()
|
|
|
|
|
|
|
|
|
|
|
|
# -------对外接口----------
|
|
|
|
|
|
def motor_start(speed: int, cycle: float, direction: int):
|
|
|
|
|
|
"""
|
|
|
|
|
|
开启电机,用于断电时电机恢复到起始位置
|
|
|
|
|
|
:param speed: 脉冲频率(hz)
|
|
|
|
|
|
:param cycle: 旋转圈数
|
|
|
|
|
|
:param direction: 0=负向(逆时针),1=正向(顺时针)
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
print("\n=== 启动步进电机 ===")
|
|
|
|
|
|
|
|
|
|
|
|
GLOBAL_MOTOR.rotate(pulse_frequency=speed, rounds=cycle, direction=direction)
|
|
|
|
|
|
time.sleep(5) # 暂停5秒
|
|
|
|
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
print("\n❌ 缺少依赖:请安装python-periphery")
|
|
|
|
|
|
print("命令:pip install python-periphery")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"\n❌ 程序异常:{str(e)}")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
def motor_stop():
|
|
|
|
|
|
"""紧急停止(仅停止脉冲,保留实例)"""
|
2026-01-05 18:11:56 +08:00
|
|
|
|
try:
|
2026-02-06 15:16:17 +08:00
|
|
|
|
if GLOBAL_MOTOR:
|
|
|
|
|
|
GLOBAL_MOTOR.stop()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print("停止失败:{e}")
|
|
|
|
|
|
|
|
|
|
|
|
def align_wire(speed: int, cycle: float):
|
|
|
|
|
|
"""
|
|
|
|
|
|
使线条对齐
|
|
|
|
|
|
:param speed: 脉冲频率(hz)
|
|
|
|
|
|
:param cycle: 旋转圈数
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
print("\n=== 启动线条对齐 ===")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
# 靠近电机方向 逆时针
|
2026-02-06 15:16:17 +08:00
|
|
|
|
GLOBAL_MOTOR.rotate(pulse_frequency=speed, rounds=cycle, direction=0)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
time.sleep(5) # 暂停5秒
|
|
|
|
|
|
# 远离电机方向 顺时针
|
2026-02-27 17:24:03 +08:00
|
|
|
|
GLOBAL_MOTOR.rotate(pulse_frequency=speed,rounds=cycle, direction=1)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
time.sleep(5) # 暂停5秒
|
|
|
|
|
|
|
|
|
|
|
|
except ImportError:
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print("\n❌ 缺少依赖:请安装python-periphery")
|
|
|
|
|
|
print("命令:pip install python-periphery")
|
2026-01-05 18:11:56 +08:00
|
|
|
|
except Exception as e:
|
2026-02-06 15:16:17 +08:00
|
|
|
|
print(f"\n❌ 程序异常:{str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def cleanup():
|
|
|
|
|
|
"""程序退出时统一清理"""
|
|
|
|
|
|
if GLOBAL_MOTOR:
|
|
|
|
|
|
GLOBAL_MOTOR.close()
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2026-02-27 18:02:55 +08:00
|
|
|
|
align_wire(speed=2500, cycle=10.0)
|
|
|
|
|
|
time.sleep(10) # 电机运动需要的时间
|
2026-03-02 11:15:14 +08:00
|
|
|
|
|
|
|
|
|
|
# 测试是否电机能停止
|
|
|
|
|
|
motor_start(speed=2500, cycle=5.0, direction=0)
|
|
|
|
|
|
time.sleep(1) # 电机运动需要的时间
|
|
|
|
|
|
motor_stop()
|
|
|
|
|
|
time.sleep(0.2)
|
|
|
|
|
|
|
|
|
|
|
|
while(True): # 防止程序退出
|
|
|
|
|
|
time.sleep(1)
|
2026-01-05 18:11:56 +08:00
|
|
|
|
|