M241 Permutation de trois (3) Pompes

Forum traitant des automates industriels de marque Schneider - Telemecanique
Répondre
jazon
Apprend le binaire
Apprend le binaire
Messages : 5
Enregistré le : 05 mai 2023, 01:48

M241 Permutation de trois (3) Pompes

Message par jazon »

BONJOUR A TOUS,

Quelqu'un pourrait me venir en aide svp :?:

Voila j'ai un automate Schneider M241 ou je doit concevoir un programme de permutation sur temps de marche minimal avec trois (03) pompes
et 03 poires de niveau pour le démarrage des pompes.
Si le niveau 1 est atteint la pompe qui est disponible et ayant le temps de marche le plus faible démarre.
Si le niveau 2 est atteint la 2eme pompe qui a le temps de marche la plus faible des 02 pompes restant démarre.
Et le 3em niveau démarre la 3eme pompe .

En quelque sorte ca consiste a joué sur l'ordre de priorité des pompes et leurs disponibilités une fois que une pompe est indisponible, automatiquement la pompe restant prends le relais.

MERCI!
roudy
Code son premier grafcet
Code son premier grafcet
Messages : 45
Enregistré le : 19 janv. 2019, 17:28

Re: M241 Permutation de trois (3) Pompes

Message par roudy »

Bonjour,

C'est un problème de logique pas si simple :
J'ai tendance à partir du principe qu'une pompe en fonctionnement n'est pas arrêté par le temps de fonctionnement, c'est au démarrage suivant qu'on effectue la rotation.
Calculer le temps de fonctionnement de chaque pompe, intégrer un temps avant rotation (calculé sur la première qui est démarré)
En déduire l'ordre de démarrage suivant voulu, au front d'arrêt de toutes les pompes.
Si c'est pas clair dans ta tête, ce sera bien pire dans ton programme.
Jambe
Créateur de langage
Créateur de langage
Messages : 706
Enregistré le : 28 mai 2020, 18:38

Re: M241 Permutation de trois (3) Pompes

Message par Jambe »

La solution consiste a ranger les heures de marches de chaque pompe dans un tableau. Trier le tableau par nombre d'heure croissant, démarrer au premier niveau avec l'index 1 du tableau, puis le deux etc...
Avatar du membre
itasoft
Mi homme - Mi automate
Mi homme - Mi automate
Messages : 7141
Enregistré le : 20 oct. 2015, 10:15
Localisation : Lyon
Contact :

Re: M241 Permutation de trois (3) Pompes

Message par itasoft »

@jambe
C’est bien ce qu’il faut faire, mais après le Tri « Général Boulanger » (croissant) tu ne sais plus quel est le n° de la pompe qui se retrouve en première position, En fait il faut un tableau à deux dimensions, une pour les temps l’autre pour les n° des pompes
exemple;
viewtopic.php?p=47396&hilit=boulanger#p47396
Automaticien privé (de tout)
itasoft@free.fr
Jambe
Créateur de langage
Créateur de langage
Messages : 706
Enregistré le : 28 mai 2020, 18:38

Re: M241 Permutation de trois (3) Pompes

Message par Jambe »

Oui @Itasoft, c'était bien mon idée mais fallait bien laisser chercher un peu Jazon
Avatar du membre
itasoft
Mi homme - Mi automate
Mi homme - Mi automate
Messages : 7141
Enregistré le : 20 oct. 2015, 10:15
Localisation : Lyon
Contact :

Re: M241 Permutation de trois (3) Pompes

Message par itasoft »

ok, ceci dit si il n'as que 3 pompes seulement, il peut faire faire un Tri bestial mot à mot, LOL
Automaticien privé (de tout)
itasoft@free.fr
Avatar du membre
JC87
Mi homme - Mi automate
Mi homme - Mi automate
Messages : 1945
Enregistré le : 20 oct. 2015, 13:00
Localisation : Nouvelle Aquitaine

Re: M241 Permutation de trois (3) Pompes

Message par JC87 »

Bonjour,

J'ai eu fait un truc de ce genre en Siemens avec 5 compresseurs qui alimentaient des turbines à gaz. Comme ces installations tournent H24 il y a toujours à minima un compresseur en marche.

Le principe qu'on avait retenu c'est de définir un besoin en nombre de compresseurs de 1 à 5 en fonction de la pression. Ça revient au même avec des pompes et des niveaux TOR. On a donc un besoin sous forme d'un entier qui va de 1 à 5.

Le temps de marche de chaque compresseur doit être calculé en permanence ensuite chaque fin de semaine on effectuait un tri pour définir un numéro d'ordre pour chacun. Celui qui a le moins d'heures de fonctionnement à un numéro d'ordre égal à 1 et ainsi de suite jusqu’à à 5.

