Bouw een nauwkeurige lineaire servo-actuator met positie-terugkoppeling – Stapsgewijze handleiding
In deze tutorial leren we hoe je een lineaire servo-actuator maakt. In tegenstelling tot gewone lineaire actuatoren die in een bepaalde richting bewegen wanneer er spanning wordt aangelegd, biedt deze zelfgebouwde lineaire servo-actuator nauwkeurige en herhaalbare bewegingen die gemakkelijk te regelen zijn.
Het heet een servo-actuator omdat het een terugkoppelingssysteem heeft waarmee we de uitgaande beweging van de actuator nauwkeurig kunnen regelen.
Je kunt de volgende video bekijken of de geschreven tutorial hieronder lezen.
Overzicht
De invoer voor het besturen van deze lineaire servo-actuator kan zowel analoog als digitaal zijn. In het geval van een analoge invoer, kan het elke soort potentiometer zijn, zoals hier getoond. Een lineaire potentiometer, een gewone draaipotentiometer, of bijvoorbeeld een joystick, wat weer een draaipotentiometer is, enzovoort.
In het geval van digitale invoer, kunnen we de actuator besturen met een RC-zender. Natuurlijk hebben we voor deze opstelling ook een RC-ontvanger nodig die als invoer aan de actuator gaat.
Voor zowel deze analoge als digitale invoermodi, hebben we slechts 3 draden nodig om een verbinding te maken, 2 voor het voeden van het invoerapparaat, en de 3e voor het invoersignaal.
Een coole functie die deze zelfgebouwde lineaire servo-actuator heeft, is dat we een aangepaste start- en eindpositie voor de uitvoerstaaf kunnen instellen, evenals de gevoeligheid of hoe snel de actuator reageert op onze invoer.
Mijn favoriete functie is echter de mogelijkheid om deze actuator vanaf een PC of laptop via seriële poortcommunicatie te besturen. We kunnen waarden in millimeters invoeren via de Arduino IDE Serial Monitor en de actuator zal naar die positie bewegen.
Wat nog cooler is, we kunnen herhaalbare bewegingen maken, of posities opslaan, door "save" te typen op de seriële monitor op elke positie die we willen, en daarna de actuator de bewegingen in een lus laten herhalen, door "run" te typen op de seriële monitor.
Nu leg ik je alles uit wat je moet weten over deze zelfgebouwde lineaire servo-actuator, hoe het werkt en hoe ik het ontwierp, zodat jij er ook een kunt bouwen.
Hoe het werkt
Het gesloten-lus regelsysteem is gebaseerd op de AS5600 magnetische draaiende positie-sensor en de geïmplementeerde PID-regeling voor het aansturen van de DC-motor.
Eigenlijk gebruik ik dezelfde custom servo-motorcontrollerbord die ik in mijn vorige video maakte, wat zijn eigen microcontroller en alles bevat om elke DC-motor makkelijk om te zetten in een zelfstandige servo-motor.
Je kunt die tutorial bekijken voor een gedetailleerde uitleg van hoe een servo-motor en een gesloten-lus regelsysteem werkt.
Kortom, een servo-motor is een gesloten-lus regelsysteem waar het invoersignaal, of de gewenste positie, vergeleken wordt met de werkelijke positie van de motor die we krijgen van de positie-terugkoppelingssensor.
Het verschil dat ontstaat, de fout genoemd, wordt dan verwerkt in de controller die de motor opdraagt te bewegen tot hij de gewenste positie bereikt.
Dus, deze lineaire servo-actuator heeft hetzelfde werkingsprincipe als een servo-motor, maar met één extra stap: het omzetten van de draaiende beweging van de motor in een lineaire beweging met behulp van een spindelmechanisme.
Ontwerp lineaire servo-actuator
Hier is het 3D-model van deze lineaire servo-actuator, waaruit we kunnen zien hoe alles werkt.
De AS5600 magnetische draaiende positie-sensor zit aan de achterkant van de actuator, en houdt de rotatie van de spindel bij. De spindel die ik gebruik heb een deeltand van 8mm, wat betekent dat bij elke volledige rotatie de spindelmoer 8mm lineaire beweging maakt.
De AS5600 is een 12-bit encoder, wat betekent dat het 4096 posities per omwenteling kan uitvoeren. Als we 8 delen door 4096, krijgen we een resolutie van 0,001953mm. Dat is de kleinste positieverandering die de AS5600-encoder kan detecteren. Dat is indrukwekkend, vind ik.
De DC-motor die ik gebruik is een 12V-motor met ingebouwde reductiekast die 480 RPM levert. Als we 480 delen door 60, krijgen we 8 omwentelingen per seconde, en als we dat vermenigvuldigen met 8, omdat de spindel 8mm deeltand heeft, krijgen we een lineaire snelheid van 64mm/s van de actuator.
Ik vond dat perfect, want de maximale reik van deze actuatorstaaf is 150mm, dus het zou ongeveer 2,5s duren van start tot eindpositie bij maximaal snel, of ongeveer 3s als we versnelling en vertraging meerekenen. Daarom gebruikte ik een 1:1 tandwielverhouding voor het aandrijven van de spindel.
Het ontwerp van de hele lineaire actuator is gebaseerd op de grootte van de customgebouwde servo-motorcontroller PCB en de spindel en spindelmoer natuurlijk. De PCB had een afmeting van 40x40mm, dus dat was de minimumgrootte van de cilinderblok.
De 8mm spindelmoer had een buitenafmeting van 22mm, dus volgens die dimensie ontwierp ik de staaf. De moer en de staaf zijn verbonden met vier M3-bouten en gezwinde-inserts. Aan de top van de staaf zit een lager dat over het cilinderblok glijdt, en wordt gebruikt voor het leiden van de staaf en het voorkomen van rotatie.
Aan de uitvoercilinderdop zitten 4 kleine lagers die de staaf uit het cilinderblok leiden.
Over het algemeen vind ik de lineaire actuator compact genoeg, gegeven alle gebruikte componenten.
Ik wist ook een micro-limietschakelaar in het cilinderblok te passen, wat gebruikt wordt voor homing en het instellen van de startpositie van de actuator.
3D-model download
Je kunt het 3D-model van deze zelfgebouwde lineaire servo-actuator direct in je webbrowser bekijken en verkennen met Onshape. (Je hebt een Onshape-account nodig, je kunt gratis een account aanmaken voor thuisgebruik)
Je kunt de STL-bestanden voor 3D-printen, evenals het STEP-bestand van dit 3D-model, downloaden van Cults3D.
Nog een ding om te vermelden: je kunt de maximale reislengte van deze lineaire actuator eenvoudig vergroten, door simpelweg de lengte van het cilinderblok en de staaf te vergroten. Ik koos deze afmetingen omdat ik wilde dat alle onderdelen op een 3D-printer met een kleiner printbed van 220x220mm passen. Het grootste deel hier is de staaf, die 215mm lang is.
3D-printen
Mijn nieuwe Creality Ender-3 V3 SE deed een geweldige job met printen in horizontale oriëntatie langs de Y-as. Hoewel we een beetje moeten schuren, zal printen van de staaf in deze oriëntatie bijdragen aan soepelere werking en een sterkere staaf.
Bij 3D-printen is het belangrijk om de Horizontale Uitbreiding-functie in je slicer-software te gebruiken om de uitrekking van het filament te compenseren en dimensioneel nauwkeurigere onderdelen te krijgen.
Ik gebruikte een waarde van –0,1mm, maar je moet wel een paar testprints doen om te zien welke waarde bij jouw 3D-printer past.
Ik printte het cilinderblok langs de Z-as om veel supportmateriaal te vermijden. De Creality Ender-3 V3 SE deed ook hier een geweldige job.
Ik was aangenaam verrast door de printkwaliteit die deze 3D-printer biedt, gezien de prijs. Het is super makkelijk om de 3D-printer in te stellen, het heeft auto bed leveling, direct extruder, geweldige printkwaliteit en verhoogde printsnelheden van tot 250mm/s. Al dit voor net onder $200 maakt het een van de beste 3D-printers voor wie op budget zit.
Bekijk deze 3D-printer in de Creality-winkel of op Amazon. Bekijk ook mijn gedetailleerde review ervan op mijn website.
Monteren
Oké, hier zijn alle 3D-geprinte onderdelen, dus we kunnen nu beginnen met het monteren van de lineaire actuator.
Onderdelenlijst
Je kunt de benodigde componenten voor dit Lineaire Servo Actuator-project via de onderstaande links verkrijgen:
Mechanisch:
- Spindel 8mm Tr8x8 ………………………………………….. Amazon / AliExpress
- Kogellager 8x22x7mm ………………………………………. Amazon / AliExpress
- Kogellager 4x9x4mm x 4 …………………………………….. Amazon / AliExpress
- Kogellager 6x13x5mm 686-2RS x1 ……………..….. Amazon / AliExpress
- M3x5mm en M4x5mm gezwinde-inserts ……….. Amazon / AliExpress
- M3 en M4 bouten en moeren ……………….…………….…. Amazon / AliExpress
Bouten: M3x8 – 10st; M3x10 – 2st; M3x10 ingesenkt – 1st; M4x6 – 2st; M4x25 – 2st; M4x30 – 2st; M2x8 – 6st; M4x5 zeskantschroef – 5st
Elektronica:
- AS5600 magnetische encoder ………………….. Amazon / AliExpress
- DRV8871 DC-motordriver ……………….…. Amazon / AliExpress
- Atmega328p-AU …………………………………. Amazon / AliExpress
- 16Mhz kristaloscillator …………………….. Amazon / AliExpress
- AMS1117 5V spanningsregelaar ………..…. Amazon / AliExpress
- 3386P vierkante potentiometer ………………. Amazon / AliExpress
- Condensatoren 0805 kit …………………………….…. Amazon / AliExpress
- 12V DC-motor – ~ 50RPM …………….……. Amazon / AliExpress
Disclaimer: Dit zijn affiliatelinks. Als Amazon Associate verdien ik aan kwalificatieve aankopen.
Monteren van het spindelmechanisme
Eerst moeten we de spindel op zijn plaats in het cilinderbasisblok installeren. Daartoe moeten we eerst deze 3D-geprinte moer plaatsen die hetzelfde gewinde heeft als de spindel.
Het is een beetje lastig om de moer op de spindel te schroeven omdat het een strakke pasvorm is, maar dat is wat we hier nodig hebben. Deze moer houdt de hele kracht vast wanneer de staaf duwt, dus hoe strakker de pasvorm, hoe meer kracht het kan aan. Daarnaast heeft de moer ook een gat voor een gezwinde-insert om het met een zeskantschroef op de as te bevestigen.
De spindel wordt op zijn plaats in het cilinderbasisblok gehouden met behulp van twee kogellagers met 22mm buitendiameter.
Aan de achterkant zit het tandwiel dat de spindel aandrijft. Dit tandwiel heeft ook een passend gewinde en twee gaten voor gezwinde-inserts om het met zeskantschroeven op de spindel te bevestigen.
Deze verbinding is ook cruciaal omdat het het hele koppel van de motor naar de spindel overdraagt, dus het mag niet glijden.
Om deze submontage te maken, moeten we eerst de gezwinde-inserts op hun plaats op het tandwiel en de moer plaatsen, evenals enkele op het cilinderblok.
We schroeven het tandwiel en de moer in tegengestelde richting, maar niet te strak, want dat voegt axiale krachten toe aan de lagers. Vervolgens kunnen we met enkele zeskantschroeven de moer en het tandwiel aan de spindel bevestigen.
Eenmaal bevestigd, merken we dat de spindel nog niet vast zit. We moeten deze plaat aan het cilinderblok toevoegen, die ervoor zorgt dat de lagers op hun plaats in het cilinderblok blijven.
Dit voltooit deze submontage; de spindel staat nu stevig op zijn plaats, terwijl hij vrij kan draaien.
Vervolgens kunnen we de staaf voorbereiden. De staaf is hol door zijn hele lengte om de spindel te accommoderen. Voor het verbinden van de spindelmoer en de staaf, moeten we eerst enkele gezwinde-inserts installeren.
Aan de bovenzijde van de staaf moeten we het leidingslager met 13mm buiten- en 6mm binnendiameter installeren.
We leggen het lager op een 3D-geprinte 6mm holle as en bevestigen het aan de staaf met een M3 ingesenkte schroef van 10mm lengte. De staaf is nu klaar en we zien hoe hij in de leidingsrails van de cylinder glijdt.
Vervolgens moeten we de cylinder aan het cilinderbasisblok bevestigen. Voordat we dat doen, moeten we de micro-limietschakelaar op zijn plaats installeren.
Eerst moeten we de draden aan het solderen, op de NC-verbinding. De draden moeten ongeveer 15cm lang zijn. De draden worden door een gat aan de top van de cylinder geleid, en dan kunnen we de micro-limietschakelaar aan de cylinder bevestigen met behulp van twee M2-bouten van 8mm lengte.
Je hebt specifiek deze micro-limietschakelaar nodig, zodat het leidingslager het op het juiste moment activeert, zonder ergens anders tegen aan te botsen.
Als je het exacte limietschakelaarmodel niet kunt vinden, kun je natuurlijk de gaten en het mechanisme aanpassen.
Om de cylinder aan het basisblok te bevestigen, moeten we hier enkele gezwinde-inserts installeren. Dan kunnen we het met twee M4-bouten van 25mm lengte bevestigen. We moeten op dit moment alleen de bovenste twee bouten inplaatsen.
De onderste twee gaan later, bij het installeren van de tandwielkast en PCB-hoes, omdat dezelfde gaten gebruikt worden voor het bevestigen van de hoes.
Vervolgens schroeven we de staaf in de spindel. Het leidingslager moet tussen de leidingsrails op de cylinder zitten.
Door het tandwiel aan de achterkant te draaien, zal de staaf achterwaarts bewegen, totdat het de micro-limietschakelaar bereikt.
Dan kunnen we de cilinderdop op zijn plaats zetten. De cilinderdop zal vier kleine lagers met 9mm buitendiameter accommoderen. De assen voor deze lagers kunnen 3D-geprint worden.
We moeten voorzichtig zijn bij het inplaatsen, want het deel waar deze assen in gaan, is vrij klein en kan gemakkelijk breken. Het is me een paar keer gebeurd, dus zorg dat ze gemakkelijk passen. Deze lagers zullen de staaf ondersteunen en leiden voor soepelere werking.
De cilinderdop wordt met vier M4-bouten bevestigd.
Installeren van de DC-motor
Oké, nu kunnen we de DC-motor op zijn plaats zetten. We bevestigen de DC-motor met zes M3-bouten. Vervolgens installeren we het tandwiel op de motoras.
Voor de bevestiging van het tandwiel gebruiken we twee gezwinde-inserts en zeskantschroeven.
Eenmaal de tandwielen goed gepaard, kunnen we doorgaan met het bevestigen van de tandwielen en PCB-hoes aan de achterkant van de lineaire actuator. Daartoe moeten we eerst nog een paar gezwinde-inserts in het cilinderbasisblok installeren.
Dan kunnen we de draden aan de DC-motor aansluiten. In mijn geval soldeerde ik ze direct aan de DC-motor.
De lengte van de draden moet ongeveer 20cm zijn. Er zit een gat in het cilinderbasisblok waar we de draden van zowel de DC-motor als de limietschakelaar doorheen moeten halen.
Daarna moeten we ze ook door twee houders op de hoes halen, die ervoor zorgen dat ze van het tandwiel afblijven.
Op dit moment kunnen we de hoes aan het basisblok bevestigen. Daartoe moeten we eerst de twee M4-bouten aan de onderkant inplaatsen, maar niet helemaal.
We moeten ongeveer 2mm of 3mm laten zitten, zodat we de hoeshouder ertussen kunnen plaatsen, en dan die bouten samen met de hoes vastdraaien.
Deze hele operatie is een beetje rommelig, maar het moest zo, omdat ik de hoes zo klein mogelijk wilde en als één print, en de PCB-houders de weg van de bouten blokkeerden.
Installeren van de custom servo-controller PCB
Oké, zodra we klaar zijn met de hoes, kunnen we de custom servo-motorcontrollerbord op zijn plaats installeren. Zoals gezegd, het is dezelfde controller uit mijn vorige video waar ik liet zien hoe je elke DC-motor in een servo-motor kunt omzetten.
Het hoofdcomponent hier is de AS5600 magnetische draaiende positie-encoder, die de hoekpositie van de magneet die aan de uitvoeras zit, bijhoudt. In dit geval zullen we de magneet aan het uitvoertandwiel op de spindel bevestigen. De magneet wordt perfect uitgelijnd met de AS5600-sensor wanneer de PCB op zijn plaats gaat.
Voor het bevestigen van de PCB moeten we eerst M2-moeren in de houdersleuven plaatsen, en dan de PCB met vier M2-bouten vastdraaien.
Wat er nog over is, is de draden op hun plaats verbinden. De DC-motordraden gaan naar de motorklemblok, en de polariteit moet additioneel gecheckt worden om te matchen met het controllerprogramma.
Eigenlijk, voordat we de motor aan de PCB koppelen, kunnen we er wat spanning op geven om te checken of het spindelmechanisme goed werkt.
Wat de limietschakelaardraden betreft, aangezien ik geen speciale pinnen daarvoor heb, soldeerde ik de massadraad aan de massapad op de elektrolytische condensator, en de NC-verbinding draad aan de SCK-pin, wat digitale pin 13 is op de ATMEGA328-microcontroller.
De klemblok voor de voeding zit rechts naast de kant van de hoes, dus er is een gat waar ik een 5,5mm voedingsconnector aan aansloot.
Ik voegde ook een heatsink toe aan de DC-motordriver. Ten slotte zetten we de snap-fit deksel aan de achterkant, en dat is het, we zijn klaar met dit project.
Nu kunnen we elke soort potentiometer of RC-ontvanger aan de geschikte ingangspinnen koppelen, en we kunnen de positie van de lineaire actuator ermee besturen.
Zoals ik al zei, in mijn vorige video legde ik uit hoe deze controller werkt, het schakelschema en hoe ik de PCB maakte.
Dus, je moet die tutorial bekijken als je deze controllerbord wilt maken.
Schakelschema
Kortom, het hoofdcomponent is de AS5600 magnetische sensor, die de positie van de actuatoruitgang bijhoudt. De sensordata gaat naar de hersenen van deze servo-controllerbord, de Atmega328-microcontroller, die de wiskunde doet, en de DRV8871 DC-motordriver vertelt hoe de DC-motor aangestuurd moet worden.
De DRV8871 DC-motordriver kan tot 3,6A piekstroom aan. Voor het voeden van het bord kunnen we 12V gebruiken, wat dan naar 5V wordt gebracht met een ASM1117-spanningsregelaar voor de Atmega328 en de andere 5V-componenten. Er is een twee-kanaals DIP-schakelaar waarmee we de invoermodus van de actuator selecteren, ofwel analoog of digitaal, of via de seriële poortcommunicatie.
Eén van de trim-potentiometers wordt gebruikt voor het instellen van de gevoeligheid van de actuator, en de SDM-drukknop voor het instellen van de start- en eindposities.
Ik bestelde de PCB bij PCBWay. Hier kunnen we simpelweg het Gerber-bestand uploaden, de eigenschappen van onze PCB kiezen, en het tegen een redelijke prijs bestellen.
Ik ontwierp de PCB met 4 lagen, de middelste zijn voor GND, wat de prijs iets verhoogt. Ik veranderde geen van de standaardeigenschappen, behalve de PCB-kleur die ik wit koos, en ik vinkte aan dat ik akkoord ga met wijziging van de oppervlakteafwerking naar Immersion gold als van toepassing zonder extra kosten.
Je kunt het Gerber-bestand vinden en downloaden via de PCBWay project-deelcommunity, waar je de PCB ook direct kunt bestellen.
Natuurlijk kun je dit lineaire actuator-project ook maken zonder deze customgebouwde servo-controller, natuurlijk.
Je kunt de AS5600-sensor op een breakout-bord gebruiken in combinatie met een Arduino-bord voor het besturen van de DC-motor.
Code
Nu kijken we naar de code van deze Lineaire Servo Actuator:
/* Lineaire Servo Actuator - Arduino-code door Dejan, www.HowToMechatronics.com
Bibliotheken:
AS5600 encoder: https://github.com/RobTillaart/AS5600
PID-regelaar: https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h */
#include "AS5600.h"
#include "Wire.h"
#include
AS5600 as5600; // standaard Wire
double Pk1 = 0.65; // snelheid waarmee hij er komt
double Ik1 = 0;
double Dk1 = 0.1;
// Variabelen definiëren die we zullen koppelen
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 om invoer te houden
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);
// Activeer Arduino interne pull-up weerstanden
pinMode(setButton, INPUT_PULLUP);
pinMode(inputSwitch, INPUT_PULLUP);
pinMode(modeSwitch, INPUT_PULLUP);
pinMode(limitSwitch, INPUT_PULLUP);
// PID-instelling
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-255, 255);
myPID.SetSampleTime(20);
// --- HOMING ----
// Beweeg achterwaarts tot je ...
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 Einde ---
}
void loop() {
// Lees encoder-waarde - huidige positie
rodPosition = as5600.readAngle() / 0.001953125; // in millimeters - De spindel is 8mm per rotatie, en de encoder RAW-waarde is 4096 per rotatie, dus 8/4096 om de waarde in millimeters te krijgen
// Seriële communicatiemodus - Lees data van de seriële monitor
if (digitalRead(modeSwitch) == 0) {
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
// Als "save" string via seriële monitor wordt gestuurd, bewaar de huidige rodPosition in de array
if (serialInput == "save") {
positionsArray[positionsCounter] = totalDistance;
delay(1000);
positionsCounter++;
}
// Wis opgeslagen posities
if (serialInput == "clear") {
// Wis arraydata naar 0
memset(positionsArray, 0, sizeof(positionsArray));
positionsCounter = 0;
}
// Converteer String naar Integer en gebruik als Setpoint voor PID-regeling
serialIntInput = serialInput.toInt();
if (serialIntInput != 0) {
if (serialIntInput < 0) {
serialIntInput = 0;
}
if (serialIntInput > 150) {
serialIntInput = 150;
}
Setpoint = serialIntInput * 45; // converteer mm naar graden (1mm lineaire beweging = 45 graden rotatiebeweging)
}
}
// Draai opgeslagen posities
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) {
// Gewenste positie / setpoint voor PID-controller
Setpoint = positionsArray[i];
// Lees encoder - gebruik die waarde als Input voor PID-regeling
readEncoder();
// Draai motor - PID-controller in runMotor();
}
delay(2000); // Vertraging tussen stappen
// Check seriële monitor voor stop-commando
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
}
}
}
}
}
// Potentiometer en RC-ontvanger regeling-modus
else if (digitalRead(modeSwitch) == 1) {
if (digitalRead(inputSwitch) == 0) {
// Analog invoer - Potentiometer
// Haal waarde van 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) {
// Digitale invoer - RC-zender
desiredValue = pulseIn(ch1, HIGH, 30000); // Lees RC-ontvanger als invoer
if (desiredValue < 1000 || desiredValue > 2000) {
desiredValue = 1000;
}
Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
}
}
// Beperk minimum en maximum waarden van setpoint
if (Setpoint > endPosition) {
Setpoint = endPosition;
}
if (Setpoint < startPosition) {
Setpoint = startPosition;
}
// Gevoeligheid aanpassen
//Pk1 = analogRead(A2) * 0.002; // Pas PID P-term aan
//myPID.SetTunings(Pk1, Ik1, Dk1);
// Lees encoder - gebruik die waarde als input voor PID-regeling
readEncoder();
// Draai motor
runMotor();
// Start- en eindposities instellen door op "set" knop te drukken
if (digitalRead(setButton) == LOW) {
delay(3000);
if (digitalRead(setButton) == LOW) {
endPosition = totalDistance;
while (digitalRead(setButton) != HIGH);
} else {
startPosition = totalDistance;
}
}
}
void readEncoder() {
// Converteer encoder RAW-waarden naar hoekwaarden voor het bijhouden van de hoekpositie van de as
encoderValue = as5600.readAngle() * 0.087890625;
// Kwadrant 1
if (encoderValue >= 0 && encoderValue <= 90) {
quadrantNumber = 1;
}
// Kwadrant 2
if (encoderValue >= 90 && encoderValue <= 180) {
quadrantNumber = 2;
}
// Kwadrant 3
if (encoderValue >= 180 && encoderValue <= 270) {
quadrantNumber = 3;
}
// Kwadrant 4
if (encoderValue >= 270 && encoderValue <= 360) {
quadrantNumber = 4;
}
if (quadrantNumber != previousQuadrantNumber) {
// Overgang van 4e naar 1e kwadrant
if (quadrantNumber == 1 && previousQuadrantNumber == 4) {
numberOfTurns++;
}
// Overgang van 1e naar 4e kwadrant
if (quadrantNumber == 4 && previousQuadrantNumber == 1) {
numberOfTurns--;
}
previousQuadrantNumber = quadrantNumber;
}
if (totalDistance >= 0) {
totalDistance = (numberOfTurns * 360) + encoderValue;
} else {
totalDistance = (numberOfTurns * 360) + encoderValue;
}
// Stel Input-waarde voor PID vast
Input = totalDistance; // huidige waarde van de positie
}
void runMotor() {
// Voer PID-proces uit om Output-waarde te krijgen
myPID.Compute();
// Beweeg rechts
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);
}
// Beweeg links
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);
}
// Beweeg niet
else if (Output > -1 && Output < 1) {
pwmValue = 0;
digitalWrite(motor_IN1, LOW);
digitalWrite(motor_IN2, LOW);
}
}
Codebeschrijving
We starten de lus met het lezen van de encoder-waarde of de huidige positie van de actuator en converteren die naar millimeters.
// Lees encoder-waarde - huidige positie
rodPosition = as5600.readAngle() / 0.001953125; // in millimeters - De spindel is 8mm per rotatie, en de encoder RAW-waarde is 4096 per rotatie, dus 8/4096 om de waarde in millimeters te krijgen
Dan, als we in de "Seriële communicatiemodus" zitten, lezen we de binnenkomende data die we op de seriële monitor invoeren. Als de invoer "save" is, slaan we de huidige actuatorpositie op, of als het "clear" is, wissen we alle al opgeslagen posities.
// Seriële communicatiemodus - Lees data van de seriële monitor
if (digitalRead(modeSwitch) == 0) {
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
// Als "save" string via seriële monitor wordt gestuurd, bewaar de huidige rodPosition in de array
if (serialInput == "save") {
positionsArray[positionsCounter] = totalDistance;
delay(1000);
positionsCounter++;
}
// Wis opgeslagen posities
if (serialInput == "clear") {
// Wis arraydata naar 0
memset(positionsArray, 0, sizeof(positionsArray));
positionsCounter = 0;
}
Als de invoer een integer of getal is, van 0 tot 150, gebruiken we die waarde als setpoint.
// Converteer String naar Integer en gebruik als Setpoint voor PID-regeling
serialIntInput = serialInput.toInt();
if (serialIntInput != 0) {
if (serialIntInput < 0) {
serialIntInput = 0;
}
if (serialIntInput > 150) {
serialIntInput = 150;
}
Setpoint = serialIntInput * 45; // converteer mm naar graden (1mm lineaire beweging = 45 graden rotatiebeweging)
}
}
We voeren de waarden in millimeters in, maar voor het bijhouden van de draaiende as gebruiken we graden, dus we converteren de millimeterwaarden naar gradenwaarden door te vermenigvuldigen met 45. Dat is zo, omdat voor 1mm lineaire beweging de spindel 45 graden moet draaien. Als je een andere deeltand op je spindel hebt, moet dit getal anders zijn.
Als we "run" typen, met behulp van enkele while- en for-lussen, zal het programma door de opgeslagen posities heen lopen in een herhalende lus.
// Draai opgeslagen posities
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) {
// Gewenste positie / setpoint voor PID-controller
Setpoint = positionsArray[i];
// Lees encoder - gebruik die waarde als Input voor PID-regeling
readEncoder();
// Draai motor - PID-controller in runMotor();
}
delay(2000); // Vertraging tussen stappen
// Check seriële monitor voor stop-commando
while (Serial.available() > 0) {
serialInput = Serial.readString();
serialInput.trim();
}
}
}
}
Aan de andere kant, als we in de Potentiometer- en RC-ontvanger-regelmodus zitten, checken we of we analoog of digitaal invoer hebben.
// Potentiometer en RC-ontvanger regeling-modus
else if (digitalRead(modeSwitch) == 1) {
if (digitalRead(inputSwitch) == 0) {
// Analog invoer - Potentiometer
// Haal waarde van 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) {
// Digitale invoer - RC-zender
desiredValue = pulseIn(ch1, HIGH, 30000); // Lees RC-ontvanger als invoer
if (desiredValue < 1000 || desiredValue > 2000) {
desiredValue = 1000;
}
Setpoint = map(desiredValue, 1000, 2000, startPosition, endPosition);
}
}
Als analoog, lezen we de analoge invoer van de potentiometer en gebruiken die waarde als setpoint, of gewenste positie voor de actuator. Op dezelfde manier, als de invoer digitaal is, lezen we de binnenkomende data van de RC-ontvanger en gebruiken die waarde als setpoint.
Dan roepen we de readEncoder() en runMotor() aangepaste functies aan om de huidige positie van de actuator te lezen en de PID-regeling uit te voeren. Met de readEncoder()-functie lezen we de huidige waarde van de sensor in hoekwaarden, en met deze if-statements houden we bij in welk kwadrant de huidige positie van de as zit.
void readEncoder() {
// Converteer encoder RAW-waarden naar hoekwaarden voor het bijhouden van de hoekpositie van de as
encoderValue = as5600.readAngle() * 0.087890625;
// Kwadrant 1
if (encoderValue >= 0 && encoderValue <= 90) {
quadrantNumber = 1;
}
// Kwadrant 2
if (encoderValue >= 90 && encoderValue <= 180) {
quadrantNumber = 2;
}
// Kwadrant 3
if (encoderValue >= 180 && encoderValue <= 270) {
quadrantNumber = 3;
}
// Kwadrant 4
if (encoderValue >= 270 && encoderValue <= 360) {
quadrantNumber = 4;
}
if (quadrantNumber != previousQuadrantNumber) {
// Overgang van 4e naar 1e kwadrant
if (quadrantNumber == 1 && previousQuadrantNumber == 4) {
numberOfTurns++;
}
// Overgang van 1e naar 4e kwadrant
if (quadrantNumber == 4 && previousQuadrantNumber == 1) {
numberOfTurns--;
}
previousQuadrantNumber = quadrantNumber;
}
if (totalDistance >= 0) {
totalDistance = (numberOfTurns * 360) + encoderValue;
} else {
totalDistance = (numberOfTurns * 360) + encoderValue;
}
// Stel Input-waarde voor PID vast
Input = totalDistance; // huidige waarde van de positie
}
Met deze informatie kunnen we bijhouden hoe de as draait en wanneer hij een volle draai maakt. De totale hoek is de Input-waarde voor de PID-controller.
Met de analoge invoer van de trim-potentiometer kunnen we de proportionele winst van de PID-controller aanpassen, en ten slotte voeren we het PID-proces uit om een uitgangswaarde te krijgen.
// Gevoeligheid aanpassen
//Pk1 = analogRead(A2) * 0.002; // Pas PID P-term aan
//myPID.SetTunings(Pk1, Ik1, Dk1);
void runMotor() {
// Voer PID-proces uit om Output-waarde te krijgen
myPID.Compute();
// Beweeg rechts
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);
}
// Beweeg links
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);
}
// Beweeg niet
else if (Output > -1 && Output < 1) {
pwmValue = 0;
digitalWrite(motor_IN1, LOW);
digitalWrite(motor_IN2, LOW);
}
}
We gebruiken die uitgangswaarde voor het aansturen van de DC-motoren met PWM-signaal, links of rechts, of in stilstand, afhankelijk van de uitgangswaarde van de PID-controller, of de fout tussen de gewenste en de werkelijke positie die de encoder leest.
De drie termen van de PID-controller, de proportionele, integrale en afgeleide, zijn bovenaan gedefinieerd, en door ze aan te passen kunnen we verschillende uitgangsresponsen krijgen.
double Pk1 = 0.65; // snelheid waarmee hij er komt
double Ik1 = 0;
double Dk1 = 0.1;
De kwaliteit, hoe goed de actuator zal werken of reageren op onze invoer, hangt direct af van deze waarden.
Testen
Hier test ik de nauwkeurigheid van de actuator. Hij keert prima terug op zijn plek. Daarna begon ik de staaf millimeter voor millimeter te bewegen. De eerste beweging was ongeveer 0,8mm in plaats van 1mm, maar de volgende 4 waren redelijk dicht bij 1mm. De 4mm beweging was ongeveer 0,15mm af.
We moeten opmerken dat de staaf een achterhaal (backlash) heeft van ongeveer 0,25mm. Deze achterhaal zit tussen de spindel en de spindelmoer. Daarnaast hebben we waarschijnlijk ook wat achterhaal in de 3D-geprinte tandwielen alsook de tandwielen van de DC-motor zelf.
Als we een kracht op de staaf uitoefenen en de nauwkeurigheid nu testen, zullen we natuurlijk nog grotere fouten krijgen, maar dit kan verbeterd worden door de PID-controller te tunen.
Toch was dit alles voor deze tutorial. Ik hoop dat je ervan genoten hebt en iets nieuws geleerd hebt.
Productieproces
- Een Arduino Tic-Tac-Toe Spel op een Touchscreen Bouwen: Stap-voor-Stap Handleiding
- De Pirani-manometer uitgelegd: werking, toepassingen en voordelen
- Arduino-gestuurde indoor tuin: Slimme, geautomatiseerde plantenverzorging
- Handmatige bandzaag: duurzame, draagbare snijoplossing voor elk project
- Belangrijkste Kenmerken van een Modern Warmbandwalzwerk voor Hoge Prestaties
- Pon & Matrijs Chipping Voorkomen en Repareren: Bewezen Oplossingen
- Een HC-SR04 ultrasone sensor verbinden met een Raspberry Pi – Een praktische gids
- Spray Lassen Uitgelegd: Processen, Technieken & Veiligheidsrichtlijnen
- Dieptrekpers: Superieure presterende poinçonnage voor plaatmetaalbewerking
- Een Horizontaal Bewerkingscentrum Kiezen: Kosten, Functies en Belangrijke Overwegingen
- Schroef vs. Bout: Belangrijkste Verschillen, Typen en Toepassingen