Aller au contenu


Photo
* * * * * 1 note(s)

Guide linéaire motorisé


  • Veuillez vous connecter pour répondre
156 réponses à ce sujet

#21 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 16 avril 2020 - 04:35

Presque tout bon ... 

Mais

a) tu as toujours des variables globales alors que tu n'en as pas besoin.... => il faut prendre l'habitude dès le départ d'en avoir quasiment pas =) surtout que " contact buté est affiché mais même pas modifié dans ton code ^^
b ) ensuite sur le principe il y a un petit truc qui ne va pas par rapport à ce que j'attendais à savoir : 


Elle teste  le bouton et si le bouton est appuyé alors 

  afficher Initialisation en cours " et attendre en continu que la butée soit appuyée
  Quand la butée est  enfin appuyée => Afficher "Initialisation terminée " . 

Fin si
Fin de la fonction 

 

"

 

du coup je te propose ce code à tester pour voir la différence =)

 

void  initMoteur () {

  bool BoutonInit = digitalRead(PinBoutonInit);

  if (BoutonInit == 0)  {
    Serial.println (" Initialisation en cours "  );
    while (digitalRead(PinContactButé) == 1) {
      // on ne fait rien en attendant ... 
    }
    Serial.println ("Initialisation terminée " );
  }

}

Pour ce qui est de ton problème avec la pull up tu peux te faire des fonctions du genre 


 

bool appuisBoutonInit() {
  return !digitalRead(PinBoutonInit);
}

Ainsi tu pourras faire 

if( appuisBoutonInit() )   

pour un if quand le bouton est appuyé

ou 

if ( !appuisBoutonInit() )

 

pour un if quand le bouton n'est pas appuyé ...


Je te laisse proposer une dernière version de code  avec l'utilisation des fonctions comme " solution " pour augmenter la lisibilité du code ? =) Et ensuite on passe soit à l'affichage sur l'écran LCD, soit au moteur, choisit en un des deux =)


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 


#22 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 16 avril 2020 - 08:56

J'ai simplifier le code au maximum . C'est vrai que c'est plus clair à lire . 

Maintenant au niveau du fonctionnement , il y a pas mieux que l'exemple .  Je suis pas arrivé avec un autre codage à arrêter autrement le moniteur série  . Il faut vraiment suivre l'état des variables tout au long du code . C'est vraiment le loop qui fait la répétions si le code ne l' arrête pas !! 

const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm

void  initMoteur () {

  if (digitalRead(PinBoutonInit) == 0) {
    Serial.println (" Initialisation en cours "  );
    while (digitalRead(PinContactButé) == 1) {}  // on bloque ici tant que la butée est relachée
    Serial.println ("Initialisation terminée " );
  }
}

void setup() {

  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
}

void loop() {


  initMoteur ();

}


#23 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 16 avril 2020 - 09:28

Du coup si je comprends bien  pas de fonctions " appuisBoutonInit() " pour simplifier le coup de l'inversion des niveau 1 et 0 ?  

Tu veux passer à la suite ? Ecran ou moteur ? 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 


#24 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 17 avril 2020 - 09:56

 Hello , J'ai essayer avec if ( !appuisBoutonInit() ) . Puis je me suis dit que c'est plus concret pour moi de modifier directement au câblage électrique . Je suis entre les deux . Je pense que je vais le savoir avec plus d'utilisation .

Je viens de tester l'écran  avec hello word qui s'affiche . Tout va bien . Sur les grandes breadboard  le + et - sont pas relié sur la longueur entière . C'est bête , j'ai pas trouvé de suite pourquoi ça marchais pas  !

Du coup en avant pour l'écran .



#25 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 17 avril 2020 - 11:18




#include <LiquidCrystal.h>
const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm
int distance = 0 ;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void  initMoteur () {


    if (digitalRead(PinBoutonInit) == 0) {
    Serial.println (" Initialisation en cours "  );


    while (digitalRead(PinContactButé) == 1) {}
    Serial.println ("Initialisation terminée " );
    distance = 30 ;
  }
}

void setup() {
  lcd.begin(16, 2);

  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
}

void loop() {


  initMoteur ();
   lcd.setCursor(0, 1);
   lcd.print(distance);
}



#26 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 17 avril 2020 - 11:30