Pour démarrer les compresseurs/pompe on compare le numéro d'ordre au besoin et on pilote tous ceux qui sont inférieurs ou égaux au besoin. Par exemple si le besoin est de trois compresseurs on démarre ceux qui ont pour numéros d'ordre 1, 2 et 3.

Un compresseur en défaut ou déclaré indisponible pour maintenance par exemple se voit attribuer le numéro d'ordre 99 ou n’importe quel chiffre supérieur au besoin maximum. Ainsi il ne peut pas démarrer.

Bon dans le principe c'est simple, mais à mettre en œuvre ça l'est un peu moins, surtout en langage SCL Step 7.

JC
"On veut faire du zéro défaut mais on a zéro bonhomme et zéro budget, et bien à la fin on a zéro résultat..."
Pierro
Code son premier grafcet
Code son premier grafcet
Messages : 47
Enregistré le : 17 mars 2020, 16:58
Localisation : 127.0.0.1

Re: M241 Permutation de trois (3) Pompes

Message par Pierro »

Bonjour,

Si ca peux en aider plus d'un, j'ai un FB que j'ai écrit en ST sur un M580 qui fait ce principe:

Code : Tout sélectionner

(* Fin = nombre maxi de pompe ou chaudiere *)
DEBUT := 1;
FIN := 4;

