执行器控制 #
一、执行器概述 #
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()));
}
}
七、总结 #
执行器控制要点:
- 驱动选择:根据执行器功率选择合适的驱动方式
- PWM控制:掌握PWM调速和位置控制技术
- 保护电路:添加必要的保护电路(续流二极管等)
- 电源管理:确保电源功率足够,避免电压跌落
- 安全考虑:添加限位保护和过载保护
下一章我们将学习显示设备,实现人机交互界面。
最后更新:2026-03-27