Präzisen Linearservo-Aktuator mit Positionsrückführung bauen – Schritt-für-Schritt
In diesem Tutorial lernen wir, wie man einen Linearservo-Aktuator baut. Im Gegensatz zu herkömmlichen Lineareaktoren, die bei angelegter Spannung einfach in eine Richtung fahren, bietet dieser Eigenbau präzise, wiederholbare Bewegungen mit exakter Steuerung.
Er heißt Servo-Aktuator, weil er ein Rückführsystem (Closed-Loop) besitzt, über das die Ausgabebewegung präzise geregelt wird.
Video ansehen oder schriftliche Anleitung unten lesen.
Überblick
Die Steuereingabe kann analog oder digital sein. Analog: beliebiges Potentiometer – Linear-, Drehpotentiometer, Joystick (wieder Drehpotentiometer) etc. Digital: RC-Sender. Für beide Modi nur 3 Drähte nötig: 2 für Stromversorgung des Eingabegeräts, 1 für das Signalsignal.
Cooles Feature: Benutzerdefinierter Start- und Endpunkt des Auslegerstabs sowie einstellbare Empfindlichkeit (Ansprechverhalten).
Mein Lieblingsfeature: Steuerung per PC/Laptop über seriellen Port. Im Arduino IDE Serial Monitor Millimeterwerte eingeben → Aktuator fährt dorthin.
Noch cooler: Wiederholbare Bewegungen speichern – an jeder gewünschten Position „save“ im Serial Monitor eingeben, dann mit „run“ die Sequenz in Endlosschleife abspielen lassen.
Jetzt alles Wissenswerte: Funktionsweise, Design, Nachbau.
Funktionsweise
Geschlossener Regelkreis basiert auf AS5600 magnetischem Drehwinkelsensor und implementierter PID-Regelung für den DC-Motor.
Ich nutze denselben Custom-Servo-Motor-Controller wie in meinem vorherigen Video – eigenes Mikrocontroller-Board, das jeden DC-Motor in einen Standalone-Servomotor verwandelt.
Dort wird detailliert erklärt, wie Servomotoren und Closed-Loop-Regelung funktionieren.
Kurz: Servomotor = Closed-Loop-System. Eingangssignal (Sollposition) wird mit Istposition (vom Positionssensor) verglichen. Differenz (Fehler) wird im Regler verarbeitet → Motor bewegt sich, bis Sollposition erreicht.
Dieser Linearservo hat dasselbe Prinzip, plus Umwandlung der Drehbewegung in Linearbewegung über Spindeltrieb (Lead Screw).
Linearservo-Aktuator Design
3D-Modell zeigt den Aufbau.
AS5600 sitzt hinten am Aktuator, erfasst Drehung der Spindel. Spindel: 8 mm Steigung → pro Umdrehung 8 mm Linearbewegung.
AS5600 ist 12-Bit-Encoder → 4096 Positionen pro Umdrehung. 8 mm / 4096 = 0,001953 mm Auflösung. Beeindruckend!
DC-Motor: 12V mit Getriebe, 480 U/min Ausgang. 480/60 = 8 U/s × 8 mm Steigung = 64 mm/s Lineargeschwindigkeit. Passt: Maximalhub 150 mm → ca. 2,5 s End-to-End (ca. 3 s mit Rampe). Daher 1:1 Übersetzung für Spindelantrieb.
Gesamtdesign basiert auf Custom-PCB (40×40 mm) und Spindel/Spindelmutter (22 mm Außendurchmesser). PCB-Maße = minimaler Zylinderblock-Durchmesser.
Spindelmutter und Stange verbunden mit 4 M3-Schrauben und Gewindeeinsätzen. Oben am Stab: Führungslager (gleitet im Zylinderblock, verhindert Verdrehung). Am Ausgangsdeckel: 4 kleine Lager für Führung der Stange aus dem Zylinder.
Gesamt kompakt für alle verbauten Komponenten.
Zusätzlich: Mikro-Endschalter im Zylinderblock für Homing/Startposition.
3D-Modell Download
3D-Modell im Browser mit Onshape erkunden (kostenloser Onshape-Account für Privatnutzer).
STL-Dateien für 3D-Druck sowie STEP-Datei auf Cults3D.
Maximalhub einfach erweiterbar: Zylinderblock und Stange verlängern. Ich wählte Maße passend für 220×220 mm Druckbett – größtes Teil (Stange) 215 mm.
3D-Druck
Mein Creality Ender-3 V3 SE druckte Stange horizontal entlang Y-Achse – top Qualität. Leichtes Schleifen nötig, aber diese Orientierung sorgt für glattere Oberfläche und festere Stange.
Wichtig: Horizontale Expansion im Slicer nutzen, um Filamentausdehnung zu kompensieren und dimensionsgenaue Teile zu erhalten.
Ich nutzte –0,1 mm – Testdrucke für optimale Werte auf eigenem Drucker machen.
Zylinderblock entlang Z-Achse gedruckt (wenig Support). Ender-3 V3 SE überzeugte auch hier. Angesichts Preis (< 200 $) einer der besten Budget-Drucker: Auto-Bed-Leveling, Direct Extruder, top Qualität, bis 250 mm/s.
Creality Store / Amazon. Mein Detailtest auf der Website.
Montage
Alle 3D-gedruckten Teile bereit – los geht’s.
Teileliste
Mechanik:
- Trapezgewindespindel 8 mm Tr8×8 – Amazon / AliExpress
- Kugellager 8×22×7 mm – Amazon / AliExpress
- Kugellager 4×9×4 mm ×4 – Amazon / AliExpress
- Kugellager 6×13×5 mm 686-2RS ×1 – Amazon / AliExpress
- M3×5 mm & M4×5 mm Gewindeeinsätze – Amazon / AliExpress
- M3/M4 Schrauben & Muttern – Amazon / AliExpress
Schrauben: M3×8 (10), M3×10 (2), M3×10 Senk (1), M4×6 (2), M4×25 (2), M4×30 (2), M2×8 (6), M4×5 Gewindestift (5)
Elektronik:
- AS5600 Magnetencoder – Amazon / AliExpress
- DRV8871 DC-Motortreiber – Amazon / AliExpress
- ATmega328p-AU – Amazon / AliExpress
- 16 MHz Quarz – Amazon / AliExpress
- AMS1117 5V Regler – Amazon / AliExpress
- 3386P Trimmer-Poti – Amazon / AliExpress
- Kondensatoren 0805 Sortiment – Amazon / AliExpress
- 12V DC-Motor ~50 U/min – Amazon / AliExpress
Hinweis: Affiliate-Links.
Spindeltrieb montieren
Zuerst Spindel in Zylinderbasis einsetzen. Dazu 3D-gedruckte Mutter (gleiches Gewinde) auf Spindel schrauben – strammer Sitz gewollt (überträgt gesamte Schubkraft). Mutter hat Bohrung für Gewindeeinsatz + Madenschraube zur Fixierung auf Welle.
Spindel in Zylinderbasis gelagert: zwei Kugellager 22 mm Außendurchmesser. Hinten: Antriebszahnrad (ebenfalls Gewinde, zwei Gewindeeinsätze für Madenschrauben) – kritische Verbindung, darf nicht rutschen.
Unterbaugruppe: Erst Gewindeeinsätze in Zahnrad, Mutter, Zylinderblock einsetzen. Zahnrad und Mutter gegeneinander auf Spindel schrauben (nicht zu fest – Axialkräfte auf Lager). Dann per Madenschrauben an Spindel sichern.
Spindel noch nicht axial fixiert. Deckelplatte auf Zylinderblock → Lager bleiben in Position. Unterbaugruppe fertig: Spindel fest stehend, frei drehbar.
Stange vorbereiten: Hohl für Spindel. Gewindeeinsätze für Verbindung mit Spindelmutter einsetzen. Oben: Führungslager (13/6 mm) auf 3D-gedruckte 6-mm-Hülse, mit M3-Senkschraube 10 mm an Stange fixieren. Stange gleitet nun in Führungsnuten des Zylinders.
Zylinder auf Basisblock montieren. Zuvor Mikro-Endschalter einbauen: Kabel (NC) anlöten (~15 cm), durch Bohrung oben im Zylinder führen, mit zwei M2×8 Schrauben befestigen. Exakt dieser Endschalter nötig, damit Führungslager ihn zum richtigen Zeitpunkt auslöst. Andernfalls Bohrungen/Mechanik anpassen.
Zylinder an Basisblock: Gewindeeinsätze einsetzen, zwei M4×25 Schrauben (nur obere zwei vorerst, untere später mit Getriebe/PCB-Abdeckung).
Stange in Spindelmutter eindrehen. Führungslager in Führungsschienen. Durch Drehen am Hinterrad bewegt sich Stange zurück bis zum Endschalter.
Zylinderdeckel montieren: Vier kleine Lager (9 mm Außendurchmesser), Wellen 3D-gedruckt. Vorsicht beim Einpressen – Teil ist filigran, bricht leicht (passierte mir mehrmals). Lager unterstützen Stange für ruhigen Lauf. Deckel mit vier M4-Schrauben fixieren.
DC-Motor einbauen
DC-Motor einsetzen, mit sechs M3-Schrauben fixieren. Zahnrad auf Motorwelle, per zwei Gewindeeinsätze + Madenschrauben sichern.
Zahnräder passen → Getriebe- und PCB-Abdeckung hinten anbringen. Weitere Gewindeeinsätze in Zylinderbasis. DC-Motor-Kabel anlöten (~20 cm). Durch Bohrung in Basisblock führen (Motor- und Endschalterkabel). Durch zwei Halter an Abdeckung → Kabel fern vom Zahnrad.
Abdeckung an Basisblock: Unten zwei M4-Schrauben ~2–3 mm hineindrehen, Abdeckungshalter dazwischen, dann festziehen. Etwas fummelig, aber Abdeckung sollte klein und einteilig sein – PCB-Halter blockierten Schraubenzugang.
Custom Servo-Controller PCB einbauen
Controllerboard (aus vorherigem Video) einbauen. Hauptkomponente: AS5600, erfasst Winkelposition des Magneten an Abtriebswelle. Hier: Magnet an Ausgangsrad der Spindel. PCB einstecken → Magnet perfekt ausgerichtet mit AS5600.
PCB befestigen: M2-Muttern in Halterschlitze, vier M2-Schrauben anziehen.
Verdrahtung: Motoranschluss an Klemmblock (Polarität prüfen!). Vorher Motor testen (Spannung anlegen → Spindelmechanik prüfen). Endschalter: Masse an Elko-Massepad, NC an SCK-Pin (Digital 13 am ATmega328). Stromklemmblock seitlich an Abdeckung → 5,5-mm-Hohlstecker durch Bohrung. Kühlkörper auf DRV8871. Snap-Fit-Deckel hinten drauf – fertig.
Jetzt beliebiges Potentiometer oder RC-Empfänger an Eingangs-Pins → Linearakktor steuern.
Controller-Details (Schaltplan, PCB) im vorherigen Video – dort anschauen, wenn man Board selbst bauen will.
Schaltplan
Kurz: AS5600 erfasst Aktuatorposition → ATmega328 rechnet → DRV8871 treibt DC-Motor. DRV8871: bis 3,6 A Spitzenstrom. 12V Versorgung → AMS1117 auf 5V für Mikrocontroller/5V-Bauteile. 2-Kanal-DIP-Schalter: Eingabemodus wählen (analog, digital, Serial). Trimmer-Poti: Empfindlichkeit einstellen. Taster: Start/Endposition setzen.
PCB bei PCBWay bestellt: 4 Lagen (mittlere GND), Farbe Weiß, Oberflächenfinish Immersion Gold (kostenlos). Gerber in PCBWay Community.
Alternativ: Ohne Custom-Controller – AS5600 Breakout + Arduino + Motortreiber.
Code
Code des Linearservo-Aktuators:
/* Linear Servo Actuator - Arduino Code by Dejan, www.HowToMechatronics.com
Libraries: AS5600 encoder: https://github.com/RobTillaart/AS5600
PID controller: https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h */
#include "AS5600.h"
#include "Wire.h"
#include
AS5600 as5600; // use default Wire
double Pk1 = 0.65; //speed it gets there
double Ik1 = 0;
double Dk1 = 0.1;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, Pk1, Ik1, Dk1, DIRECT);
#define motor_IN1 5
#define motor_IN2 6
#define ch1 2
#define setButton 7
#define inputSwitch 3
#define modeSwitch 4
#define limitSwitch 13
int ch1Value;
long encoderValue, desiredValue, pwmValue;
String serialInput = ""; // string to hold input
int serialIntInput;
double totalDistance = 0;
long startPosition = 358;
long endPosition = 6750;
long rangeAdjustment = 0;
float sensitivityAdjustment = 0;
float angle = 0;
float angleValue = 0;
float rodPosition;
float positionsArray[100];
int positionsCounter = 0;
long quadrantNumber = 2;
long previousQuadrantNumber = 3;
long numberOfTurns = 0;
float totalAngle = 0;
char incomingByte = 0;
int intInput = 0;
void setup() {
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
pinMode(motor_IN1, OUTPUT);
pinMode(motor_IN2, OUTPUT);
pinMode(setButton, INPUT_PULLUP);
pinMode(inputSwitch, INPUT_PULLUP);
pinMode(modeSwitch, INPUT_PULLUP);
pinMode(limitSwitch, INPUT_PULLUP);
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-255, 255);
myPID.SetSampleTime(20);
// --- HOMING ----
while (digitalRead(limitSwitch) != 1) {
digitalWrite(motor_IN1, LOW);
analogWrite(motor_IN2, 70);
encoderValue = as5600.readAngle();
}
while (digitalRead(limitSwitch) != 0) {
analogWrite(motor_IN1, 50);
digitalWrite(motor_IN2, LOW);
}
digitalWrite(motor_IN1, LOW);
digitalWrite(motor_IN2, LOW);
startPosition = as5600.readAngle() * 0.087890625;
endPosition = 6000;
Setpoint = startPosition;
// --- HOMING End ---
}
void loop() {
// Read encoder value - current position
rodPosition = as5600.readAngle() / 0.001953125; // in millimeters
// Serial communication mode
if (digitalRead(modeSwitch) == 0) {
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
if (serialInput == "save") {
positionsArray[positionsCounter] = totalDistance;
delay(1000);
positionsCounter++;
}
if (serialInput == "clear") {
memset(positionsArray, 0, sizeof(positionsArray));
positionsCounter = 0;
}
serialIntInput = serialInput.toInt();
if (serialIntInput != 0) {
if (serialIntInput < 0) serialIntInput = 0;
if (serialIntInput > 150) serialIntInput = 150;
Setpoint = serialIntInput * 45; // mm to degrees (1mm = 45°)
}
}
// Run stored positions
if (serialInput == "run") {
while (serialInput != "stop") {
for (int i = 0; i <= positionsCounter - 1; i++) {
if (serialInput == "stop") break;
while (positionsArray[i] > totalDistance + 25 || positionsArray[i] < totalDistance - 25 || pwmValue != 0) {
Setpoint = positionsArray[i];
readEncoder();
runMotor();
}
delay(2000);
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
}
}
}
}
}
// Potentiometer and RC Receiver control mode
else if (digitalRead(modeSwitch) == 1) {
if (digitalRead(inputSwitch) == 0) {
// Analog input - Potentiometer
desiredValue = analogRead(A0);
if (desiredValue < 15) desiredValue = 15;
if (desiredValue > 1008) desiredValue = 1008;
Setpoint = map(desiredValue, 15, 1008, startPosition, endPosition);
} else if (digitalRead(inputSwitch) == 1) {
// Digital input - RC transmitter
desiredValue = pulseIn(ch1, HIGH, 30000);
if (desiredValue < 1000 || desiredValue > 2000) desiredValue = 1000;
Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
}
// Confine setpoint limits
if (Setpoint > endPosition) Setpoint = endPosition;
if (Setpoint < startPosition) Setpoint = startPosition;
// Adjusting sensitivity (commented out)
// Pk1 = analogRead(A2) * 0.002;
// myPID.SetTunings(Pk1, Ik1, Dk1);
readEncoder();
runMotor();
// Set start and end positions by pressing "set" button
if (digitalRead(setButton) == LOW) {
delay(3000);
if (digitalRead(setButton) == LOW) {
endPosition = totalDistance;
while (digitalRead(setButton) != HIGH);
} else {
startPosition = totalDistance;
}
}
}
}
void readEncoder() {
encoderValue = as5600.readAngle() * 0.087890625;
if (encoderValue >= 0 && encoderValue <= 90) quadrantNumber = 1;
else if (encoderValue >= 90 && encoderValue <= 180) quadrantNumber = 2;
else if (encoderValue >= 180 && encoderValue <= 270) quadrantNumber = 3;
else if (encoderValue >= 270 && encoderValue <= 360) quadrantNumber = 4;
if (quadrantNumber != previousQuadrantNumber) {
if (quadrantNumber == 1 && previousQuadrantNumber == 4) numberOfTurns++;
if (quadrantNumber == 4 && previousQuadrantNumber == 1) numberOfTurns--;
previousQuadrantNumber = quadrantNumber;
}
if (totalDistance >= 0) totalDistance = (numberOfTurns * 360) + encoderValue;
else totalDistance = (numberOfTurns * 360) + encoderValue;
Input = totalDistance;
}
void runMotor() {
myPID.Compute();
if (Output > 1) {
pwmValue = Output;
if (pwmValue < 50 && pwmValue > 15) pwmValue = pwmValue + 40;
if (pwmValue <= 15) pwmValue = 0;
analogWrite(motor_IN1, pwmValue);
digitalWrite(motor_IN2, LOW);
} else if (Output < 1) {
pwmValue = abs(Output);
if (pwmValue < 50 && pwmValue > 15) pwmValue = pwmValue + 40;
if (pwmValue <= 15) pwmValue = 0;
digitalWrite(motor_IN1, LOW);
analogWrite(motor_IN2, pwmValue);
} else if (Output > -1 && Output < 1) {
pwmValue = 0;
digitalWrite(motor_IN1, LOW);
digitalWrite(motor_IN2, LOW);
}
}
Code-Erklärung
Loop startet mit Einlesen Encoder (Istposition in mm):
rodPosition = as5600.readAngle() / 0.001953125; // 8mm/4096 = 0,001953125 mm pro Step
Serial-Modus (modeSwitch=0): Serial Monitor auslesen. „save“ → aktuelle Position im Array speichern. „clear“ → Array löschen.
if (serialInput == "save") { positionsArray[positionsCounter] = totalDistance; ... }
if (serialInput == "clear") { memset(positionsArray, 0, sizeof(positionsArray)); ... }
Zahleneingabe (0–150) → Setpoint. Umrechnung mm → Grad: ×45 (1 mm = 45° Drehung bei 8 mm Steigung). Andere Steigung → anderer Faktor.
serialIntInput = serialInput.toInt();
if (serialIntInput != 0) { ... Setpoint = serialIntInput * 45; }
„run“ → gespeicherte Positionen in Schleife abfahren (while/for loops).
if (serialInput == "run") { while (serialInput != "stop") { for ... { while (positionsArray[i] > totalDistance+25 || ...) { Setpoint = positionsArray[i]; readEncoder(); runMotor(); } delay(2000); ... } } }
Potentiometer/RC-Modus (modeSwitch=1): Analog → analogRead(A0) → Setpoint mappen. Digital → pulseIn() von RC-Empfänger → Setpoint mappen.
if (digitalRead(inputSwitch) == 0) { desiredValue = analogRead(A0); ... Setpoint = map(...); }
else { desiredValue = pulseIn(ch1, HIGH, 30000); ... Setpoint = map(...); }
Dann readEncoder() und runMotor() für PID-Regelung.
readEncoder(): RAW-Encoder in Winkel (0–360°), Quadranten-Erkennung für Drehzahlverfolgung (Vollumdrehungen zählen). totalDistance = Input für PID.
Trimmer-Poti (A2) justiert P-Anteil der PID (auskommentiert). runMotor(): PID-Output → PWM für Motor links/rechts/stop.
double Pk1 = 0.65; double Ik1 = 0; double Dk1 = 0.1;
Qualität der Regelung hängt direkt von diesen Werten ab.
Test
Genauigkeitstest: Rückkehr zur Position gut. Millimeterweise Bewegung: 1. Schritt 0,8 mm (statt 1 mm), nächste 4 nahe 1 mm. 4 mm Bewegung ~0,15 mm Abweichung. Rückschlag (Backlash) ~0,25 mm (Spindel/Mutter) + 3D-Druck-Zahnräder + Motorgetriebe. Unter Last größere Abweichung – durch PID-Tuning verbesserbar.
Das war’s für dieses Tutorial. Hoffe, es hat gefallen und du hast was gelernt.
Herstellungsprozess
- SolidWorks Feder-Animation: Adaptive Federn in Nockenwellen-Baugruppe
- Home Energy Saver: Smarte Stromspar-Steckdose mit Particle Photon & Blynk
- Effizienz freisetzen: 6 Hauptvorteile von Palettenregalsystemen
- Umfassender Leitfaden zu mechanischen Lagern: Funktion, Typen und Anwendungen
- Windkraftanlagen: Typen, Vor- und Nachteile im Überblick
- Fortgeschrittenes Tischtennisball-Tracking mit Raspberry Pi und OpenCV
- Stoßdämpfer schweißen? Vor- & Nachteile und Sicherheitstipps
- Lichtbogen-Schweißen: Verfahren, Typen und Anwendungen
- Der komplette Leitfaden zu Sicherungen: Schutz Ihrer Elektronik
- Arduino Energiemonitor & Datenlogger (Dr. Wattson, MCP39F521) – Schritt-für-Schritt