Weizen Eingießanlage: Unterschied zwischen den Versionen
(29 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 65: | Zeile 65: | ||
Datei:Technischer Systementwurf Weizen Eingiesanlage.png | Technischer Systementwurf | Datei:Technischer Systementwurf Weizen Eingiesanlage.png | Technischer Systementwurf | ||
Datei: RegelKreis_Weizenglas.png| Weizen Eingießanlage Regelkreis | Datei: RegelKreis_Weizenglas.png| Weizen Eingießanlage Regelkreis | ||
</gallery> | |||
'''Ansatz zur Herleitung der Übertragungsfunktion:''' | |||
<gallery widths="500" heights="500"> | |||
Datei:ÜbertragungsfunktionHerleitungAnsatz.png | Ansatz zur Herleitung der Übertragungsfunktion | |||
</gallery> | </gallery> | ||
<!-- Füllen Sie Ihre Projektskizze bis hierher aus. Fügen Sie einen Projektplan unten ein. --> | <!-- Füllen Sie Ihre Projektskizze bis hierher aus. Fügen Sie einen Projektplan unten ein. --> | ||
Zeile 84: | Zeile 90: | ||
|- | |- | ||
| 2 | | 2 | ||
| | | DM442 Motortreiber | ||
| | | | ||
* Vollschritt Einstellung | * Vollschritt Einstellung | ||
Zeile 95: | Zeile 101: | ||
* Versorgungsspannung 5V | * Versorgungsspannung 5V | ||
* Messbereich: bis zu 5Kg | * Messbereich: bis zu 5Kg | ||
* 24 Bit ADC | |||
|- | |- | ||
| 4.1 | | 4.1 | ||
Zeile 126: | Zeile 133: | ||
Die Konstruktion der Halterung ist auf den ersten Blick sehr simpel. Dennoch galt es für uns die Kosten sowie das Gewicht niedrig zu halten. Daher belaufen sich fast alle Komponenten auf Holz und Kunststoff. Für die meisten Komponenten konnten wir auf unser Betriebsinternes Lager zurückgreifen lediglich die Holzplatte und die Halterungen für Flasche und Glas waren noch nicht vorhanden. Die Halterungen für Glas und Flasche wurden durch einen 3D-Kunststoffdruck gefertigt. Zudem mussten wir uns überlegen wie wir die Flasche in der Halterung befestigen. Um auch hier eine günstige, einfache und leichte Lösung zu erreichen, setzen wir auf die Spannkraft zweier Gummis, welche die Flasche im Halter halten. | Die Konstruktion der Halterung ist auf den ersten Blick sehr simpel. Dennoch galt es für uns die Kosten sowie das Gewicht niedrig zu halten. Daher belaufen sich fast alle Komponenten auf Holz und Kunststoff. Für die meisten Komponenten konnten wir auf unser Betriebsinternes Lager zurückgreifen lediglich die Holzplatte und die Halterungen für Flasche und Glas waren noch nicht vorhanden. Die Halterungen für Glas und Flasche wurden durch einen 3D-Kunststoffdruck gefertigt. Zudem mussten wir uns überlegen wie wir die Flasche in der Halterung befestigen. Um auch hier eine günstige, einfache und leichte Lösung zu erreichen, setzen wir auf die Spannkraft zweier Gummis, welche die Flasche im Halter halten. | ||
'''Wägezelle HX711:''' | '''Wägezelle HX711:''' | ||
Messprinzip: Durch das zu messende Gewicht wird die Wägezelle leicht gebogen. An der Seite befinden sich 4 Dehnungsmesstreifen, welche als Wheatsstone Brücke angeordnet sind. Der Widerstand der Messstreifen ändert sich mit dem Dehnungsgrad. Somit ändert sich bei einer Last der Widerstand und damit auch der Spannungsabfall der Brückenschaltung. Der Spannungsabfall zwischen den Punkten A und B wird mit einem A/D Wandler ausgewertet. | |||
<gallery widths="500" heights="500"> | |||
Datei:Messschaltung HX711 DMS.png | - Messchaltungsaufbau | |||
</gallery> | |||
Da die Wägezelle über die physikalische Kraft der Gewichtskraft funktioniert, war es eine Herausforderung in unserem drehenden System die korrekten Werte für den Füllstand zu erhalten. Dies war deshalb so schwierig weil sich die Wägezelle immer in einem anderen Winkel zur Gewichtskraft des Füllstandes befindet. Aus diesem Grund mussten wir die Messwerte noch einmal mit dem cos des Winkels multiplizierten um die korrekte Gewichtskraft zu erhalten. Wir verwenden außerdem einen Look-up-Table um uns die Verarbeitung der Messdaten zu vereinfachen. Konkret subtrahieren wir unseren gemessenen wert mit dem Wert aus dem Look-up-Table an der gleichen Winkelstellung. Somit Erhalten wir die korrekte Gewichtskraft des Pegelstandes des Glases. Dies ist dann dementsprechend unser IST-Wert. | Da die Wägezelle über die physikalische Kraft der Gewichtskraft funktioniert, war es eine Herausforderung in unserem drehenden System die korrekten Werte für den Füllstand zu erhalten. Dies war deshalb so schwierig weil sich die Wägezelle immer in einem anderen Winkel zur Gewichtskraft des Füllstandes befindet. Aus diesem Grund mussten wir die Messwerte noch einmal mit dem cos des Winkels multiplizierten um die korrekte Gewichtskraft zu erhalten. Wir verwenden außerdem einen Look-up-Table um uns die Verarbeitung der Messdaten zu vereinfachen. Konkret subtrahieren wir unseren gemessenen wert mit dem Wert aus dem Look-up-Table an der gleichen Winkelstellung. Somit Erhalten wir die korrekte Gewichtskraft des Pegelstandes des Glases. Dies ist dann dementsprechend unser IST-Wert. | ||
Zeile 132: | Zeile 145: | ||
'''PI-Regler:''' | '''PI-Regler:''' | ||
Der IST-Wert wird nun mit dem SOLL-Wert von 500 (500ml/Kg) subtrahiert. Somit erhalten wir unsere Regelabweichung welche wir weiterhin mit dem PI-Regler verarbeiten. Aufgrund dessen, dass 1-Motorschritt in 0,39 Grad am äußeren Umfang der Holzplatte entspricht benötigen wir eine sehr schnelle Regulierung der Motorschritte, sobald Flüssgkeit in das Glas läuft. Daher benötigen wir eine möglichst schnelle Sprungantwort und somit einen großen P-Anteil in unserem Regler. Für unser System haben sich die Werte Kp=0, | Der IST-Wert wird nun mit dem SOLL-Wert von 500 (500ml/Kg) subtrahiert. Somit erhalten wir unsere Regelabweichung welche wir weiterhin mit dem PI-Regler verarbeiten. Aufgrund dessen, dass 1-Motorschritt in 0,39 Grad am äußeren Umfang der Holzplatte entspricht, benötigen wir eine sehr schnelle Regulierung der Motorschritte, sobald Flüssgkeit in das Glas läuft. Daher benötigen wir eine möglichst schnelle Sprungantwort und somit einen großen P-Anteil in unserem Regler. Für unser System haben sich die Werte Kp=0,014 und Ki=0,00675 als praktikabel erwiesen. Die Stellgröße des Reglers wird direkt als Ansteuerung für den Motor genutzt. | ||
'''Motoransteuerung:''' | '''Motoransteuerung:''' | ||
Zeile 144: | Zeile 157: | ||
- '''PUL:''' Über diesen Pin wird ein PWM-Signal gesendet, welches das Drehmoment und die Geschwindigkeit bestimmt. | - '''PUL:''' Über diesen Pin wird ein PWM-Signal gesendet, welches das Drehmoment und die Geschwindigkeit bestimmt. | ||
'''Verdrahtungsplan:''' | |||
<gallery widths="500" heights="500"> | |||
Datei:Verdrahtungsplan Eingießanlage.png| Verdrahtungsplan Eingießanlage - [[Datei:Verdrahtungsplan Hopfomat.zip|mini]] | |||
</gallery> | |||
=== Software === | |||
'''Programmablaufplan:''' | |||
[[Datei:Berechnung Regelabweichung.png|mini]] | |||
<gallery widths="500" heights="500"> | <gallery widths="500" heights="500"> | ||
Datei: | Datei:Hopfomat PAP.png| Programmablaufplan - [[Datei:HopfomatPAP.zip|mini]] | ||
</gallery> | </gallery> | ||
''' | '''Simulink Regler Model:''' | ||
<gallery widths="500" heights="500"> | |||
[[Datei:Simulink-Regler-Final.png| Weizenglas Eingießanlage - Regler - Simulink - [[Datei:Simulink Regler.zip|mini]]]] | |||
</gallery> | |||
{| class="mw-datatable" | |||
! style="font-weight: bold;" | | |||
! style="font-weight: bold;" | | |||
|+ style = "text-align: left"| | |||
|- | |||
|[[Datei:Berechnung Regelabweichung.png|300px|mini|links|Berechnung der Regelabweichung Subsystem]] | |||
|[[Datei:PI-Regler.png|300px|mini|rechts|PID-Regler Subsystem]] | |||
|} | |||
'''DM442.h:''' | |||
<div style="width:1100px; height:500px; overflow:scroll; border: hidden"> | |||
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger"> | |||
#ifndef DM442_h | |||
#define DM442_h | |||
#define RIGHT (bool) true | |||
#define LEFT (bool) false | |||
//Motor Driver Leadshine DM442 | |||
class DM442 | |||
{ | |||
public: | |||
void begin(int dir, int ena, int pul); | |||
void start(); | |||
void stop(); | |||
//Use the defines for setting the direction | |||
void setDirection(bool direction); //sets the rotating direction | |||
void setFrequency(int stepsPerSecond); //sets the speed | |||
void setDutyCycle(int percent); //sets the torque | |||
void resetSteps(); //Reset of counted steps | |||
void singleStep(); //Drive one step | |||
void driveSteps(int steps); //Drive many steps | |||
long getSteps(); //Get driven steps | |||
private: | |||
int dir; //Direction-Pin address | |||
int ena; //Enable-Pin address | |||
int pul; //Pulse-Pin address | |||
int frequency; //highs per second | |||
int dutyCycle; //in % | |||
bool running; //Motor can drive | |||
bool direction; //motor rotates in direction TRUE = right | FALSE = left viewed from the front | |||
long steps; //positive value for right steps | negative for left steps | |||
}; | |||
#endif | |||
</syntaxhighlight> | |||
</div> | |||
'''DM442.cpp:''' | |||
<div style="width:1100px; height:500px; overflow:scroll; border: hidden"> | |||
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger"> | |||
#include "DM442.h" | |||
#include "Arduino.h" | |||
void DM442::begin(int dir, int ena, int pul) { | |||
this->dir = dir; | |||
this->ena = ena; | |||
this->pul = pul; | |||
pinMode(dir, OUTPUT); | |||
pinMode(ena, OUTPUT); | |||
pinMode(pul, OUTPUT); | |||
this->steps = 0; | |||
this->running = false; | |||
this->direction = RIGHT; | |||
this->dutyCycle = 50; | |||
this->frequency = 1; | |||
TCCR4B = TCCR4B & 0b11111000 | 0x01; | |||
} | |||
void DM442::start() { | |||
digitalWrite(this->ena, LOW); | |||
} | |||
void DM442::stop() { | |||
digitalWrite(this->ena, HIGH); | |||
} | |||
void DM442::setDirection(bool direction) { | |||
this->direction = direction; | |||
if (direction) digitalWrite(this->dir, HIGH); | |||
else digitalWrite(this->dir, LOW); | |||
} | |||
void DM442::setFrequency(int stepsPerSecond) { | |||
this->frequency = stepsPerSecond; | |||
} | |||
void DM442::setDutyCycle(int percent) { | |||
this->dutyCycle = percent; | |||
} | |||
void DM442::singleStep() { | |||
//analogWrite(this->pul, 255 * this->dutyCycle / 100); | |||
if (this->frequency == 0) return; | |||
long delayHigh = (float) this->dutyCycle / 100 * 500 / this->frequency; | |||
long delayLow = (float) (100 - this->dutyCycle) / 100 * 500 / this->frequency; | |||
digitalWrite(this->pul, HIGH); | |||
delay(delayHigh); | |||
digitalWrite(this->pul, LOW); | |||
delay(delayLow); | |||
this->steps++; | |||
} | |||
void DM442::driveSteps(int steps) { | |||
int percentPerStep = 40 / steps; | |||
for (int i = 0; i < steps; i++) { | |||
setDutyCycle(10 + i * percentPerStep); | |||
singleStep(); | |||
} | |||
} | |||
long DM442::getSteps() { | |||
return this->steps; | |||
} | |||
void DM442::resetSteps() { | |||
this->steps = 0; | |||
} | |||
</syntaxhighlight> | |||
</div> | |||
'''read_HX711 S-Function:''' | |||
<div style="width:1100px; height:500px; overflow:scroll; border: hidden"> | |||
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger"> | |||
/* Includes_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
#include "HX711.h" | |||
#define OFFSET 66981 //Calibration Value | |||
#define SCALE (float) (145173 - OFFSET) / 200 //Scaling factor of raw Values | |||
HX711 loadCell; | |||
#endif | |||
/* Includes_END */ | |||
/* Externs_BEGIN */ | |||
/* extern double func(double a); */ | |||
/* Externs_END */ | |||
void read_HX711_Start_wrapper(real_T *xD, | |||
const uint8_T *SCK, const int_T p_width0, | |||
const uint8_T *DOUT, const int_T p_width1) | |||
{ | |||
/* Start_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
//Initialisation of LoadCell | |||
loadCell.begin((int) DOUT[0], (int) SCK[0]); | |||
loadCell.set_offset(OFFSET); | |||
loadCell.set_scale(SCALE); | |||
#endif | |||
/* Start_END */ | |||
} | |||
void read_HX711_Outputs_wrapper(real_T *force, | |||
const real_T *xD, | |||
const uint8_T *SCK, const int_T p_width0, | |||
const uint8_T *DOUT, const int_T p_width1) | |||
{ | |||
/* Output_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
//Read force | |||
float f = loadCell.get_units(10); | |||
force[0] = (double) f; | |||
#endif | |||
/* Output_END */ | |||
} | |||
void read_HX711_Update_wrapper(real_T *force, | |||
real_T *xD, | |||
const uint8_T *SCK, const int_T p_width0, | |||
const uint8_T *DOUT, const int_T p_width1) | |||
{ | |||
/* Update_BEGIN */ | |||
/* Update_END */ | |||
} | |||
void read_HX711_Terminate_wrapper(real_T *xD, | |||
const uint8_T *SCK, const int_T p_width0, | |||
const uint8_T *DOUT, const int_T p_width1) | |||
{ | |||
/* Terminate_BEGIN */ | |||
/* | |||
* Custom Terminate code goes here. | |||
*/ | |||
/* Terminate_END */ | |||
} | |||
</syntaxhighlight> | |||
</div> | |||
'''drive_Steps S-Function:''' | |||
<div style="width:1100px; height:500px; overflow:scroll; border: hidden"> | |||
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger"> | |||
/* Includes_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
#include "DM442.h" | |||
DM442 stepper; | |||
#endif | |||
/* Includes_END */ | |||
/* Externs_BEGIN */ | |||
/* extern double func(double a); */ | |||
/* Externs_END */ | |||
void drive_Steps_Start_wrapper(real_T *xD, | |||
const uint8_T *ENA, const int_T p_width0, | |||
const uint8_T *DIR, const int_T p_width1, | |||
const uint8_T *PUL, const int_T p_width2) | |||
{ | |||
/* Start_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
//Initialise and start Steppermotor | |||
stepper.begin(DIR[0], ENA[0], PUL[0]); | |||
stepper.setDirection(RIGHT); | |||
stepper.setFrequency(30); | |||
stepper.start(); | |||
#endif | |||
/* Start_END */ | |||
} | |||
void drive_Steps_Outputs_wrapper(const uint16_T *stepsToDrive, | |||
uint16_T *stepsDriven, | |||
const real_T *xD, | |||
const uint8_T *ENA, const int_T p_width0, | |||
const uint8_T *DIR, const int_T p_width1, | |||
const uint8_T *PUL, const int_T p_width2) | |||
{ | |||
/* Output_BEGIN */ | |||
#ifndef MATLAB_MEX_FILE | |||
//Drive x-Steps in 1 second | |||
stepper.setFrequency(stepsToDrive[0]); | |||
stepper.driveSteps(stepsToDrive[0]); | |||
long steps = stepper.getSteps(); | |||
stepsDriven[0] = steps; | |||
#endif | |||
/* Output_END */ | |||
} | |||
void drive_Steps_Update_wrapper(const uint16_T *stepsToDrive, | |||
uint16_T *stepsDriven, | |||
real_T *xD, | |||
const uint8_T *ENA, const int_T p_width0, | |||
const uint8_T *DIR, const int_T p_width1, | |||
const uint8_T *PUL, const int_T p_width2) | |||
{ | |||
/* Update_BEGIN */ | |||
/* Update_END */ | |||
} | |||
void drive_Steps_Terminate_wrapper(real_T *xD, | |||
const uint8_T *ENA, const int_T p_width0, | |||
const uint8_T *DIR, const int_T p_width1, | |||
const uint8_T *PUL, const int_T p_width2) | |||
{ | |||
/* Terminate_BEGIN */ | |||
/* | |||
* Custom Terminate code goes here. | |||
*/ | |||
/* Terminate_END */ | |||
} | |||
</syntaxhighlight> | |||
</div> | |||
'''Arduino Regler:''' | |||
<div style="width:1100px; height:500px; overflow:scroll; border: hidden"> | |||
<syntaxhighlight lang="cpp" style="border: none; background-color: #EFF1C1; font-size:larger"> | |||
#include "HX711.h" | |||
#include "DM442.h" | |||
//Debug Funktionen | |||
//#define DEBUG | |||
//#define DEBUG_PID | |||
// Definitionen für die Konfiguration | |||
#define OFFSET 66981 | |||
#define SCALE (float) (145173 - OFFSET) / 200 | |||
#define MAX_FILL_LEVEL 500 | |||
#define ANGLE_PER_STEP (float) 0.39 | |||
#define ROUND_OFFSET 0.1f | |||
#define KP 0.014 | |||
#define KI 0.00675 | |||
#define KD 0 | |||
#define PID_MAX 30 | |||
#define PID_MIN 0 | |||
#define START_SWITCH 2 | |||
HX711 loadCell; | |||
DM442 stepper; | |||
int arrayLength = 50; | |||
//Daten für Lookuptable | |||
float forceArray[50] = {-590.41, -574.05, -554.12, -534.25, -514.57, -494.57, -474.99, -455.52, -436.21, -417.06, -389.06, -379.36, -360.83, -342.52, -324.58, -306.87, -289.52, -272.52, -255.77, -239.32, -223.36, -207.89, -192.75, -178.08, -162.85, -150.12, -136.92, -124.11, -111.74, -99.90, -88.64, -77.87, -67.81, -58.80, -49.67, -41.64, -34.24, -27.44, -21.46, -15.85, -11.08, -7.04, -3.68, -0.94, 1.15, 2.47, 3.17}; | |||
double angleArray[50] = {90, 88.05, 86.10, 84.15, 82.20, 80.25, 78.30, 76.35, 74.40, 72.45, 70.50, 68.55, 66.60, 64.65, 62.70, 60.75, 58.80, 56.85, 54.90, 52.95, 51.00, 49.05, 47.10, 45.15, 43.20, 41.25, 39.30, 37.35, 35.40, 33.45, 31.50, 29.55, 27.60, 25.65, 23.70, 21.75, 19.80, 17.85, 15.90, 13.95, 12.00, 10.05, 8.10, 6.15, 4.20, 2.25, 0.30, 0}; | |||
// Initialisierung der Lastzelle | |||
void setupHX711() { | |||
loadCell.begin(8, 9); | |||
loadCell.set_offset(OFFSET); | |||
loadCell.set_scale(SCALE); | |||
} | |||
// Initialisierung des Schrittmotors | |||
void setupDM442() { | |||
stepper.begin(3, 4, 5); | |||
stepper.setDirection(RIGHT); | |||
stepper.setFrequency(30); | |||
stepper.start(); | |||
} | |||
// Aufruf der Setup-Funktionen und Initialisierung des seriellen Ports | |||
void setup() { | |||
setupDM442(); | |||
setupHX711(); | |||
pinMode(START_SWITCH, INPUT_PULLUP); | |||
#if defined(DEBUG) || defined(DEBUG_PID) | |||
Serial.begin(9600); | |||
#endif | |||
} | |||
bool run = true; | |||
void loop() { | |||
static float angle = 90; | |||
//Schalter AUS resette das Programm | |||
if(digitalRead(START_SWITCH) != 0) { | |||
angle = 90; | |||
stepper.resetSteps(); | |||
pid(0, true); | |||
return; | |||
} | |||
if(angle < 0) return; //Für sporadische Messunsicherheiten | |||
#ifdef DEBUG | |||
Serial.print("ANGLE:"); | |||
Serial.print(angle); | |||
Serial.print(","); | |||
#endif | |||
//Messe die anliegende Kraft | |||
float force = loadCell.get_units(10); | |||
#ifdef DEBUG | |||
Serial.print("FORCE:"); | |||
Serial.print(force); | |||
Serial.print(","); | |||
#endif | |||
double cosVal = cos(angle * 3.14 / 180); | |||
//Durch Lookuptable die genullte Kraft bestimmen | |||
double zeroForce = interpolateY(angleArray, forceArray, arrayLength, angle); | |||
#ifdef DEBUG | |||
Serial.print("ZERO:"); | |||
Serial.print(zeroForce); | |||
Serial.print(","); | |||
#endif | |||
//Die Kraft differenz zwischen Nullkraft und anliegender Kraft | |||
double dF = force - zeroForce; | |||
#ifdef DEBUG | |||
Serial.print("dF:"); | |||
Serial.print(dF); | |||
Serial.print(","); | |||
#endif | |||
//Berechne die senkrechte Kraft -> Füllstand | |||
double fillLevel = cosVal * dF; | |||
#if defined(DEBUG) || defined(DEBUG_PID) | |||
Serial.print("Level:"); | |||
Serial.print(fillLevel); | |||
Serial.print(","); | |||
#endif | |||
//Berechne die Differenz zum vollen Glas | |||
double error = MAX_FILL_LEVEL - fillLevel; | |||
#ifdef DEBUG_PID | |||
Serial.print("ERROR:"); | |||
Serial.print(error); | |||
Serial.print(","); | |||
#endif | |||
//Benutze PID-Regler um Schritte für Motor zu ermitteln | |||
int steps = (int) pid(error, false); | |||
#ifdef DEBUG_PID | |||
Serial.print("STEPS:"); | |||
Serial.print(steps); | |||
Serial.print(","); | |||
#endif | |||
//Fahre in einer Sekunde die vorgegebenen Schritte | |||
stepper.setFrequency(steps); | |||
stepper.driveSteps(steps); | |||
//Speichere die insgesamt gefahrenen Schritte abe | |||
unsigned long drivenSteps = stepper.getSteps(); | |||
#ifdef DEBUG_PID | |||
Serial.print("DRIVEN:"); | |||
Serial.print(drivenSteps); | |||
#endif | |||
//Ziehe vom Startwinkel den insgesamt gefahrenen Winkel ab | |||
angle = 90 - drivenSteps * ANGLE_PER_STEP; | |||
Serial.println(); | |||
} | |||
//Umsetzung eines PID-Reglers | |||
int pid(double error, bool reset) { | |||
static unsigned long startMillis = 0; | |||
long iterationTime = millis() - startMillis; | |||
static double errorSum = 0; | |||
static double lastError = 0; | |||
if(reset) { | |||
startMillis = millis(); | |||
errorSum = 0; | |||
lastError = 0; | |||
return 0; | |||
} | |||
errorSum += error; | |||
double d = (error - lastError) / iterationTime; | |||
lastError = error; | |||
double regulated = KP * error + KI * errorSum / iterationTime + KD * d; | |||
#ifdef DEBUG_PID | |||
Serial.print("PID:"); | |||
Serial.print(regulated); | |||
Serial.print(","); | |||
#endif | |||
int out = roundTo(regulated); | |||
if(out > PID_MAX) return PID_MAX; | |||
else if(out < PID_MIN) return PID_MIN; | |||
else return out; | |||
} | |||
//Interpolationsfunktion als Look-up Table | |||
float interpolateY(double X[], float Y[], int n, double X_interpolated) { | |||
for (int i = 0; i < n - 1; i++) { | |||
if (X[i] <= X_interpolated && X_interpolated <= X[i + 1]) { | |||
double X1 = X[i]; | |||
double X2 = X[i + 1]; | |||
float Y1 = Y[i]; | |||
float Y2 = Y[i + 1]; | |||
return (float) ( Y1 + ((Y2 - Y1) / (X2 - X1)) * (X_interpolated - X1)); | |||
} | |||
} | |||
return 0.0; | |||
} | |||
//Rundet nicht bei 0.5 sondern bei 0.1 | |||
int roundTo(float x) { | |||
float diff = x - (int) x; | |||
if(diff > ROUND_OFFSET) return (int) x + 1; | |||
else return (int) x; | |||
} | |||
</syntaxhighlight> | |||
</div> | |||
== Komponententest == | == Komponententest == | ||
Zeile 206: | Zeile 698: | ||
== Zusammenfassung == | == Zusammenfassung == | ||
=== Lessons Learned === | === Lessons Learned === | ||
Wir haben am Anfang viele Überlegungen getroffen. Aufgrund von Berechnungsfehlern oder falschen Datenblättern haben wir viele Rükschläge erleiden müssen. Schlussendlich konnten wir unseren Anfangs geplanten Ablauf nicht in die Tat umsetzen und sind ziehmlich in Zeitstress gekommen. Daraus haben wir gelernt jedes Datenblatt zweimal zu überprüfen und Berechnungen auch in der Realität auszuprobieren und nicht auf theoretische Annahmen zu vertrauen. | |||
Auch die Programmierung mit Simulink war eine Tortur. Aufgrund unseres Zeitdrucks haben wir uns dann dazu entschieden erstmal ein Arduino Programm zu entwickeln, womit wir immerhin schonmal alles testen konnten und weiter kamen. Später haben wir dann die Reglereinstellungen und Ansteuerungen für Sensor und Aktor in S-Function-Blöcke übernommen und so ein lauffähiges Simulink Modell kreiert. Die Schlüsse die wir hier raus gezogen haben, sind erstmal den vertrauten einfachen Weg gehen und später erst den unkonventionellen Weg nehmen. Auch haben wir gelernt nie wieder auf Matlab Simulink etwas für einen Arduino zu programmieren, da dies um ein viel faches schwieriger ist als ein sauber aufgebautes Arduino Projekt. | |||
In der Programmierung gab es noch ein weiteres Problem. Für den HX711 gab es eine lauffähige Bibliothek schon zur Verfügung. Da wir nicht auf den Anfangs geplanten Motor von Funduino zurück greifen konnten mussten wir einen anderen Motor nehmen. Da dieser Motor eine Anfertigung für die Firma Diebold Nixdorf ist gibt es keine öffentlichen Bibliotheken zur Steuerung. Somit musste hier eine eigene Bibliothek zur Steuerung programmiert werden. Diese musste dann auch wieder getestet und Fehler behoben werden. Auch hier wäre mit mehr Zeit es einfacher gewesen einen Motor zu beschaffen der schon eine fertige Bibliothek öffentlich verfügbar hat. | |||
== Projektunterlagen == | == Projektunterlagen == | ||
Zeile 215: | Zeile 712: | ||
=== Projektdurchführung === | === Projektdurchführung === | ||
Dieses Projekt ist Teil des Moduls GET-Fachpraktikum. Dieses Projekt konzentriert sich auf die Regelung und Ansteuerung eines Motors zur Füllstandsregelung eines 0,5 Liter Glases. | |||
Zu Beginn des Projektes ging es vorwiegend um die Konzeptplanung und die damit verbundenen benötigten Bauteile. Als nächstes wurden die benötigten Bauteile beschafft und auf ihre Eignung geprüft. Des Weiteren wurden die einzelnen Komponenten zu einem Gesamtsystem zusammengebaut und die Funktion des Motors getestet. Dabei fiel auf, dass wir einen stärkeren Motor für unser System benötigten. Nach Einbindung des neuen Motors in das System begannen wir damit die Messwerte zu klassifizieren und den Regler auszulegen. Abschließend legten wir diese Dokumentation im HSHL Wiki an. | |||
=== Projektdaten === | |||
ZIP-Archiv: [[Datei:165 Weizen Eingiessanlage.zip|mini]] <br clear="all"> | |||
== YouTube Video == | == YouTube Video == | ||
In dem folgenden Youtube Video [https:// | In dem folgenden Youtube Video [https://youtu.be/AbGATzM_GaI YouTube-Video] werden die Funktionen Funktion der Weizeneingießanlage gezeigt. | ||
{{#ev:youtube|https:// | {{#ev:youtube|https://youtu.be/AbGATzM_GaI| 750 | | Video 1: Funktion der Weizeneingießanlage |frame}} | ||
== Weblinks == | == Weblinks == |
Aktuelle Version vom 18. Januar 2024, 00:34 Uhr
Autor: Philipp Sander, Dennis Fleer
Betreuer: Prof. Dr. Mirek Göbel
Einleitung
Dieser Artikel beschreibt den Aufbau und die Funktion einer Weizenglas-Eingießanlage. Hierbei gibt es eine Halterung für die Flasche und eine für ein Glas. Beide sind jeweils an einer rotierenden Holzplatte montiert. Diese Holzplatte wird mittels Elektromotor rotiert. Über einem Drucksensor wird der Füllstand des Weizenglases ermittelt und eine optimale Einfüllung des Glases erreicht.
Anforderungen
Nr. | Beschreibung | Bereich | Zuständig |
---|---|---|---|
1 | Die Halterungen müssen an der Holzplatte befestigt werden. | Hardware | Dennis Fleer, Philipp Sander |
2 | Der Motor muss die Holzplatte drehen können. | Hardware | Dennis Fleer, Philipp Sander |
3 | Die Elektrik muss eingebaut und verkabelt werden. | Hardware | Dennis Fleer, Philipp Sander |
4 | Die Regelung muss durch den Kraftsensor erfolgen. | Software | Dennis Fleer, Philipp Sander |
5 | Der Motor muss mittels Regler angesteuert werden. | Software | Dennis Fleer, Philipp Sander |
Funktionaler Systementwurf/Technischer Systementwurf
Im folgenden Systementwurf wird das Projekt in Systemkomponenten unterteilt:
- Arduino: In Matlab Simulink programmierte Ansteuerung des Motors und Regelung.
- Motor: Drehbewegung der Holzplattform
- Drucksensor: Kraftmessung für die Füllstandserkennung
- Regler: Füllstandsregelung des Weizenglases
-
Weizen Eingießanlage Schematisch - Skizze
-
Technischer Systementwurf
-
Weizen Eingießanlage Regelkreis
Ansatz zur Herleitung der Übertragungsfunktion:
-
Ansatz zur Herleitung der Übertragungsfunktion
Komponentenspezifikation
Nr. | Bauteil | Beschreibung |
---|---|---|
1 | Arduino MEGA 2560 |
|
2 | DM442 Motortreiber |
|
3 | HX711 Wägezelle |
|
4.1 | Schrittmotor ... |
|
Umsetzung (HW/SW)
Hardware
Projektübersicht:
In unserem Projekt liegen die Anforderung vorrangig darin eine motorbetriebene drehbare Holzplatte zu konstruieren und dieses System mit einer geeigneten Motoransteuerung zu betreiben. In bezug auf Kosten und Gewicht haben wir uns daher für eine Holzkonstruktion entschieden. Die Holzplatte, an welcher die Halterungen für die Bierflasche und Weizenflasche sowie der Sensor befestigt ist, wird von einer Massivholzwelle getragen. Diese Welle wird mittels Lageraufständen auf einer Holzgrundplatte befestigt. Die Welle wird durch eine Riemenübersetzung mit dem Schrittmotor angetrieben.
3D Ansicht:
Baugruppen Model
-
Eingießanlage - SolidWorks - Datei:MaschineBaugrp.sldasm
Motor:
Wir wollten zunächst den Motor des Arduino-Mega-Sets verwenden. Allerdings ist uns nach einer kleinen Testfahrt aufgefallen dass dieser zu wenig Haltedrehmoment aufweist. Daher haben wir auf einen ausgediehnten Motor aus unsere Partnerfirma "Diebold-Nixdorf Inc." gesetzt. Dieser Motor war nun auch in der Lage unsere Anforderungen zu erfüllen. Um diesen Motor zu betreiben wählten wir einen Motortreiber welchen wir ebenfalls in einem ausgediehnten Geldautomaten-System fanden.
Konstruktion:
Die Konstruktion der Halterung ist auf den ersten Blick sehr simpel. Dennoch galt es für uns die Kosten sowie das Gewicht niedrig zu halten. Daher belaufen sich fast alle Komponenten auf Holz und Kunststoff. Für die meisten Komponenten konnten wir auf unser Betriebsinternes Lager zurückgreifen lediglich die Holzplatte und die Halterungen für Flasche und Glas waren noch nicht vorhanden. Die Halterungen für Glas und Flasche wurden durch einen 3D-Kunststoffdruck gefertigt. Zudem mussten wir uns überlegen wie wir die Flasche in der Halterung befestigen. Um auch hier eine günstige, einfache und leichte Lösung zu erreichen, setzen wir auf die Spannkraft zweier Gummis, welche die Flasche im Halter halten.
Wägezelle HX711:
Messprinzip: Durch das zu messende Gewicht wird die Wägezelle leicht gebogen. An der Seite befinden sich 4 Dehnungsmesstreifen, welche als Wheatsstone Brücke angeordnet sind. Der Widerstand der Messstreifen ändert sich mit dem Dehnungsgrad. Somit ändert sich bei einer Last der Widerstand und damit auch der Spannungsabfall der Brückenschaltung. Der Spannungsabfall zwischen den Punkten A und B wird mit einem A/D Wandler ausgewertet.
-
- Messchaltungsaufbau
Da die Wägezelle über die physikalische Kraft der Gewichtskraft funktioniert, war es eine Herausforderung in unserem drehenden System die korrekten Werte für den Füllstand zu erhalten. Dies war deshalb so schwierig weil sich die Wägezelle immer in einem anderen Winkel zur Gewichtskraft des Füllstandes befindet. Aus diesem Grund mussten wir die Messwerte noch einmal mit dem cos des Winkels multiplizierten um die korrekte Gewichtskraft zu erhalten. Wir verwenden außerdem einen Look-up-Table um uns die Verarbeitung der Messdaten zu vereinfachen. Konkret subtrahieren wir unseren gemessenen wert mit dem Wert aus dem Look-up-Table an der gleichen Winkelstellung. Somit Erhalten wir die korrekte Gewichtskraft des Pegelstandes des Glases. Dies ist dann dementsprechend unser IST-Wert.
PI-Regler:
Der IST-Wert wird nun mit dem SOLL-Wert von 500 (500ml/Kg) subtrahiert. Somit erhalten wir unsere Regelabweichung welche wir weiterhin mit dem PI-Regler verarbeiten. Aufgrund dessen, dass 1-Motorschritt in 0,39 Grad am äußeren Umfang der Holzplatte entspricht, benötigen wir eine sehr schnelle Regulierung der Motorschritte, sobald Flüssgkeit in das Glas läuft. Daher benötigen wir eine möglichst schnelle Sprungantwort und somit einen großen P-Anteil in unserem Regler. Für unser System haben sich die Werte Kp=0,014 und Ki=0,00675 als praktikabel erwiesen. Die Stellgröße des Reglers wird direkt als Ansteuerung für den Motor genutzt.
Motoransteuerung:
Die Motoransteuerung erfolgt über den Motortreiber DM441 dieser braucht eine externe Versorgungsspannung von 24V. Außerdem werden zur Steuerung drei Pins angeschlossen.
- ENA: Sorgt für das Starten des Motors und zusätzlich auch für einen Haltestrom.
- DIR: Gibt die Richtung, in die der Motor fahren soll.
- PUL: Über diesen Pin wird ein PWM-Signal gesendet, welches das Drehmoment und die Geschwindigkeit bestimmt.
Verdrahtungsplan:
-
Verdrahtungsplan Eingießanlage - Datei:Verdrahtungsplan Hopfomat.zip
Software
Programmablaufplan:
-
Programmablaufplan - Datei:HopfomatPAP.zip
Simulink Regler Model:
DM442.h:
DM442.cpp:
read_HX711 S-Function:
drive_Steps S-Function:
Arduino Regler:
Komponententest
Nr. | Beschreibung | Testmethode | Zuständigkeit | Testergebnis |
---|---|---|---|---|
1 | Die Halterungen müssen an der Holzplatte befestigt werden. | Die Halterungen wurden korrekt befestigt und halten dem Gewicht und der Rotation stand. | Fleer, Sander | Bestanden |
2 | Der Motor muss die Holzplatte drehen können. | Der Motor kann sowohl beide Gewichte als auch nur die Flasche ohne Probleme heben und halten. | Fleer, Sander | Bestanden |
3 | Die Elektrik muss eingebaut und verkabelt werden. | Die Verdrahtung der Bauteile wurde korrekt durchgeführt und nochmals mittels Durchgangsprüfung geprüft. | Fleer, Sander | Bestanden |
4 | Die Regelung muss durch den Kraftsensor erfolgen. | Die Wägezelle misst jede Sekunde die Gewichtskraft des Inhalts. Dieser Ist-Wert wird mit dem Soll-Wert subtrahiert und geht als Regelfehler in unseren Regler. | Fleer, Sander | Bestanden |
5 | Der Motor muss mittels Regler angesteuert werden | Die Ansteuerung erfolgt softwareseitig durch die Stellgröße unseres Reglers. | Fleer, Sander | Bestanden |
Ergebnis
Das Projekt zur Füllstandsregelung eines Bierglases an der HSHL im Fachgebiet der Mechatronik führte zu einem positiven Ergebnis. Die Anlage ist in der Lage durch umlegen eines Schalters ein 0,5Liter Bierglas einzuschenken. Zu Beginn des Einfüllvorgangs werden die maximalen Motorschritte gefahren. Sobald der Füllstand erkannt wird, werden, je nach Füllstand, weniger Schritte gefahren.
-
Frontansicht der Weizeneingießanlage
-
Rückansicht der Weizeneingießanlage
Zusammenfassung
Lessons Learned
Wir haben am Anfang viele Überlegungen getroffen. Aufgrund von Berechnungsfehlern oder falschen Datenblättern haben wir viele Rükschläge erleiden müssen. Schlussendlich konnten wir unseren Anfangs geplanten Ablauf nicht in die Tat umsetzen und sind ziehmlich in Zeitstress gekommen. Daraus haben wir gelernt jedes Datenblatt zweimal zu überprüfen und Berechnungen auch in der Realität auszuprobieren und nicht auf theoretische Annahmen zu vertrauen.
Auch die Programmierung mit Simulink war eine Tortur. Aufgrund unseres Zeitdrucks haben wir uns dann dazu entschieden erstmal ein Arduino Programm zu entwickeln, womit wir immerhin schonmal alles testen konnten und weiter kamen. Später haben wir dann die Reglereinstellungen und Ansteuerungen für Sensor und Aktor in S-Function-Blöcke übernommen und so ein lauffähiges Simulink Modell kreiert. Die Schlüsse die wir hier raus gezogen haben, sind erstmal den vertrauten einfachen Weg gehen und später erst den unkonventionellen Weg nehmen. Auch haben wir gelernt nie wieder auf Matlab Simulink etwas für einen Arduino zu programmieren, da dies um ein viel faches schwieriger ist als ein sauber aufgebautes Arduino Projekt.
In der Programmierung gab es noch ein weiteres Problem. Für den HX711 gab es eine lauffähige Bibliothek schon zur Verfügung. Da wir nicht auf den Anfangs geplanten Motor von Funduino zurück greifen konnten mussten wir einen anderen Motor nehmen. Da dieser Motor eine Anfertigung für die Firma Diebold Nixdorf ist gibt es keine öffentlichen Bibliotheken zur Steuerung. Somit musste hier eine eigene Bibliothek zur Steuerung programmiert werden. Diese musste dann auch wieder getestet und Fehler behoben werden. Auch hier wäre mit mehr Zeit es einfacher gewesen einen Motor zu beschaffen der schon eine fertige Bibliothek öffentlich verfügbar hat.
Projektunterlagen
Projektplan
-
Projektplan
Projektdurchführung
Dieses Projekt ist Teil des Moduls GET-Fachpraktikum. Dieses Projekt konzentriert sich auf die Regelung und Ansteuerung eines Motors zur Füllstandsregelung eines 0,5 Liter Glases. Zu Beginn des Projektes ging es vorwiegend um die Konzeptplanung und die damit verbundenen benötigten Bauteile. Als nächstes wurden die benötigten Bauteile beschafft und auf ihre Eignung geprüft. Des Weiteren wurden die einzelnen Komponenten zu einem Gesamtsystem zusammengebaut und die Funktion des Motors getestet. Dabei fiel auf, dass wir einen stärkeren Motor für unser System benötigten. Nach Einbindung des neuen Motors in das System begannen wir damit die Messwerte zu klassifizieren und den Regler auszulegen. Abschließend legten wir diese Dokumentation im HSHL Wiki an.
Projektdaten
ZIP-Archiv: Datei:165 Weizen Eingiessanlage.zip
YouTube Video
In dem folgenden Youtube Video YouTube-Video werden die Funktionen Funktion der Weizeneingießanlage gezeigt.
Weblinks
Literatur
→ zurück zur Übersicht: WS 22/23: Angewandte Elektrotechnik (BSE)