【昉·星光 2 RISC-V单板计算机】步进电机的 Web 远程控制与 LabVIEW 数据采集
分享作者:lijinlei
评测品牌:赛昉科技
评测型号:VF202040-A0
发布时间:2025-05-30 10:44:19
0
前言
介绍了昉·星光2 (StarFive2)单板计算机通过 Web 网页实现基于 HTTP 协议的远程步进电机旋转角度和速度控制、LabVIEW 上位机实现步进电机的单步和连续控制以及远程自动数据采集的项目设计。
开源口碑分享内容

【昉·星光 2 RISC-V单板计算机】步进电机的 Web 远程控制与 LabVIEW 数据采集

本文介绍了昉·星光2 (StarFive2)单板计算机通过 Web 网页实现基于 HTTP 协议的远程步进电机旋转角度和速度控制、LabVIEW 上位机实现步进电机的单步和连续控制以及远程自动数据采集的项目设计。

项目介绍

  • 驱动步进电机旋转,并实现角度和转速控制;
  • 结合 HTTP 网页传输协议,实现 Web 端发送指令,控制步进电机旋转角度;
  • 通过 LabVIEW 和 HTTP 协议,实现步进电机的连续精确控制和数据采集任务。

28BYJ-48 步进电机

参数


参数名称参数值说明
型号28BYJ-48一种常见的小型步进电机,广泛用于各种 DIY 和小型项目。
相数4相电机有4个绕组,每个绕组称为一相。
相电阻300Ω与线圈匝数有关,匝数越多,相电阻值越大。
步距角5.625°/步每个脉冲使电机旋转的角度。28BYJ-48 电机的步距角为 5.625°/步,但实际使用中通常通过驱动器进行细分。
减速比1:64减速装置传递动力的比例,计算公式:减速比 = 输出轴每转步数 / 输入轴每转步数。
细分8细分通过驱动器(如 ULN2003)进行细分,实际步距角为 0.703125°/步(5.625°/8)。
起动转矩≥300 PPS·g·cm使停止状态的步进电机启动的频率(脉冲频率)与负载转矩之间的关系。
起动频率≥500 PPS指在空载情况下能够正常启动的脉冲频率。
定位转矩≥300 g·cm电机各相绕组处于开路状态时,永磁材料的磁场产生的转矩。
绝缘耐压600 VAC/s电机的耐电压和绝缘性能。
额定电压5V电机的额定工作电压。
额定电流100mA/相每相绕组的最大电流。
转速300RPM(无负载)电机在无负载情况下的最大转速。
扭矩34.4mN·m(无负载)电机在无负载情况下的最大扭矩。
尺寸48mm x 35mm x 35mm电机的尺寸(长 x 宽 x 高)。
重量30克电机的重量。
引脚数量6个电机有6个引脚,其中4个用于控制,2个用于电源。
控制方式单四拍、双四拍、八拍电机可以通过不同的控制方式驱动,影响步距角和运行平稳性。

驱动器

28BYJ-48 步进电机通常采用 ULN2003 驱动器驱动。

ULN2003 是高压大电流达林顿晶体管阵列系列产品,具有电流增益高、工作电压高、温度范围宽、带负载能力强等特点,适应于各类要求高速大功率驱动的系统。

ULN2003A 由 7 组达林顿晶体管阵列和相应的电阻网络以及钳位二极管网络构成,具有同时驱动 7 组负载的能力,为单片双极型大功率高速集成电路。ULN2003 一般用于小型步进电机驱动。

详见:ULN2003A Datasheet - TI . 硬石科技 .

驱动方式


驱动方式通电顺序说明
单四拍A-B-C-D-A每次只有一相绕组通电,每四步完成一个循环。电能消耗小,但输出力矩小,振动大。
双四拍AB-BC-CD-DA-AB每次有两相绕组同时通电,每四步完成一个循环。输出力矩大,但精度低,振动大。
八拍A-AB-B-BC-C-CD-D-DA-A结合了单拍和双拍的特点,每八步完成一个循环,步距角更小,运行更平稳。

八拍驱动模式控制顺序表

