张大头闭环步进电机_排版优化

张大头 2026-02-24

新手说明

先低速、小行程验证方向和回零

这篇讲张大头闭环步进电机的上位机、米思齐和 Python 控制。小白不要一上来就给大速度和大脉冲数,先确认串口能通、方向正确、回零逻辑正确,再扩大行程。

开始前准备

  • 先确认电机 ID、串口号、电源、电机线和驱动板连接正常,必要时把机械负载先拆掉。
  • 第一次动作时,把速度、加速度、脉冲数都设小一点,先验证一小段位移是否和预期一致。

图片怎么看

  • 先走“读取参数 -> 相对运动 -> 绝对运动 -> 回零设置 -> 米思齐 / Python 控制”这个顺序。
  • 文中如果写 CW / CCW,默认理解为站在电机轴正前方观察的旋转方向;如果你的安装朝向反了,实际表现也会反过来。

容易出错点

  • 脉冲数、细分、丝杆导程或轮径之间是联动关系。看到“1000 脉冲走多远”时,要先换算自己机构的实际位移。
  • 软件里如果写的是 senless,多半就是无传感器回零模式的拼写差异;理解成 sensorless / 无传感器回零即可。

完成判定

  • 串口能稳定读参,低速相对运动和绝对运动方向都正确,再去启用自动回零和脚本控制。
  • 回零触发后如果出现持续硬顶、抖动或发热,立即停机检查,不要让电机长期堵转。
补充提醒:闭环步进虽然比普通开环更稳,但堵转、回零和大加速度下仍可能伤机构。所有参数建议从小到大逐步放开,不要直接照搬别人的数值。
公开参考:Python 官方下载页:https://www.python.org/downloads/

张大头闭环步进电机 可以用上位机,米思齐,python三种方式控制,本文档会依次进行介绍。用之前需要准备两个自制DIYPCB板子,如下


图解说明:这张图对应“上位机调试 用下面这个板子”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。

图解说明:这张图对应“上位机调试 用下面这个板子”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

 

一、上位机调试 用下面这个板子


图解说明:这张图对应“上位机调试 用下面这个板子”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

打开张大头软件


图解说明:这张图对应“打开张大头软件”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

选择正确的端口号,点击打开串口


图解说明:这张图对应“选择正确的端口号,点击打开串口”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

点击读取系统状态参数,右侧会有该电机数据。如果没有数据,关闭打开串口再试一次。或者切换上方电机的ID,出厂电机ID一般为1


图解说明:这张图对应“点击读取系统状态参数,右侧会有该电机数据。如果没有数据,关闭打开串口再试一次。或者切换上方电机的 ID,出厂...”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

 

 

找到位置模式,选择相对运动,方向CW逆时针,CCW顺时针。速度设置为100(转的快慢)加速度(速度增加速度)0,脉冲数1000(转多少),点击发送指令,观察电机转动情况。相对运动的意思是从当前位置运动多少个脉冲,绝对运动是从启动点开始计算运动多少个脉冲(例如绝对位置运动1000后,再运行1000则不会运动)


