执行器控制 #

一、执行器概述 #

1.1 执行器分类 #

text
┌─────────────────────────────────────────────────────────┐
│                    执行器分类体系                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  按控制方式分类:                                        │
│  ├── 开关型:继电器、电磁阀                              │
│  │   └── ON/OFF控制                                    │
│  ├── 模拟型:直流电机、LED                              │
│  │   └── PWM调速/调光                                  │
│  └── 位置型:舵机、步进电机                              │
│      └── 精确位置控制                                   │
│                                                         │
│  按驱动方式分类:                                        │
│  ├── 直驱:GPIO直接驱动(小功率)                        │
│  ├── 晶体管驱动:三极管/MOSFET                          │
│  ├── 驱动芯片:L298N、ULN2003                           │
│  └── 驱动模块:电机驱动模块                              │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 常见执行器参数 #

执行器 控制方式 驱动需求 典型应用
继电器 开关 三极管/驱动芯片 电器控制
直流电机 PWM H桥驱动 风扇、小车
舵机 PWM 直驱 机械臂、舵
步进电机 脉冲序列 驱动器 打印机、CNC
电磁阀 开关 继电器 流体控制

二、继电器控制 #

2.1 基本继电器控制 #

java
package com.example.actuator;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;

public class RelayController {

    public static class Relay {
        private final DigitalOutput output;
        private final String name;
        private final boolean activeLow;
        
        public Relay(var pi4j, String name, int pin, boolean activeLow) {
            this.name = name;
            this.activeLow = activeLow;
            
            DigitalState initialState = activeLow ? DigitalState.HIGH : DigitalState.LOW;
            
            this.output = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                    .id("relay-" + name)
                    .name(name)
                    .address(pin)
                    .shutdown(initialState)
                    .initial(initialState)
                    .provider("pigpio-digital-output"));
        }
        
        public void on() {
            output.state(activeLow ? DigitalState.LOW : DigitalState.HIGH);
        }
        
        public void off() {
            output.state(activeLow ? DigitalState.HIGH : DigitalState.LOW);
        }
        
        public void toggle() {
            output.toggle();
        }
        
        public boolean isOn() {
            DigitalState state = output.state();
            return activeLow ? state == DigitalState.LOW : state == DigitalState.HIGH;
        }
        
        public String getName() {
            return name;
        }
    }
    
    private final Map<String, Relay> relays = new HashMap<>();
    
    public void addRelay(String name, int pin, boolean activeLow, var pi4j) {
        relays.put(name, new Relay(pi4j, name, pin, activeLow));
    }
    
    public void turnOn(String name) {
        Relay relay = relays.get(name);
        if (relay != null) {
            relay.on();
        }
    }
    
    public void turnOff(String name) {
        Relay relay = relays.get(name);
        if (relay != null) {
            relay.off();
        }
    }
    
    public void toggle(String name) {
        Relay relay = relays.get(name);
        if (relay != null) {
            relay.toggle();
        }
    }
    
    public void allOff() {
        relays.values().forEach(Relay::off);
    }
    
    public void allOn() {
        relays.values().forEach(Relay::on);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        RelayController controller = new RelayController();
        controller.addRelay("light", 17, true, pi4j);
        controller.addRelay("fan", 18, true, pi4j);
        controller.addRelay("pump", 22, true, pi4j);
        
        System.out.println("继电器控制演示");
        
        System.out.println("打开灯");
        controller.turnOn("light");
        Thread.sleep(2000);
        
        System.out.println("打开风扇");
        controller.turnOn("fan");
        Thread.sleep(2000);
        
        System.out.println("打开水泵");
        controller.turnOn("pump");
        Thread.sleep(2000);
        
        System.out.println("关闭所有");
        controller.allOff();
        
        pi4j.shutdown();
    }
}

2.2 多路继电器模块 #

java
package com.example.actuator;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;
import java.util.ArrayList;
import java.util.List;