(* front montant d'une seconde *)
M1S := %S6;
FM_1Hz := RE(M1S);

(* TRANSFERT DES STRUCTURE POMPES DANS TABLEAU POUR CALCUL *)

POMPE[1] := PPE1;
POMPE[2] := PPE2;
POMPE[3] := PPE3;
POMPE[4] := PPE4;


(* recopie des entrées dans les tableaux *)
CP_PPE[1] := PPE1.ETAT.DISPO;
CP_PPE[2] := PPE2.ETAT.DISPO;
CP_PPE[3] := PPE3.ETAT.DISPO;
CP_PPE[4] := PPE4.ETAT.DISPO;



(*Nombre de pompe disponible*)
NB_PPE_DISPO := 0;

FOR i := DEBUT TO Fin DO
    IF CP_PPE[i] THEN
        NB_PPE_DISPO := NB_PPE_DISPO + 1;
    END_IF;
END_FOR;


(* Nombre d'equipement en fonctionnement *)
NB_PPE_FONCT := 0;
FOR i := DEBUT TO Fin DO
    IF  POMPE[i].ETAT.MARCHE_AV THEN
        NB_PPE_FONCT := NB_PPE_FONCT + 1;
    END_IF;
END_FOR;




(* Calcul de la priorité de l'equipement a l'arret depuis le plus longtemps *)

(* RECHECHE DU MAXIMUMU DE TEMPS DE NON FONCTIONNEMENT *)
MAXINT := MAX ( IN1 := PPE1.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN2 := PPE2.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN3 := PPE3.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN4 := PPE4.TPS_FONCT_MAINT.TPS_FONCT_H);

MININT := MIN  (IN1 := PPE1.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN2 := PPE2.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN3 := PPE3.TPS_FONCT_MAINT.TPS_FONCT_H,
		IN4 := PPE4.TPS_FONCT_MAINT.TPS_FONCT_H);




(* BOUCLE DE CALCUL DE LA POMPE PRIORITAIRE, CELLE QUI A LE MOINS DE TEMPS DE NON FONCTIONNEMENT *)
memint := 0;
memprio := 0;
DEBUTINDEX := 1 ;
MEMNBPPEFONCT := NB_PPE_FONCT ;
MEMONBPPEDISPO := NB_PPE_DISPO;

R_TRIG_MAINT (CLK := not PPE1.ETAT.DISPO or not PPE2.ETAT.DISPO or not PPE3.ETAT.DISPO or not PPE4.ETAT.DISPO );

(*  *)
 IF R_TRIG_MAINT.Q THEN
	MEMNBPPEFONCT := 0;
END_IF; 
	

(* CALCUL DES POMPES A L'ARRET POUR INTEGRATION *)
FOR i := MININT TO MAXINT BY 1 DO 

	FOR j := DEBUT to FIN DO

		IF POMPE[j].TPS_FONCT_MAINT.TPS_FONCT_H  =  i 
		 AND POMPE[j].ETAT.DISPO
		 AND (NOT POMPE[j].ETAT.MARCHE_AV OR R_TRIG_MAINT.Q)
		 AND (PERMUTATION_PPES.PRIORITE_PPES[j] > NB_PPE_FONCT or PERMUTATION_PPES.PRIORITE_PPES[j] = 0 OR R_TRIG_MAINT.Q)
		   THEN

			MEMNBPPEFONCT := MEMNBPPEFONCT +1;
			PERMUTATION_PPES.PRIORITE_PPES[j] := MEMNBPPEFONCT;

		ELSIF NOT POMPE[j].ETAT.DISPO
			AND POMPE[j].TPS_FONCT_MAINT.TPS_FONCT_H  =  i  THEN
			
			MEMONBPPEDISPO := MEMONBPPEDISPO + 1;
			PERMUTATION_PPES.PRIORITE_PPES[j] := MEMONBPPEDISPO;
			
		
		END_IF;
	END_FOR;
END_FOR;


MEMOREINDEX := 0;
(* RECALCUL DES POMPES EN MARCHE  *)
FOR i := MININT TO MAXINT BY 1 DO 

	FOR j := DEBUT to FIN DO

		IF POMPE[j].ETAT.MARCHE_AV
		AND POMPE[j].TPS_FONCT_MAINT.TPS_FONCT_H  =  i 
		or  PERMUTATION_PPES.PRIORITE_PPES[j] = 0
		THEN

			MEMOREINDEX := MEMOREINDEX +1;
			PERMUTATION_PPES.PRIORITE_PPES[j] := MEMOREINDEX;
		END_IF;
	END_FOR;
END_FOR;
Avatar du membre
pfe
Code sa première boucle
Code sa première boucle
Messages : 13
Enregistré le : 14 nov. 2015, 10:31

Re: M241 Permutation de trois (3) Pompes

Message par pfe »

Et pourquoi pas quelque chose de lisible par la maintenance ? :)
C'est en fait plutot simple et facile à débugger, en prenant le courage d'écrire plus de lignes de code (ce qui ne charge pas forcément la cpu...) :
De mémoire, j'avais eu peu de mal pour faire une rotation du même genre.

Ca ressemblerait à ceci en gérant les priorité au démarrage et aussi à l'arrêt :

Code : Tout sélectionner

(* pompe_struct: a definir ou adapter (def. ici seulement pour que l'exemple soit lisible) *)
pmp1 : pompe_struct;
pmp2 : pompe_struct;
pmp3 : pompe_struct;

(* Demandes de pompes : poires dans l'ordre des niveaux de haut en bas -
    (ici, une poire 'demande' du remplissage, la "1" est en haut) : inverser si besoin ^^ 
*)
    dmd0 := not poire1 and not poire2 and not poire3; (* demande 0 pompe  *)
    dmd1 :=     poire1 and not poire2 and not poire3; (* demande 1 pompe  *)
    dmd2 :=     poire1 and     poire2 and not poire3; (* demande 2 pompes *)
    dmd3 :=     poire1 and     poire2 and     poire3; (* demande 3 pompes *)
    id dmd0 then pmp_demandes := 0; end_if;
    id dmd1 then pmp_demandes := 1; end_if;
    id dmd2 then pmp_demandes := 2; end_if;
    id dmd3 then pmp_demandes := 3; end_if;

(* Pompes disponibles au demarrage = à l'arret sans defaut, dispo pour automatique.... *)
(* pmpxxx.en_marche : direct pour 'dispo pour arreter' bien sur *)
    pmp1.dispo := not pmp1.deft and <etc... (cond permanentes)> and not pmp1.en_marche;
    pmp2.dispo := not pmp2.deft and <etc... (cond permanentes)> and not pmp2.en_marche;
    pmp3.dispo := not pmp3.deft and <etc... (cond permanentes)> and not pmp3.en_marche;

(* Temps de marche pour equilibre *)
    if %s6 and not mem_s6 then (*oui, je n'aime pas du tout 'RE(...)' ^^ *)
        (* compte tp marche pour gestion de priorité                                             
           nota: compter annees,jours,heure,min,sec separement et seulement si besoin d'affichage   
           
           permet de lier a un 'reset' temps de marche séparé si une pompe est remplacee par une neuve
           sinon, la pompe neuve sera beaucoup plus utilisee les premiers temps                       
           avec une rotation basee seulement sur le temps de marche
        *)
        
        if  pmp1.en_marche then pmp1.tp_marche := pmp1.tp_marche +1; end_if;
        if  pmp2.en_marche then pmp2.tp_marche := pmp2.tp_marche +1; end_if;
        if  pmp3.en_marche then pmp3.tp_marche := pmp3.tp_marche +1; end_if;
        
        (* et ... on peut compter sur un simple entier (ici sur 500 heures, env 21jours) sans souci *)
        if (pmp1.tp_marche > 30_000) and (pmp2.tp_marche > 30_000) and (pmp3.tp_marche > 30_000) then
            pmp1.tp_marche := pmp1.tp_marche - 30_000;
            pmp2.tp_marche := pmp2.tp_marche - 30_000;
            pmp3.tp_marche := pmp3.tp_marche - 30_000;
        end_if;               
    end _if;
    mem_s6 := %s6;

(* Priorites *)
    (* prio au demarrage : la pompe dispo la moins utilisée des pompes dispos *)
    (*                     une seule pompe a demarrer par cycle! *)
    pmp1.prio_Dem := pmp1.dispo and ((pmp1.tp_marche < pmp2.tp_marche) or not pmp2.dispo) and ((pmp1.tp_marche < pmp3.tp_marche) or not pmp3.dispo);
    pmp2.prio_Dem := not pmp1.prio_Dem 
                 and pmp2.dispo and ((pmp2.tp_marche < pmp1.tp_marche) or not pmp1.dispo) and ((pmp2.tp_marche < pmp3.tp_marche) or not pmp3.dispo);
    pmp3.prio_Dem := not pmp1.prio_Dem and not pmp2.prio_Dem 
                 and pmp3.dispo and ((pmp3.tp_marche < pmp1.tp_marche) or not pmp1.dispo) and ((pmp3.tp_marche < pmp2.tp_marche) or not pmp2.dispo);

    (* resoud le cas rare ou les temps sont equilibres : *)
    (* -- a shunter pendant le test -- *)
    if not pmp1.prio_Dem and not pmp2.prio_Dem and not pmp3.prio_Dem then 
        pmp1.prio_Dem := pmp1.dispo;
        pmp2.prio_Dem := pmp2.dispo and not pmp1.prio_Dem;
        pmp3.prio_Dem := pmp3.dispo and not pmp1.prio_Dem and not pmp2.prio_Dem;
    end_if;

    (* prio a l'arret: la pompe en marche la plus utilisée des pompes en marche *)
    (*                     une seule pompe a arreter par cycle! *)
    pmp1.prio_Art := pmp1.en_marche and ((pmp1.tp_marche > pmp2.tp_marche) or not pmp2.en_marche) and ((pmp1.tp_marche > pmp3.tp_marche) or not pmp3.en_marche);
    pmp2.prio_Art := not pmp1.prio_Art and pmp2.en_marche and ((pmp2.tp_marche > pmp1.tp_marche) or not pmp1.en_marche) and ((pmp2.tp_marche > pmp3.tp_marche) or not pmp3.en_marche);
    pmp3.prio_Art := not pmp1.prio_Art and not pmp2.prio_Art and pmp3.en_marche and ((pmp3.tp_marche > pmp1.tp_marche) or not pmp1.en_marche) and ((pmp3.tp_marche > pmp2.tp_marche) or not pmp2.en_marche);

    (* resoud le cas rare ou les temps sont equilibres : *)
    (* -- a shunter pendant le test -- *)
    if not pmp1.prio_Art and not pmp2.prio_Art and not pmp3.prio_Art then 
        pmp1.prio_Art := pmp1.en_marche;
        pmp2.prio_Art := pmp2.en_marche and not pmp1.prio_Art;
        pmp3.prio_Art := pmp3.en_marche and not pmp1.prio_Art and not pmp2.prio_Art;
    end_if;

(* Demandes M/A : il pourrait être bon de decaler les demandes en cascade  *)
(*                ici on ne demarre ou n'arrete qu'une pompe par cycle API *)

(* edit : ajout d'une cascade TON: *) 
    Cascade (not dmd_start and not dmd_stop , T#2s);

    (* Compte le nombre de pompes en marche *)
    pmp_running := bool_to_int(pmp1.en_marche) + bool_to_int(pmp2.en_marche) + bool_to_int(pmp2.en_marche);

    (* Dmd Start nouvelle pompe *)
    dmd_start := Cascade.Q and (pmp_demandes > pump_running);
    start_p1 := dmd_start and pmp1.prio_Dem;
    start_p2 := dmd_start and pmp2.prio_Dem;
    start_p3 := dmd_start and pmp3.prio_Dem;

    (* Dmd Arret d'une pompe *)
    dmd_stop := Cascade.Q and (pmp_demandes < pump_running);
    stop_p1 := dmd_stop and pmp1.prio_Art;
    stop_p2 := dmd_stop and pmp2.prio_Art;
    stop_p3 := dmd_stop and pmp3.prio_Art;
    
(* Outs "a l'ancienne..." *)
    P1 := (start_p1 or P1) and not stop_p1 and not pmp1.deft and <etc... (cond permanentes)>);
    P2 := (start_p2 or P2) and not stop_p2 and not pmp2.deft and <etc... (cond permanentes)>);
    P3 := (start_p3 or P3) and not stop_p3 and not pmp3.deft and <etc... (cond permanentes)>);

(* -- and voilà *)
Répondre