参数说明

  • 步距角:28BYJ-48 电机的步距角为 5.625°/步;
  • 细分:通常使用 8 细分,实际步距角为 0.703125°/步(5.625 / 8);
  • 总步数:电机完成一圈(360°)所需的步数为 512 步(360 / 0.703125).
考虑到步进电机齿轮设计公差等因素,单圈步数需根据实际情况调整。

工程调试

工程调试分为下面几个阶段

  1. 驱动步进电机旋转,并精确控制角度和速度;
  2. 通过 MQTT 协议连接 EMQX 服务器,实现步进电机旋转角度和速度的远程网络 JSON 消息控制;
  3. 通过 HomeAssistant 智能家居平台,远程控制步进电机旋转角和速度;
  4. 通过 LabVIEW 上位机发送 MQTT 消息实现步进电机的旋转角度和速度控制,并实现数据采集。

本地控制

利用 Python 语言编程实现 VisionFive2 开发板对步进电机的角度和速度控制。

流程图

参考:驱动28BYJ-48步进电机 .

代码

采用运行更平稳的八拍模式驱动 28BYJ-48 步进电机。

新建 step_motor.py 文件,添加如下代码

'''
Name: Stepper Motor (28BYJ-48) Control with ULN2003 Driver
Version: 2.0
Date: 2025.05
Author: ljl
Other: Rotate stepper motor (28byj-48) for custom angle.
Hardware Connections:
    GPIO13 -> IN1 (ULN2003)
    GPIO15 -> IN2 (ULN2003)
    GPIO16 -> IN3 (ULN2003)
    GPIO18 -> IN4 (ULN2003)
'''
import VisionFive.gpio as GPIO
import time

# define GPIO pin
motor_pins = [13, 15, 16, 18]

# initialize GPIO pins
GPIO.setmode(GPIO.BOARD)
for pin in motor_pins:
    GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)

# define step sequence for 4-phase 8-step 28BYJ-48 stepper motor
STEP_SEQUENCE = [
    [1, 0, 0, 1],  # Phase A and D
    [1, 0, 0, 0],  # Phase A
    [1, 1, 0, 0],  # Phase A and B
    [0, 1, 0, 0],  # Phase B
    [0, 1, 1, 0],  # Phase B and C
    [0, 0, 1, 0],  # Phase C
    [0, 0, 1, 1],  # Phase C and D
    [0, 0, 0, 1]   # Phase D
]

def move_steps(steps, step_delay_ms=1):
    """Rotate motor by specified number of steps
    Args:
        steps: Positive = clockwise, Negative = counter-clockwise
        step_delay_ms: Delay between steps (ms) controls speed
    """
    direction = 1 if steps >= 0 else -1
    for _ in range(abs(steps)):
        for phase in range(8)[::direction]:  # Direction control
            for pin, state in zip(motor_pins, STEP_SEQUENCE[phase]):
                GPIO.output(pin, state)
            time.sleep(step_delay_ms / 1000)  # Convert ms to seconds

def rotate_motor(angle,speed=1):
    """ rotate angle and speed """
    ROUND_VALUE = 512  # steps number for one round
    steps = int(ROUND_VALUE * angle / 360)
    if speed < 0 or speed > 10:
    	print("Error: Speed must be between 0 and 10.")
    	release_motor()
    	return
    if speed == 0:
        release_motor()
        return
    step_delay_ms = (10 - 1) / 9 * (10 - speed) + 1
    move_steps(steps, step_delay_ms)
    
def release_motor():
    """Release motor torque (set all pins LOW)"""
    for pin in motor_pins:
        GPIO.output(pin, GPIO.LOW)
        
if __name__ == "__main__":
    try:
        while True:
            angle,speed = 90,10
            print("Rotating 90 degree clockwise...")
            rotate_motor(angle, speed)
            release_motor()
            time.sleep(1)
            print("Rotating 90 degree counter-lockwise...")
            rotate_motor(-angle, speed)
            release_motor()
            time.sleep(1)
    except KeyboardInterrupt:
        GPIO.cleanup() # release pins to avoid motor from heating up by continuous input
        print('\nMotor released. Program terminated.')

终端执行 python3 step_motor.py 运行程序,此时步进电机顺时针和逆时针循环转动,间隔为 1 秒。

