Here weâre really getting into the technical side, and my English might make me misunderstand or express myself poorly.
I think that if I want to be at least a bit clear, I need to show you how my scanner is designed:
Here is the wiring of the box at the back, which provides power and acts as a âhubâ for the motors, lighting, ventilation, and the laser:
Schematic_Schéma de branchement pour boitier arriÚre_2023-07-30.pdf (55.3 KB)
Then there is the control box, which manages and coordinates all of these components:
Schematic_Schéma pour boitier de commande avant_2023-07-30.pdf (78.2 KB)
And finally, there is the program that runs on the Arduino, which brings us back to your question:
Basically, each perforation sends a pulse to the Teensy, each pulse triggers this method, and the regulation is carried out with this code, each time a new pulse is received:
// === CAPTEUR LASER ===
void checkPerf() {
digitalWrite(CamShoot, HIGH);
timerDelayCam.begin(temporisateurCam, 200);
// Mise à jour de la durée du flash selon le potentiomÚtre
flashDuration = map(analogRead(potFlashDuration), 0, 1023, 50, 1500);
// Calcul du temps écoulé entre deux images
endTime = millis();
frameInterval = endTime - startTime;
startTime = endTime;
// Calcul du FPS actuel et envoi au filtre de lissage + affichage
float currentFPS = (frameInterval > 0) ? (1000.0 / frameInterval) : 0;
computeSmoothedFPS(currentFPS,false);
// RĂ©gulation de la vitesse moteur en fonction de lâerreur sur lâintervalle cible
float targetInterval = (imagesPerSecond > 0) ? (1000.0 / imagesPerSecond) : 0;
float error = frameInterval - targetInterval;
float correction = computeCorrection(error);
currentMotorSpeed += correction;
currentMotorSpeed = constrain(currentMotorSpeed, 50, 10000);
if (targetInterval == 0) currentMotorSpeed = 0;
// Application de la vitesse aux moteurs selon lâĂ©tat courant
if (currentState == REWIND) {
stepperG.spin(currentMotorSpeed * getInvert());
} else if (currentState == FORWARD) {
stepperD.spin(currentMotorSpeed * getInvert());
}
// Gestion du flash et de la caméra
digitalWrite(Light, HIGH);
}
void temporisateurCam() {
digitalWrite(CamShoot, LOW);
timerflashDuration.begin(timerflash, flashDuration);
timerDelayCam.end();
}
void timerflash() {
digitalWrite(Light, LOW);
timerflashDuration.end();
}
float computeCorrection(float error) {
float Kp = 0.40;
float Ki = 0.02;
if (abs(error) > 0.5) integral += error;
integral = constrain(integral, -50, 50);
float correction = Kp * error + Ki * integral;
return constrain(correction, -120, 120);
}
float computeSmoothedFPS(float newFPS, bool reset){
static const int bufferSize = 10;
static float buffer[bufferSize];
static int index = 0;
static bool filled = false;
// Si reset demandé, vider le buffer et repartir de zéro
if (reset) {
for (int i = 0; i < bufferSize; i++) buffer[i] = 0;
index = 0;
filled = false;
return 0;
}
// Ajouter la valeur seulement si elle est dans une plage raisonnable
if (newFPS > 0 && newFPS < 60) {
buffer[index++] = newFPS;
if (index >= bufferSize) {
index = 0;
filled = true;
}
} else {
// logMessage("[DEBUG] Valeur FPS rejetée : " + String(newFPS)); // Optionnel pour debug
}
// Si le buffer nâest pas encore rempli, ne rien afficher
if (!filled) {
return 0;
}
// Calcul de la moyenne
float sum = 0;
for (int i = 0; i < bufferSize; i++) {
sum += buffer[i];
}
float average = sum / bufferSize;
// Affichage toutes les 500 ms
static unsigned long lastPrint = 0;
if (millis() - lastPrint >= 500) {
logMessage(â[FROM_SCAN_INFO] fsp actuel : " + String(average, 1) + " img/sâ);
lastPrint = millis();
}
return average;
}
Tell me if this answers your question.