图解说明:这张图对应“找到位置模式,选择相对运动,方向 CW逆时针,CCW顺时针。速度设置为100(转的快慢)加速度(速度增加速度...”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

如何设置张大头开机自动回零,设置完了以后会一直保存


图解说明:这张图对应“如何设置张大头开机自动回零,设置完了以后会一直保存”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

设置回零模式为senless(无限位回零模式),堵转自动停止

回零方向cw逆时针,ccw顺时针

回零速度,电机运动到堵转位置的速度,一般100左右

回零超时时间 默认运动10秒,不管是否堵转都会停

无限位回零检测转速,和回零转速保持一致,低于该转速代表堵转

无限位回零检测电流,大于该电流即代表堵转

无限位回零检测时间,超过该时间未转即堵转

5 6 7 三个条件满足一个即堵转停止

上电触发回零 选择enable,上电自动归零

是否储存选择是 设置完以上条件后点击修改回零参数,再点击读取回零参数看一下是否保存成功

 

 

测试回零是否正确

右侧回零模式选择senless

点击设置单圈回零的零点位置,观察电机转动情况

设置完回零后,后续使用米思齐还是python都会自动归零

电机如果不动或者转动情况不正确

可以恢复初始化


图解说明:这张图对应“设置完回零后,后续使用米思齐还是 python都会自动归零”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

找到菜单中的Restore ,点击恢复出厂设置

没进行过编码器校准的需要先进行编码器校准,操作步骤如下:

1. 上电后选中 Cal 菜单,按 Enter 键确认选择,电机开始校准编码器;

2. 电机在“哔”的一声后会慢慢的正转一圈,然后再慢慢的反转一圈;

 

米思齐控制


图解说明:这张图对应“电机在 “哔”的一声后会慢慢的正转一圈,然后再慢慢的反转一圈”这一步。执行前先拆桨或脱开负载,避免设备突然动作。
 


图解说明:这张图对应“电机在 “哔”的一声后会慢慢的正转一圈,然后再慢慢的反转一圈”这一步。执行前先拆桨或脱开负载,避免设备突然动作。
 

python控制

最简控制

import serial
ser = serial.Serial('COM5', 115200)
ser.write(bytes.fromhex('01FD0100100000007D0000006B'))

print(f"位置控制命令已发送: {ser.read(10).hex().upper() if ser.read(10) else '无响应'}")


图解说明:这张图对应“电机在 “哔”的一声后会慢慢的正转一圈,然后再慢慢的反转一圈”这一步。这里的输出结果能帮助你判断驱动和环境是否真的装好了。
 

这段话怎么和说明书对应,速度脉冲数,方向和模式


图解说明:这张图对应“电机在 “哔”的一声后会慢慢的正转一圈,然后再慢慢的反转一圈”这一步。第一次只用小速度、小脉冲测试,先确认方向和回零逻辑正确。
 

复杂版,python做了一个控制台

import tkinter as tk
from tkinter import ttk, messagebox
import struct
import serial
import serial.tools.list_ports


class MotorControlGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("闭环步进电机控制台 ")
        self.root.geometry("550x650")
        self.ser = None
        self.setup_ui()

    def setup_ui(self):
        # --- 1. 串口配置区 ---
        frame_serial = tk.LabelFrame(self.root, text="1. 硬件通信设置", padx=10, pady=10)
        frame_serial.pack(fill="x", padx=10, pady=5)

        tk.Label(frame_serial, text="端口:").pack(side="left")
        self.port_combo = ttk.Combobox(frame_serial, values=[p.device for p in serial.tools.list_ports.comports()],
                                       width=10)
        self.port_combo.pack(side="left", padx=5)

        tk.Label(frame_serial, text="波特率:").pack(side="left")
        self.baud_combo = ttk.Combobox(frame_serial, values=["115200", "9600", "38400", "57600"], width=8)
        self.baud_combo.current(0)  # 默认选中 115200
        self.baud_combo.pack(side="left", padx=5)

        self.btn_connect = tk.Button(frame_serial, text="连接串口", command=self.toggle_serial, bg="#2196F3",
                                     fg="white")
        self.btn_connect.pack(side="left", padx=10)

        # --- 2. 参数设置区 ---
        frame_params = tk.LabelFrame(self.root, text="2. 控制参数设置", padx=10, pady=10)
        frame_params.pack(fill="x", padx=10, pady=5)

        self.inputs = {}
        fields = [
            ("电机 ID (Hex)", "01"),
            ("转速 (RPM)", "1500"),
            ("加速度 (0-255)", "0"),
            ("脉冲数 (32000=10圈)", "32000")
        ]

        for text, default in fields:
            f = tk.Frame(frame_params)
            f.pack(fill="x", pady=3)
            tk.Label(f, text=text, width=18, anchor="w").pack(side="left")
            ent = tk.Entry(f)
            ent.insert(0, default)
            ent.pack(side="right", expand=True, fill="x", padx=5)
            self.inputs[text] = ent

        # 方向单选
        f_dir = tk.Frame(frame_params)
        f_dir.pack(fill="x", pady=5)
        tk.Label(f_dir, text="旋转方向:", width=18, anchor="w").pack(side="left")
        self.dir_var = tk.StringVar(value="01")
        tk.Radiobutton(f_dir, text="逆时针 CCW (01)", variable=self.dir_var, value="01").pack(side="left")
        tk.Radiobutton(f_dir, text="顺时针 CW (00)", variable=self.dir_var, value="00").pack(side="left")

        # 模式单选
        f_mode = tk.Frame(frame_params)
        f_mode.pack(fill="x", pady=5)
        tk.Label(f_mode, text="位置模式:", width=18, anchor="w").pack(side="left")
        self.mode_var = tk.StringVar(value="00")
        tk.Radiobutton(f_mode, text="相对位置 (00)", variable=self.mode_var, value="00").pack(side="left")
        tk.Radiobutton(f_mode, text="绝对位置 (01)", variable=self.mode_var, value="01").pack(side="left")

        # --- 3. 操作按钮区 ---
        frame_action = tk.Frame(self.root)
        frame_action.pack(fill="x", padx=10, pady=5)

        tk.Button(frame_action, text="生成并发送自定义指令", bg="#4CAF50", fg="white",
                  font=("Arial", 11, "bold"), height=2, command=self.send_custom_cmd).pack(fill="x", pady=2)

        tk.Button(frame_action, text="一键发送说明书原版示例指令 (仅作测试)", bg="#FF9800", fg="white",
                  command=self.send_exact_example).pack(fill="x", pady=2)

        # --- 4. 运行日志区 ---
        tk.Label(self.root, text="运行日志 (接收与发送):").pack(anchor="w", padx=10)
        self.txt_log = tk.Text(self.root, height=10, bg="#eeeeee")
        self.txt_log.pack(fill="both", expand=True, padx=10, pady=5)

    def toggle_serial(self):
        if not self.ser:
            try:
                self.ser = serial.Serial(self.port_combo.get(), int(self.baud_combo.get()), timeout=0.1)
                self.btn_connect.config(text="断开连接", bg="#f44336")
                self.txt_log.insert(tk.END, f"成功连接 {self.port_combo.get()} ({self.baud_combo.get()})\n")
            except Exception as e:
                messagebox.showerror("连接失败", f"无法打开串口,可能被占用或不存在:\n{e}")
        else:
            self.ser.close()
            self.ser = None
            self.btn_connect.config(text="连接串口", bg="#2196F3")
            self.txt_log.insert(tk.END, "串口已断开。\n")
        self.txt_log.see(tk.END)

    def send_exact_example(self):
        # 无视任何输入框,直接发送说明书里一模一样的 13 个字节
        cmd = bytes.fromhex("01 FD 01 05 DC 00 00 00 7D 00 00 00 6B")
        self.execute_send("说明书测试", cmd)

    def send_custom_cmd(self):
        try:
            # 提取所有参数
            addr = int(self.inputs["电机 ID (Hex)"].get(), 16)
            speed = int(self.inputs["转速 (RPM)"].get())
            accel = int(self.inputs["加速度 (0-255)"].get())
            pulses = int(self.inputs["脉冲数 (32000=10圈)"].get())
            direction = int(self.dir_var.get(), 16)
            mode = int(self.mode_var.get(), 16)
            sync = 0x00  # 默认不启用多机同步

            # 严格打包前 12 字节的数据
            # 格式: B(ID) + B(FD) + B(方向) + H(速度) + B(加速度) + I(脉冲) + B(模式) + B(同步)
            packet = struct.pack('>BB B H B I B B', addr, 0xFD, direction, speed, accel, pulses, mode, sync)

            # 【核心修改】:不计算校验,直接强制在末尾拼接 0x6B
            cmd = packet + bytes([0x6B])

            self.execute_send("自定义指令", cmd)

        except Exception as e:
            messagebox.showerror("参数格式错误", f"请确保输入的是纯数字!\n详细错误: {str(e)}")

    def execute_send(self, label, cmd):
        hex_str = cmd.hex(' ').upper()
        self.txt_log.insert(tk.END, f"[{label}] 发送: {hex_str}\n")
        self.txt_log.see(tk.END)

        if self.ser and self.ser.is_open:
            try:
                self.ser.write(cmd)
                # 等待并读取电机返回的数据 (预期为 4 个字节,例如 01 FD 02 6B)
                resp = self.ser.read(10)
                if resp:
                    self.txt_log.insert(tk.END, f" ---> 收到返回: {resp.hex(' ').upper()}\n")
                else:
                    self.txt_log.insert(tk.END, f" ---> (无响应,请检查A/B线、波特率或使能状态)\n")
            except Exception as e:
                self.txt_log.insert(tk.END, f" ---> (串口写入失败: {e})\n")
        else:
            self.txt_log.insert(tk.END, " ---> (串口未连接,指令仅作预览计算)\n")

        self.txt_log.insert(tk.END, "-" * 40 + "\n")
        self.txt_log.see(tk.END)


if __name__ == "__main__":
    root = tk.Tk()
    app = MotorControlGUI(root)
    root.mainloop()