public class MultiRelayModule {

    private final List<DigitalOutput> relays;
    private final boolean activeLow;
    
    public MultiRelayModule(var pi4j, int[] pins, boolean activeLow) {
        this.relays = new ArrayList<>();
        this.activeLow = activeLow;
        
        DigitalState initialState = activeLow ? DigitalState.HIGH : DigitalState.LOW;
        
        for (int i = 0; i < pins.length; i++) {
            DigitalOutput relay = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                    .id("relay-" + i)
                    .name("Relay " + i)
                    .address(pins[i])
                    .shutdown(initialState)
                    .initial(initialState)
                    .provider("pigpio-digital-output"));
            relays.add(relay);
        }
    }
    
    public void set(int index, boolean on) {
        if (index < 0 || index >= relays.size()) return;
        
        DigitalOutput relay = relays.get(index);
        if (activeLow) {
            relay.state(on ? DigitalState.LOW : DigitalState.HIGH);
        } else {
            relay.state(on ? DigitalState.HIGH : DigitalState.LOW);
        }
    }
    
    public void setAll(boolean on) {
        for (int i = 0; i < relays.size(); i++) {
            set(i, on);
        }
    }
    
    public void setPattern(int pattern) {
        for (int i = 0; i < relays.size(); i++) {
            set(i, ((pattern >> i) & 1) == 1);
        }
    }
    
    public void sequence(int delayMs) throws InterruptedException {
        for (int i = 0; i < relays.size(); i++) {
            set(i, true);
            Thread.sleep(delayMs);
            set(i, false);
        }
    }
    
    public void knightRider(int cycles, int delayMs) throws InterruptedException {
        for (int cycle = 0; cycle < cycles; cycle++) {
            for (int i = 0; i < relays.size(); i++) {
                set(i, true);
                Thread.sleep(delayMs);
                set(i, false);
            }
            for (int i = relays.size() - 2; i > 0; i--) {
                set(i, true);
                Thread.sleep(delayMs);
                set(i, false);
            }
        }
    }
    
    public int size() {
        return relays.size();
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        int[] pins = {17, 18, 22, 23, 24, 25, 5, 6};
        MultiRelayModule module = new MultiRelayModule(pi4j, pins, true);
        
        System.out.println("8路继电器模块演示");
        
        System.out.println("顺序点亮");
        module.sequence(200);
        
        System.out.println("Knight Rider效果");
        module.knightRider(3, 100);
        
        System.out.println("二进制计数");
        for (int i = 0; i < 16; i++) {
            module.setPattern(i);
            Thread.sleep(300);
        }
        
        module.setAll(false);
        pi4j.shutdown();
    }
}

三、直流电机控制 #

3.1 L298N电机驱动 #

java
package com.example.actuator.motor;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;
import com.pi4j.io.pwm.Pwm;
import com.pi4j.io.pwm.PwmType;

public class L298NMotorDriver {

    private final Pwm enA;
    private final DigitalOutput in1;
    private final DigitalOutput in2;
    private final Pwm enB;
    private final DigitalOutput in3;
    private final DigitalOutput in4;
    
    private static final int PWM_FREQ = 10000;
    