效果

同时终端打印步进电机的状态

远程控制

使用 Web 网页实现步进电机旋转角度和速度的远程控制。

流程图

代码

新建 step_motor_HTTP.py 文件,添加如下代码

'''
Name: Stepper Motor (28BYJ-48) Control with ULN2003 Driver
Version: 2.0
Date: 2025.05
Author: ljl
Other: Rotate stepper motor (28byj-48) using HTTP with Web.
Hardware Connections:
    GPIO13 -> IN1 (ULN2003)
    GPIO15 -> IN2 (ULN2003)
    GPIO16 -> IN3 (ULN2003)
    GPIO18 -> IN4 (ULN2003)
'''
import VisionFive.gpio as GPIO
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse

# define GPIO pin
motor_pins = [13, 15, 16, 18]

# initialize GPIO pins
GPIO.setmode(GPIO.BOARD)
for pin in motor_pins:
    GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)

# define step sequence for 4-phase 8-step 28BYJ-48 stepper motor
STEP_SEQUENCE = [
    [1, 0, 0, 1],  # Phase A and D
    [1, 0, 0, 0],  # Phase A
    [1, 1, 0, 0],  # Phase A and B
    [0, 1, 0, 0],  # Phase B
    [0, 1, 1, 0],  # Phase B and C
    [0, 0, 1, 0],  # Phase C
    [0, 0, 1, 1],  # Phase C and D
    [0, 0, 0, 1]   # Phase D
]

def move_steps(steps, step_delay_ms=1):
    """Rotate motor by specified number of steps"""
    direction = 1 if steps >= 0 else -1
    for _ in range(abs(steps)):
        for phase in range(8)[::direction]:  # Direction control
            for pin, state in zip(motor_pins, STEP_SEQUENCE[phase]):
                GPIO.output(pin, state)
            time.sleep(step_delay_ms / 1000)  # Convert ms to seconds

def rotate_motor(angle, speed=1):
    """ rotate angle and speed """
    ROUND_VALUE = 512  # steps number for one round
    steps = int(ROUND_VALUE * angle / 360)
    if speed < 0 or speed > 10:
        print("Error: Speed must be between 0 and 10.")
        release_motor()
        return
    if speed == 0:
        release_motor()
        return
    step_delay_ms = (10 - 1) / 9 * (10 - speed) + 1
    move_steps(steps, step_delay_ms)
    
def release_motor():
    """Release motor torque (set all pins LOW)"""
    for pin in motor_pins:
        GPIO.output(pin, GPIO.LOW)

# HTML page
HTML_CONTENT = """
<!DOCTYPE html>
<html>
<head>
    <title>Step Motor Controller</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
        }
        .control-panel {
            background-color: #f5f5f5;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input[type="number"], select {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover {
            background-color: #45a049;
        }
        .status {
            margin-top: 20px;
            padding: 10px;
            border-radius: 4px;
        }
        .success {
            background-color: #dff0d8;
            color: #3c763d;
        }
    </style>
</head>
<body>
    <h1>Step Motor Control Panel</h1>
    <div class="control-panel">
        <form method="GET">
            <div class="form-group">
                <label for="angle">Angle:</label>
                <input type="number" id="angle" name="angle" min="-360" max="360" value="90" required>
            </div>
            <div class="form-group">
                <label for="speed">Speed (1-10):</label>
                <input type="number" id="speed" name="speed" min="1" max="10" value="10" required>
            </div>
            <div class="form-group">
                <label for="direction">Direction:</label>
                <select id="direction" name="direction">
                    <option value="cw">CW</option>
                    <option value="ccw">CCW</option>
                </select>
            </div>
            <button type="submit">Run</button>
        </form>
        <div class="status" id="status"></div>
    </div>
    <script>
        // Show state from URL
        const params = new URLSearchParams(window.location.search);
        if(params.has('angle')) {
            const angle = params.get('angle');
            const speed = params.get('speed');
            const direction = params.get('direction');
            const statusEl = document.getElementById('status');
            statusEl.className = 'status success';
            statusEl.textContent = `Motor has rotated ${Math.abs(angle)} degree, Direction: ${direction === 'cw' ? 'CW' : 'CCW'}, Speed: ${speed}`;
        }
    </script>
</body>
</html>
"""

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(HTML_CONTENT.encode('utf-8'))
        elif self.path.startswith('/?'):
            # Analyze parameters
            query = urllib.parse.urlparse(self.path).query
            params = urllib.parse.parse_qs(query)
            
            try:
                angle = float(params.get('angle', ['90'])[0])
                speed = int(params.get('speed', ['10'])[0])
                direction = params.get('direction', ['cw'])[0]
                
                if direction == 'ccw':
                    angle = -angle
                
                rotate_motor(angle, speed)
                release_motor()
                
                # callback message
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                self.wfile.write(HTML_CONTENT.encode('utf-8'))
                
                print(f"Motor has rotated {abs(angle)} degree, Direction: {'CW' if direction == 'cw' else 'CCW'}, Speed: {speed}")
            except Exception as e:
                self.send_response(400)
                self.send_header('Content-type', 'text/plain')
                self.end_headers()
                self.wfile.write(f"Error: {str(e)}".encode('utf-8'))
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'404 Not Found')