J'ai mis un float à la place du int pour la distance . Il me manquait les millimètres . 
#include <LiquidCrystal.h>
const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm
float distance = 0 ;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void  initMoteur () {


    if (digitalRead(PinBoutonInit) == 0) {
    Serial.println (" Initialisation en cours "  );


    while (digitalRead(PinContactButé) == 1) {}
    Serial.println ("Initialisation terminée " );
    distance = 30.00 ;
  }
}

void setup() {
  lcd.begin(16, 2);

  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
}

void loop() {


  initMoteur ();
   lcd.setCursor(0, 0);
   lcd.print(distance);
}



#27 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 17 avril 2020 - 12:37

Bon hé bien si le test de l'écran ça marche on peut passer au test du moteur alors =). 

Limite pour ce test là on peut le faire en deux temps 

1) on fait un nouveau programme tout simple pour tester le moteur qui ne fait que tourner le moteur, et on s'en sert pour éventuellement développer les fonction qu'on va vouloir utiliser;  ( voir notamment les changements de sens etc ... ) 

2) une fois qu'on sait exactement comment marche le code pour le moteur on ajoute le code moteur  ( des fonctions qu'on a crée ) dans notre code précédent  =) 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 


#28 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 17 avril 2020 - 01:35

Je cherchais la solution pour marquer à l'écran " appuyé sur init pour initialisé le chariot " en menu déroulant . Quelque chose comme ça à mettre dans le setup  . Je vous poserez la question plus tard . 

J'aime bien l'idée de tester le moteur . Le câblage du pilote est sur le site . J'essaye de suite ....



#29 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 17 avril 2020 - 02:15

Je voudrai régler mon pilote TB 6600 avec mon moteur NEMA 17 , mais au niveau de l'ampérage rien correspond à 100 pour cent . 

Mon moteur consommerai 1.68 ampères ,  c'est bien mais pas assez complet pour faire un réglage .

D’après mon pilote "en photo jointe"   quelles colonnes il faut choisir ?

current (A) ou  la colonne PK curent .

 

Quelqu'un connait la réponse ?

 

 

 

Spécifica&tion du moteur :

  • Type de moteur: bipolaire
  • Angle de pas: 0.067 deg
  • Couple de serrage sans boîte de vitesses: 52Ncm(73.6oz.in)
  • Courant/phase: 1.68A
  • Résistance/phase: 1.65ohms
  • Tension: 2.8V
  • Inductance: 2.8mH ± 20%(1KHz)

 

Image(s) jointe(s)

  • TB 6600.jpg


#30 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 17 avril 2020 - 04:12

Tu peux régler à 2.0 A   2.2 A  Peak pour ton moteur. 

Perso je règle toujours égale ou juste au dessus du nominal. 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 


#31 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 21 avril 2020 - 06:01

Bonjour , 

J'ai mis en route le moteur suivant l'exemple constant speed que je connaissais . Le réglage de puissance est sur 2.0 A   2.2 A  . Le pas est réglé sur 1600 parce que le chariot se déplace de 11 cm par tour d'axe moteur ( à confirmer avec le matériel ) . Il me semble que c'est bien assez  .

  • Ce moteur est équipé d'un Rapport d'engrenage: 26.85: 1
  •  
  • Il marche bien , mais j'ai une question ?
  • Il y a un truc qui revient souvent dans les explications des pilotes c'est la sortie enable . Avec Accel stepper , les doc  signalent souvent de ne pas en tenir compte . A quoi elle sert ?  C'est un frein moteur ou un embrayage quand la sortie est alimentée ? Et dans mon code ,elle alimenté automatiquement ?
// ConstantSpeed.pde
// -*- mode: C++ -*-


#include <AccelStepper.h>
AccelStepper stepper (1, 9, 8);


void setup()
{  
   stepper.setMaxSpeed(6000);
   stepper.setSpeed(-5000);	
}

void loop()
{  
   stepper.runSpeed();
}


#32 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 21 avril 2020 - 08:04

J'ai fait un calcul du nombre de pas pour que le moteur effectue 1 tour . Si il faut au moteur 1600 pas pour faire un tour complet parce que le pilote est réglé sur 1/8 et que le  Rapport d'engrenage est de 26.85 pour 1 . Si je multiplie 26.85 par 1600 je trouve le nombre de pas qu'il faut donner au code pour faire une rotation complète ? 