    public L298NMotorDriver(var pi4j, 
                           int enAPin, int in1Pin, int in2Pin,
                           int enBPin, int in3Pin, int in4Pin) {
        
        this.enA = pi4j.create(Pwm.newConfigBuilder(pi4j)
                .id("motor-en-a")
                .name("Motor ENA")
                .address(enAPin)
                .pwmType(PwmType.HARDWARE)
                .frequency(PWM_FREQ)
                .dutyCycle(0)
                .build());
        
        this.in1 = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("motor-in1")
                .name("Motor IN1")
                .address(in1Pin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        
        this.in2 = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("motor-in2")
                .name("Motor IN2")
                .address(in2Pin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        
        this.enB = pi4j.create(Pwm.newConfigBuilder(pi4j)
                .id("motor-en-b")
                .name("Motor ENB")
                .address(enBPin)
                .pwmType(PwmType.HARDWARE)
                .frequency(PWM_FREQ)
                .dutyCycle(0)
                .build());
        
        this.in3 = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("motor-in3")
                .name("Motor IN3")
                .address(in3Pin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        
        this.in4 = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("motor-in4")
                .name("Motor IN4")
                .address(in4Pin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
    }
    
    public void motorA(int speed) {
        if (speed > 0) {
            in1.high();
            in2.low();
        } else if (speed < 0) {
            in1.low();
            in2.high();
        } else {
            in1.low();
            in2.low();
        }
        
        enA.on(PWM_FREQ, Math.min(100, Math.abs(speed)));
    }
    
    public void motorB(int speed) {
        if (speed > 0) {
            in3.high();
            in4.low();
        } else if (speed < 0) {
            in3.low();
            in4.high();
        } else {
            in3.low();
            in4.low();
        }
        
        enB.on(PWM_FREQ, Math.min(100, Math.abs(speed)));
    }
    
    public void stopA() {
        in1.low();
        in2.low();
        enA.on(PWM_FREQ, 0);
    }
    
    public void stopB() {
        in3.low();
        in4.low();
        enB.on(PWM_FREQ, 0);
    }
    
    public void brakeA() {
        in1.high();
        in2.high();
        enA.on(PWM_FREQ, 100);
    }
    
    public void brakeB() {
        in3.high();
        in4.high();
        enB.on(PWM_FREQ, 100);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        L298NMotorDriver driver = new L298NMotorDriver(pi4j,
            18, 17, 22,
            19, 23, 24);
        
        System.out.println("L298N双电机驱动演示");
        
        System.out.println("电机A正转加速");
        for (int speed = 0; speed <= 100; speed += 10) {
            driver.motorA(speed);
            Thread.sleep(200);
        }
        
        System.out.println("电机A减速停止");
        for (int speed = 100; speed >= 0; speed -= 10) {
            driver.motorA(speed);
            Thread.sleep(200);
        }
        
        driver.stopA();
        
        System.out.println("电机B反转");
        driver.motorB(-50);
        Thread.sleep(2000);
        driver.stopB();
        
        pi4j.shutdown();
    }
}

3.2 小车运动控制 #

java
package com.example.actuator.motor;

import com.pi4j.Pi4J;

public class CarController {

    private final L298NMotorDriver driver;
    
    public CarController(var pi4j, 
                        int enAPin, int in1Pin, int in2Pin,
                        int enBPin, int in3Pin, int in4Pin) {
        this.driver = new L298NMotorDriver(pi4j, 
            enAPin, in1Pin, in2Pin, enBPin, in3Pin, in4Pin);
    }
    
    public void forward(int speed) {
        driver.motorA(speed);
        driver.motorB(speed);
    }
    
    public void backward(int speed) {
        driver.motorA(-speed);
        driver.motorB(-speed);
    }
    
    public void turnLeft(int speed) {
        driver.motorA(-speed);
        driver.motorB(speed);
    }
    
    public void turnRight(int speed) {
        driver.motorA(speed);
        driver.motorB(-speed);
    }
    
    public void spinLeft(int speed) {
        driver.motorA(-speed);
        driver.motorB(speed);
    }
    
    public void spinRight(int speed) {
        driver.motorA(speed);
        driver.motorB(-speed);
    }
    
    public void stop() {
        driver.stopA();
        driver.stopB();
    }
    
    public void brake() {
        driver.brakeA();
        driver.brakeB();
    }
    
    public void move(int leftSpeed, int rightSpeed) {
        driver.motorA(leftSpeed);
        driver.motorB(rightSpeed);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        CarController car = new CarController(pi4j,
            18, 17, 22,
            19, 23, 24);
        
        System.out.println("小车运动控制演示");
        
        System.out.println("前进");
        car.forward(60);
        Thread.sleep(2000);
        
        System.out.println("左转");
        car.turnLeft(60);
        Thread.sleep(1000);
        
        System.out.println("前进");
        car.forward(60);
        Thread.sleep(2000);
        
        System.out.println("右转");
        car.turnRight(60);
        Thread.sleep(1000);
        
        System.out.println("后退");
        car.backward(60);
        Thread.sleep(2000);
        
        System.out.println("停止");
        car.stop();
        
        pi4j.shutdown();
    }
}

四、舵机控制 #

4.1 标准舵机控制 #

java
package com.example.actuator.servo;

import com.pi4j.Pi4J;
import com.pi4j.io.pwm.Pwm;
import com.pi4j.io.pwm.PwmType;

public class StandardServo {

    private static final int SERVO_FREQ = 50;
    private static final double MIN_PULSE = 0.5;
    private static final double MAX_PULSE = 2.5;
    
    private final Pwm pwm;
    private final int minAngle;
    private final int maxAngle;
    private int currentAngle;
    
    public StandardServo(var pi4j, int pin, int minAngle, int maxAngle) {
        this.minAngle = minAngle;
        this.maxAngle = maxAngle;
        
        this.pwm = pi4j.create(Pwm.newConfigBuilder(pi4j)
                .id("servo")
                .name("Servo Motor")
                .address(pin)
                .pwmType(PwmType.HARDWARE)
                .frequency(SERVO_FREQ)
                .dutyCycle(0)
                .build());
    }
    
    public StandardServo(var pi4j, int pin) {
        this(pi4j, pin, 0, 180);
    }
    
    public void setAngle(int angle) {
        angle = Math.max(minAngle, Math.min(maxAngle, angle));
        currentAngle = angle;
        
        double pulseWidth = MIN_PULSE + (MAX_PULSE - MIN_PULSE) * 
            (angle - minAngle) / (maxAngle - minAngle);
        
        double dutyCycle = (pulseWidth / 20.0) * 100;
        pwm.on(SERVO_FREQ, dutyCycle);
    }
    
    public void setPulseWidth(double ms) {
        double dutyCycle = (ms / 20.0) * 100;
        pwm.on(SERVO_FREQ, dutyCycle);
    }
    
    public int getAngle() {
        return currentAngle;
    }
    
    public void moveTo(int targetAngle, int speed) throws InterruptedException {
        int step = targetAngle > currentAngle ? 1 : -1;
        int delay = 1000 / speed;
        
        while (currentAngle != targetAngle) {
            currentAngle += step;
            setAngle(currentAngle);
            Thread.sleep(delay);
        }
    }
    
    public void off() {
        pwm.off();
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        StandardServo servo = new StandardServo(pi4j, 18);
        
        System.out.println("标准舵机控制演示");
        
        System.out.println("转到0度");
        servo.setAngle(0);
        Thread.sleep(1000);
        
        System.out.println("转到90度");
        servo.setAngle(90);
        Thread.sleep(1000);
        
        System.out.println("转到180度");
        servo.setAngle(180);
        Thread.sleep(1000);
        
        System.out.println("平滑移动到0度");
        servo.moveTo(0, 100);
        
        servo.off();
        pi4j.shutdown();
    }
}

4.2 多舵机机械臂 #

java
package com.example.actuator.servo;

import com.pi4j.Pi4J;
import com.pi4j.io.pwm.Pwm;
import com.pi4j.io.pwm.PwmType;
import java.util.ArrayList;
import java.util.List;

public class RobotArm {

    private final List<Pwm> servos = new ArrayList<>();
    private final List<Integer> currentAngles = new ArrayList<>();
    
    private static final int SERVO_FREQ = 50;
    private static final double MIN_PULSE = 0.5;
    private static final double MAX_PULSE = 2.5;
    
    public RobotArm(var pi4j, int[] pins) {
        for (int i = 0; i < pins.length; i++) {
            Pwm servo = pi4j.create(Pwm.newConfigBuilder(pi4j)
                    .id("servo-" + i)
                    .name("Servo " + i)
                    .address(pins[i])
                    .pwmType(PwmType.HARDWARE)
                    .frequency(SERVO_FREQ)
                    .dutyCycle(0)
                    .build());
            servos.add(servo);
            currentAngles.add(90);
        }
    }
    
    public void setAngle(int servoIndex, int angle) {
        if (servoIndex < 0 || servoIndex >= servos.size()) return;
        
        angle = Math.max(0, Math.min(180, angle));
        currentAngles.set(servoIndex, angle);
        
        double pulseWidth = MIN_PULSE + (MAX_PULSE - MIN_PULSE) * angle / 180.0;
        double dutyCycle = (pulseWidth / 20.0) * 100;
        
        servos.get(servoIndex).on(SERVO_FREQ, dutyCycle);
    }
    
    public void setAngles(int[] angles) {
        for (int i = 0; i < Math.min(angles.length, servos.size()); i++) {
            setAngle(i, angles[i]);
        }
    }
    
    public void moveTo(int servoIndex, int targetAngle, int speed) 
            throws InterruptedException {
        if (servoIndex < 0 || servoIndex >= servos.size()) return;
        
        int current = currentAngles.get(servoIndex);
        int step = targetAngle > current ? 1 : -1;
        int delay = 1000 / speed;
        
        while (current != targetAngle) {
            current += step;
            setAngle(servoIndex, current);
            Thread.sleep(delay);
        }
    }
    
    public void moveToAll(int[] targetAngles, int speed) throws InterruptedException {
        boolean moving = true;
        int delay = 1000 / speed;
        
        while (moving) {
            moving = false;
            
            for (int i = 0; i < servos.size(); i++) {
                int current = currentAngles.get(i);
                int target = targetAngles[i];
                
                if (current != target) {
                    moving = true;
                    int step = target > current ? 1 : -1;
                    setAngle(i, current + step);
                }
            }
            
            Thread.sleep(delay);
        }
    }
    
    public int getAngle(int servoIndex) {
        if (servoIndex < 0 || servoIndex >= servos.size()) return 0;
        return currentAngles.get(servoIndex);
    }
    
    public void home() {
        int[] homePositions = {90, 90, 90, 90};
        setAngles(homePositions);
    }
    
    public void off() {
        servos.forEach(Pwm::off);
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        int[] pins = {18, 19, 22, 23};
        RobotArm arm = new RobotArm(pi4j, pins);
        
        System.out.println("机械臂控制演示");
        
        System.out.println("回到初始位置");
        arm.home();
        Thread.sleep(1000);
        
        System.out.println("移动到位置1");
        arm.moveToAll(new int[]{45, 90, 135, 90}, 50);
        Thread.sleep(1000);
        
        System.out.println("移动到位置2");
        arm.moveToAll(new int[]{135, 45, 90, 135}, 50);
        Thread.sleep(1000);
        
        System.out.println("回到初始位置");
        arm.home();
        Thread.sleep(1000);
        
        arm.off();
        pi4j.shutdown();
    }
}

五、步进电机控制 #

5.1 ULN2003步进电机驱动 #

java
package com.example.actuator.motor;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;
import java.util.List;

public class StepperMotor {

    private final List<DigitalOutput> pins;
    private final int stepsPerRevolution;
    private int currentStep = 0;
    
    private static final int[][] HALF_STEP_SEQUENCE = {
        {1, 0, 0, 0},
        {1, 1, 0, 0},
        {0, 1, 0, 0},
        {0, 1, 1, 0},
        {0, 0, 1, 0},
        {0, 0, 1, 1},
        {0, 0, 0, 1},
        {0, 0, 0, 1}
    };
    
    private static final int[][] FULL_STEP_SEQUENCE = {
        {1, 0, 0, 0},
        {0, 1, 0, 0},
        {0, 0, 1, 0},
        {0, 0, 0, 1}
    };
    
    public StepperMotor(var pi4j, int[] pinNumbers, int stepsPerRevolution) {
        this.stepsPerRevolution = stepsPerRevolution;
        this.pins = new ArrayList<>();
        
        for (int pin : pinNumbers) {
            DigitalOutput output = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                    .id("stepper-" + pin)
                    .name("Stepper Pin")
                    .address(pin)
                    .shutdown(DigitalState.LOW)
                    .initial(DigitalState.LOW)
                    .provider("pigpio-digital-output"));
            pins.add(output);
        }
    }
    
    public void step(int steps, int delayMs, boolean halfStep) {
        int[][] sequence = halfStep ? HALF_STEP_SEQUENCE : FULL_STEP_SEQUENCE;
        int sequenceLength = sequence.length;
        
        int direction = steps > 0 ? 1 : -1;
        steps = Math.abs(steps);
        
        for (int i = 0; i < steps; i++) {
            currentStep = (currentStep + direction + sequenceLength) % sequenceLength;
            
            int[] pattern = sequence[currentStep];
            for (int j = 0; j < pins.size(); j++) {
                if (pattern[j] == 1) {
                    pins.get(j).high();
                } else {
                    pins.get(j).low();
                }
            }
            
            try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
        }
    }
    
    public void rotate(double degrees, int delayMs, boolean halfStep) {
        int stepsPerRev = halfStep ? stepsPerRevolution * 2 : stepsPerRevolution;
        int steps = (int) (degrees * stepsPerRev / 360.0);
        step(steps, delayMs, halfStep);
    }
    
    public void stop() {
        for (DigitalOutput pin : pins) {
            pin.low();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        int[] pins = {17, 18, 22, 23};
        StepperMotor stepper = new StepperMotor(pi4j, pins, 2048);
        
        System.out.println("步进电机控制演示");
        
        System.out.println("正转一圈(半步模式)");
        stepper.rotate(360, 2, true);
        
        Thread.sleep(1000);
        
        System.out.println("反转一圈(全步模式)");
        stepper.rotate(-360, 3, false);
        
        stepper.stop();
        pi4j.shutdown();
    }
}

六、执行器管理器 #

6.1 统一执行器管理 #

java
package com.example.actuator;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ActuatorManager {

    public interface Actuator {
        String getName();
        String getState();
        void execute(String command, Map<String, Object> params);
    }
    
    private final Map<String, Actuator> actuators = new ConcurrentHashMap<>();
    
    public void register(String id, Actuator actuator) {
        actuators.put(id, actuator);
    }
    
    public void unregister(String id) {
        actuators.remove(id);
    }
    
    public Actuator get(String id) {
        return actuators.get(id);
    }
    
    public void execute(String id, String command) {
        execute(id, command, Map.of());
    }
    
    public void execute(String id, String command, Map<String, Object> params) {
        Actuator actuator = actuators.get(id);
        if (actuator != null) {
            actuator.execute(command, params);
        }
    }
    
    public Map<String, String> getAllStates() {
        Map<String, String> states = new ConcurrentHashMap<>();
        actuators.forEach((id, actuator) -> states.put(id, actuator.getState()));
        return states;
    }
    
    public void executeAll(String command) {
        actuators.values().forEach(a -> a.execute(command, Map.of()));
    }
}

七、总结 #

执行器控制要点:

  1. 驱动选择:根据执行器功率选择合适的驱动方式
  2. PWM控制:掌握PWM调速和位置控制技术
  3. 保护电路:添加必要的保护电路(续流二极管等)
  4. 电源管理:确保电源功率足够,避免电压跌落
  5. 安全考虑:添加限位保护和过载保护

下一章我们将学习显示设备,实现人机交互界面。

最后更新:2026-03-27