def run_server():
    try:
        server_address = ('', 8000)
        httpd = HTTPServer(server_address, RequestHandler)
        print('Server on http://192.168.1.112:8000')
        httpd.serve_forever()
    except KeyboardInterrupt:
        GPIO.cleanup()
        print('\nMotor released. Server stopped.')

if __name__ == '__main__':
    run_server()

保存代码,终端执行指令 python3 step_motor_HTTP.py ,程序运行。

效果

网页端控制

  • 浏览器输入链接 http://192.168.1.112:8080 对应 VisionFive2 板端 IP 地址;
  • 输入 angle 角度、speed 速度、选择 direction 方向,点击 Run 按钮,实现步进电机的转动控制;
  • 待完成转动后,反馈完成信息。

移动端控制


LabVIEW 控制

结合前面 Web 网页端的项目设计,使用 LabVIEW 发送包含参数信息的 URL 实现步进电机旋转角度和速度的精确控制。

子 VI

为了实现模块化操作,便于维护和二次开发,设计步进电机驱动的子 VI 程序,实现控制程序的单次执行。

前面板

包括旋转角度、速度、方向、运行状态、反馈信息等部分。

程序框图

输入旋转参数,运行程序,可实现单次转动。

关键部分是使用了 HTTP Get.vi 程序,位于 数据通信 - 协议 - HTTP 面板;

以及 搜索/拆分字符串.vi 程序,用以分析反馈字符串,判断是否完成转动。

详见:GET - NI, search/split string - NI .

主程序

在单步运行子 VI 设计的基础上,结合循环结构,进一步实现指定步长的连续转动和数据采集任务。

前面板

操作步骤

  1. 检查子VI中设备IP地址是否正确;
  2. 点击左上角运行按钮;
  3. 单次运行: (a)输入速度和目标旋转角度值; (b)点击 Go To 按钮,字符面板显示HTTP的反馈信息; (c) Running 指示灯显示旋转状态(运行时点亮,静止时熄灭);
  4. 连续运行: (a)输入步长、目标角度、延时、文件保存路径; (b)点击 START 按钮开始运行; (c)实时显示位置、循环状态、提示字符串; (d)显示数据与角度演化曲线; (e)运行结束后,曲线数据自动保存至目标路径。
  5. 点击 Terminate 按钮终止程序。

程序框图

效果

LabVIEW 运行程序,控制步进电机连续转动,结合阶跃函数和随机数,模拟完成数据采集。

步进电机运行效果

运行结束后,数据保存效果如下

第一列为步进电机转动角度,第二列为数据采集的模拟值。

总结

本文介绍了昉·星光2 (StarFive2)单板计算机通过 Web 网页实现基于 HTTP 协议的远程步进电机旋转角度和速度控制、LabVIEW 上位机实现步进电机的单步和连续控制以及远程自动数据采集的项目设计,为 VisionFive2 开发板的物联网应用、以及工业和科研领域的应用提供了参考。


全部评论
暂无评论
0/144