Donc j'ai rentrer dans mon code 42960 pas pour faire un tour .  C'est bon comme calcul ?

// Bounce.pde
// -*- mode: C++ -*-
//
// Make a single stepper bounce from one limit to another
//
// Copyright (C) 2012 Mike McCauley
// $Id: Random.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $

#include <AccelStepper.h>

// Define a stepper and the pins it will use

AccelStepper stepper (1, 9, 8);
void setup() {
  // Set the maximum speed and acceleration:
  stepper.setMaxSpeed(3000);
  stepper.setAcceleration(500);
}
void loop() {
  // Set the target position:
  stepper.moveTo(-42960);
  // Run to target position with set speed and acceleration/deceleration:
  stepper.runToPosition(); 
  delay(1000);
  
}


#33 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 21 avril 2020 - 10:13

Oui le calcul est bon, 

Il reste maintenant à vérifier. Tu peux mettre un bout de scotch sur ton axe et lui demander de tourner un tour et voir si il le fait. 
Petite astuce pour ce genre de test : tu peux mettre ton code dans le setup, et appuyer sur le bouton reset de ton arduino à chaque fois que tu veux que ton moteur fasse un tour. 

Du coup tu confirme que tu as bien ton moteur qui tourne et dans les deux sens en fonction de ce que tu lui demande comme consigne ?

 

Le enable ça permet d'autoriser de couper l'alimentation du moteur indépendamment de tout le reste. 
Par exemple tu peux brancher un bouton d'arrêt d'urgence dessus. 

Quelque soit l'état de ton programme si tu appuis sur ce bouton qui contrôle le énable ton moteur ne tournera pas. 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 


#34 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 22 avril 2020 - 09:06

Oui il tourne bien d'un tour ! Dans les deux sens aussi



#35 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 22 avril 2020 - 01:36

Mon premier test pour faire tourner mon moteur avec la commande que l'on a fait avant et sa marche pas !!!! 

J'ai tout retourné la seule explication que j'envisage c'est qu'il faut que runspeed soit dans la loop !! ?? 

Je  comprends pas .

#include <AccelStepper.h>
AccelStepper stepper (1, 9, 8);



const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm



void  initMoteur () {


    if (digitalRead(PinBoutonInit) == 0) {
        stepper.runSpeed();
   
   Serial.println (" Initialisation en cours "  );


    while (digitalRead(PinContactButé) == 1) {
      // on attends la buté 
      }
       stepper.stop();
    Serial.println ("Initialisation terminée " );
    
  }
}



void setup() {
  
  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
     stepper.setMaxSpeed(4000);
  stepper.setSpeed(3900); 
}

void loop() {


  initMoteur ();
 
}


#36 Ludovic Dille

Ludovic Dille

    Membre occasionnel

  • Membres
  • Pip
  • 120 messages
  • Gender:Male
  • Location:Belgique
  • Interests:Robotique, électronique, embarqué, informatique, ...

Posté 22 avril 2020 - 03:02

Hello,
est-ce que tu as bien compris comment fonctionnait ce programme ?

ta fonction initMoteur() te permet lorsque tu appuies sur le bouton d'init de remettre la position de ton chariot à 0 (sur la butée) donc:
- On appuie sur le bouton
- On met le moteur en route
- Quand on reçoit l'information que le bouton de contact de la butée est enfoncée on arrête le moteur

donc mettre le stepper.runSpeed() dans ta loop() ne fera tourner tous le temps ton moteur sans avoir vraiment contrôle dessus (mais si c'est cela que tu cherches, tout va bien).
Donc est-ce que tu as bien tout vérifié ? (typo dans le code, câbles bien branchés, moteur alimenté ....)

Ludo
 



#37 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 22 avril 2020 - 04:04

Salut Ludovic , en fait je me suis dit que stepper.runSpeed marchait peut etre que dans le loops parce ce doit être programmé comme cela simplement . Donc ton message m'a fait pensé que non . Alors je l'ai déplacé comme l'exemple ci dessous . Et là mon moteur tourne ! 

 

Ce qui voudrai dire que mon code ne voit pas le stepper.runSpeed();  . Pourtant le if fonctionne bien parce que " Initialisation en cours " s'affiche lors de l'appuis .

Alors là je sèche !

void  initMoteur () {
stepper.runSpeed();

    if (digitalRead(PinBoutonInit) == 0) {
       


#38 cook

cook

    Membre occasionnel

  • Membres
  • Pip
  • 93 messages

Posté 22 avril 2020 - 05:22

J 'ai rajouté une Led des fois que cela m'apporte une solution . Ma Led marche . Mon com aussi et mon moteur toujours

pas .

Je dois pas être seul a pas comprendre ! C'est bientôt l'apéro

#include <AccelStepper.h>
AccelStepper stepper (1, 9, 8);



const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm
const int LedPin = 10;


void  initMoteur () {


    if (digitalRead(PinBoutonInit) == 0) {
         stepper.runSpeed();
   digitalWrite(10,HIGH);
   Serial.println (" Initialisation en cours "  );


    while (digitalRead(PinContactButé) == 1) {
     //  on attends la buté 
     }
      stepper.stop();
      digitalWrite(10,LOW);
   Serial.println ("Initialisation terminée " );
    
  }
}



void setup() {
  
  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(10, OUTPUT); 
  stepper.setMaxSpeed(4000);
  stepper.setSpeed(-3900); 
}

void loop() {


  initMoteur ();
 
}


#39 Ludovic Dille

Ludovic Dille

    Membre occasionnel

  • Membres
  • Pip
  • 120 messages
  • Gender:Male
  • Location:Belgique
  • Interests:Robotique, électronique, embarqué, informatique, ...

Posté 22 avril 2020 - 05:28

Ok j'ai regardé la documentation et voila ce que j'ai compris la fonction runSpeed(): enfait la fonction runSpeed ne va pas tourner dans une thread pour effectuer les pas à ton moteur quand il faut mais cette fonction va regarder si elle doit effectuer un pas et si oui, l'effectuer. Donc avant ton changement, ton programme n'appelait ta fonction qu'une fois (ensuite il attendait que le moteur touche la butée pour sortir de la boucle mais vu que ton moteur ne tournait pas, il touchait pas la butée et donc rien ne se passait).

Donc une modification que tu pourrais tenter:

#include <AccelStepper.h>
AccelStepper stepper (1, 9, 8);



const int PinBoutonInit = A0; //Bouton qui déplacera le chariot vers la buté à 30 cm
const int PinContactButé = A1;// Contact qui stope le chariot à 30 cm



void  initMoteur () {


    if (digitalRead(PinBoutonInit) == 0) {
   
   Serial.println (" Initialisation en cours "  );


    while (digitalRead(PinContactButé) == 1) {
      stepper.runSpeed();
      // on attends la buté 
      }
       stepper.stop();
    Serial.println ("Initialisation terminée " );
    
  }
}



void setup() {
  
  Serial.begin(9600);
  pinMode(A0, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
  pinMode(A1, INPUT_PULLUP); //  pour le mettre en INPUT_PULLUP
     stepper.setMaxSpeed(4000);
  stepper.setSpeed(3900); 
}

void loop() {


  initMoteur ();
 

Ce qui mettrait tes updates dans la boucle qui attend que le moteur touche la butée
 

Ludo



#40 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 8 896 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 22 avril 2020 - 07:23

Un moteur pas à pas, contrairement à un moteur CC à besoin qu'on lui dise d'avancer à chaque pas pour qu'il avance. 
Dès qu'on arrête de lui dire d'avancer il n'avance plus le bougre. 

La fonction set speed permet de configurer la vitesse à laquelle on veut aller quand on dira au moteur d'avance. 
Mais tant qu'on ne lui dira pas d'avancer il avancera pas.

Répéter en boucle " runSpeed " permet de dire au robot d'avancer en boucle. 


La différence entre une commande en vitesse et une commande en position avec la librairie , c'est que la fonction " runToPosition " est bloquante, elle va dire au moteur de tourner en boucle jusqu'à ce que le moteur soit arrivé. Ce qui est pratique pour une code simple où on éxécute séquentiellement des actions mais pas forcément pratique quand on cherche à faire plusieurs choses en même temps. 

Maintenant que tu sais utiliser runSpeed normalement tu devrais pouvoir l'utiliser dans ta fonction initMoteur =) Si tu regarde on a un " While bloquant dans ta fonction qui est exprès là pour accueillir ton RunSpeed ;)


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 

 

Les réalisations de Mike118  

 

 

 





0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users