Sketch per movimento servo con 4 pulsanti

Ciao a tutti, premetto che sto imparando ad utilizzare e programmare Arduino, attualmente sto imparando le basi con i led.

Veniamo al motivo di questo topic: nella realizzazione di un plastico ferroviario in grande scala (1:32) ho progettato al Cad e realizzato mediante taglio laser una semi-piattaforma girevole per i treni da collocare nella costruenda stazione (semi perchè ha un limitato angolo di rotazione).
Eccola qui:


Attualmente il suo movimento è manuale mediante la rotazione di una manopola collegata ad una vite senza fine ed un ingranaggio solidale al perno della piattaforma.
Qualche mese fa però sono andato alla Maker Fair a Roma ed ho capito che con Arduino posso muovere elettronicamente la piattaforma (quindi l'ho acquistato insieme ad uno starter kit).

Ecco la mia idea: collegare una leva al perno della piattaforma, ed un servo collegato alla leva. Avendo la piattaforma quattro uscite, si potrebbe programmare Arduino per far muovere il servo mediante la pressione di quattro pulsanti, ciascuno di essi identifica un'uscita (1,2,3 e 4) a cui è stato stabilito un determinato angolo di rotazione. Ovviamente anch la velocità di rotazione conta molto, dovendo la piattaforma muoversi in maniera piuttosto realistica.

Qualcuno mi può aiutare?

Ciao
Antonio

La prima cosa da fare è individuare i quattro angoli.
Inizia quindi con un primo circuito da controllare con il monitor seriale.
Puoi fare una cosa in cui se scrivi + l'angolo aumenta di 1, se scrivi - diminuisce, se scrivi +x aumenta di x gradi, -x diminuisce, @x si porta ad x gradi e così via, inviandoti la posizione sul monitor di volta in volta.
Stabilisci così con esattezza quali sono i valori dei quattro angoli.
A questo punto passerai alla fase successiva, con quattro pulsanti ed un semplice IF che ti stabilisce l'angolo in base a quale hai premuto.
Per la velocità: si regola facendo avanzare il servo progressivamente, direi che sarebbe bene in questa applicazione farlo partire lentamente, poi più veloce a metà rotazione, poi di nuovo lento fino a fermarsi. Mi intendo poco di questi argomenti ma se le rotaie sono metalliche potresti anche sfruttare il fatto che fanno contatto elettrico per stabilire il punto esatto in cui fermarti.

Tutto un passo alla volta, visto che hai appena iniziato.
Parti con il semplice motore ed un programma in cui gli dai l'angolo, per fare pratica.

@ paulus1969: Grazie per le indicazioni!

Purtroppo non avendo ancora montato il sistema di leve ed il servocomando non posso identificare gli angoli, credo che adatterò il programma realizzato variando i gradi di rotazione adattandoli al posizionamento di ogni uscita.
Da una mezz'ora mi sono messo a scrivere un codice e dopo vari fallimenti, sono riuscito a fare questa roba qui:

#include <Servo.h>
 
Servo myservo;  // create servo object

int p1 = 2;
int p2 = 3;
int p3 = 4;
int p4 = 5;
 
void setup() {

myservo.attach(9);

pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);

}

void loop() {

int p1 = digitalRead(2);
int p2 = digitalRead(3);
int p3 = digitalRead(4);
int p4 = digitalRead(5);

if (p1 == HIGH) {

myservo.write(30);
delay(15); }

if (p2 == HIGH) {

myservo.write(60);
delay(15); }

if (p3 == HIGH) {

myservo.write(90);
delay(15); }

if (p4 == HIGH) {

myservo.write(120);
delay(15); }

}

Il tutto funziona egregiamente. Alla pressione del tasto voluto il servo raggiunge la posizione che gli è stata indicata da Arduino, in questo caso i gradi 30, 60, 90 e 120 collegati rispettivamente ai tasti 1, 2, 3 e 4.
Attualmente quando alimento Arduino il servo si posiziona a 90° e la cosa mi va bene...se però volessi indicare una posizione iniziale, cosa devo aggiungere al programma?
Altra questione da risolvere è quella della velocità...come faccio per regolarla? Fare come mi suggerivi tu, ovvero partire ad una velocità X, incrementarla a XX durante il tragitto e tornare a quella X prima di fermarsi, sarebbe un sogno ma qui andiamo di molto al di fuori delle mie conoscenze attuali.

Un'altra domanda che mi pongo è questa: volendo dotare la piattaforma di un piccolo catenaccio a scorrimento mosso da un microservo, è possibile realizzare questa sequenza:

pressione tasto > servo "catenaccio" si muove aprendo il catenaccio, breve pausa, servo "piattaforma" ruota fino alla posizione desiderata, breve pausa, servo "catenaccio" si muove chiudendo il catenaccio.

Ciao
Antonio

bernix81:
Un'altra domanda che mi pongo è questa: volendo dotare la piattaforma di un piccolo catenaccio a scorrimento mosso da un microservo, è possibile realizzare questa sequenza:

pressione tasto > servo "catenaccio" si muove aprendo il catenaccio, breve pausa, servo "piattaforma" ruota fino alla posizione desiderata, breve pausa, servo "catenaccio" si muove chiudendo il catenaccio.

Sono riuscito a collegare e comandare i due servi con il movimento da me desiderato e qui sopra descritto...riporto un breve esempio del comando:

if (p1 == HIGH) {

myservo1.write(60);
delay(1000);
  
myservo2.write(30);
delay(1000);

myservo1.write(120);
delay(1000);}

Tutto funziona a meraviglia...ora l'unico problema è quello dell'impostazione della velocità dei servocomandi che non so come programmare.

Ciao
Antonio

La velocità di un servo è una cosa che NON può essere comandata da programma (... non esiste il segnale per farlo).

L'unico modo per simulare una velocità più lenta e raggiungere la posizione finale in ... tanti piccoli passi :slight_smile:

Guglielmo

gpb01:
La velocità di un servo è una cosa che NON può essere comandata da programma (... non esiste il segnale per farlo).

Dipende dal servo, molti modelli digitali sono programmabili in vari parametri, tra cui la velocità, i servo specifici per la robotica si comandano tramite comandi su bus seriale ed è possibile specificare sia la velocità di rotazione che le rampe di accelerazione oltre alla posizione.
Per questo lavoro consiglio caldamente i servo AX12A di Dynamixel, c'è una libreria specifica per Arduino.

astrobeed:
Dipende dal servo, molti modelli digitali sono programmabili in vari parametri, tra cui la velocità, i servo specifici per la robotica si comandano tramite comandi su bus seriale ed è possibile specificare sia la velocità di rotazione che le rampe di accelerazione oltre alla posizione.

Hai ragione, davo per scontato stesse usando quelli classici ( = più economici) ... con il semplice controllo ad impulsi su un singolo filo ... anche perché sta usando la libreria std. Servo e non qualche cosa di specifico ... :grin:

Guglielmo

Prima che te lo dicano i moderatori... ti avverto io, il codice dovrebbe essere contrassegnato con l'apposito comando, quella specie di tasto # in alto.

Senza

Con

In pratica lo scrivi, poi lo evidenzi e premi il tasto di cui sopra.

Passiamo al tuo programma.
Funziona ma....
all'inizio metti:

int p1 = 2;
int p2 = 3;
int p3 = 4;
int p4 = 5;

come se volessi indicare queste variabili per individuare i pulsanti, poi invece le usi così:

int p1 = digitalRead(2);
int p2 = digitalRead(3);
int p3 = digitalRead(4);
int p4 = digitalRead(5);

quindi che cosa le dichiari a fare all'inizio?
e se sono stati digitali, inoltre, puoi farle boolean invece di int, non è meglio?

Visto questo, passiamo al discorso della regolazione di velocità.

Se usi questi motori standard con la libreria standard, occorre suddividere lo spostamento in piccoli tratti come detto da Guglielmo, altrimenti dovresti usare quelli indicati da Astro.

Costruisci una funzione che esegue il movimento, una cosa come una
void spostagraduale (int angolofinale)
che dovrebbe fare le seguenti cose:

  • prendi l'angolo attuale (lo puoi fare in diversi modi)
  • suddividi la differenza fra angolo finale ed angolo attuale in intervalli diseguali, l'intervallo centrale più largo e gli altri via via più stretti, verso gli estremi dell'intervallo
    quindi, tanto per spiegarmi:

sono 9 sottointervalli.
A-B--C---D----E-----F----G---H--I-J
supponiamo di dovere andare da A a J
sei già in A

delay(xxx);
myservo2.write(B);
delay(xxx);
myservo2.write(C);
delay..... ecc ecc

oppure in alternativa suddividi l'intervallo in parti uguali ma modifichi il delay ad ogni passo (forse è meglio).

Così è come lo farei io, forse esistono dei metodi migliori ma li ignoro.

Ho modificato i miei precedenti interventi integrando il codice in maniera corretta. Grazie per la segnalazione e per le correzioni alla compilazione del codice, dagli errori che faccio si capisce che non sono molto esperto.

Dunque, questa sera ho rimesso mano su Arduino, dopo alcune ricerche su internet ho scoperto l'esistenza della libreria "VarSpeedServo".
Ho provato a studiarmela un attimo ed ho compilato il programma di oggi adattato a questa nuova libreria. Eccolo qui:

#include <VarSpeedServo.h>

VarSpeedServo myservo1;
VarSpeedServo myservo2;

void setup() {

myservo1.attach(8, 5, 175);
myservo2.attach(9, 5, 175);

pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);

}

void loop() {

boolean p1 = digitalRead(2);
boolean p2 = digitalRead(3);
boolean p3 = digitalRead(4);
boolean p4 = digitalRead(5);

if (p1 == HIGH) {

myservo1.slowmove (5, 50);
delay(1000);

myservo2.slowmove (5, 10);
delay(5000);

myservo1.slowmove (175, 50);
delay(1000);}

if (p2 == HIGH) {

myservo1.slowmove (5, 50);
delay(1000);

myservo2.slowmove (60, 10);
delay(5000);

myservo1.slowmove (175, 50);
delay(1000);}

if (p3 == HIGH) {

myservo1.slowmove (5, 50);
delay(1000);

myservo2.slowmove (120, 10);
delay(5000);

myservo1.slowmove (175, 50);
delay(1000);}

if (p4 == HIGH) {

myservo1.slowmove (5, 50);
delay(1000);

myservo2.slowmove (175, 10);
delay(5000);

myservo1.slowmove (175, 50);
delay(1000);}

}

Adesso i movimenti sono più lenti e molto più adatti ai miei scopi ludici. Ho ancora qualche cosa da aggiustare però...mi spiego meglio.

Con servo1 ho identificato il "catenaccio" che alla pressione di un qualsiasi pulsante si apre, avviene lo spostamento della piattaforma e poi si richiude.
Con servo2 ho identificato la piattaforma.
Ho impostato il tempo tra apertura e chiusura del servo1 in 5 secondi, ovvero poco più del tempo che il servo2 che sto utilizzando ora impiega per andare dalla posizione massima alla minima e viceversa. Se però faccio fare una rotazione minima, tipo da 5° a 60°, il tempo di servo2 è minore e quindi c'è un pò di attesa prima che servo1 torni in posizione iniziale.
Come posso fare per ovviare a questa cosa? Ovvero, come posso personalizzare i tempi di servo1 in base a ciascun movimento?
La cosa più immediata che mi viene in mente è creare tanti if quante sono le possibilità di movimento (16), e cioè:

Da 5° a 5° -> x operazioni
Da 5° a 60° -> x1 operazioni
Da 5° a 120° -> x2 operazioni
Da 5° a 175° -> x3 operazioni
e così via.
In questo caso però credo che il programma debba tenere a mente la posizione da cui parte il movimento per poi stabilire le operazioni da eseguire alla pressione di un tasto qualsiasi.
Qualcuno mi può dare un'idea di come sia fatto il codice?

Altra questione riguarda sempre il servo di rotazione: paulus mi hai messo in testa il fatto di far accelerare e poi decelerare il servo durante il movimento, purtroppo però non riesco a capire (causa inesperienza) come poter utilizzare le indicazioni che mi hai scritto e non so nemmeno se vanno bene con questa nuova libreria (ma credo di sì). Potresti spiegarmi meglio?
Io ho provato una cosa del genere ma non funziona (supponendo la partenza da 5° e l'arrivo a 175°):

myservo2.slowmove (35, 10);
myservo2.slowmove (145, 100);
myservo2.slowmove (175, 10);

Grazie a tutti!

Ciao
Antonio

Mai usata questa varspeedservo... sembra interessante.
Riassumimi cosa fa o collegami una pagina e vediamo come adattare il software.

paulus1969:
Mai usata questa varspeedservo... sembra interessante.
Riassumimi cosa fa o collegami una pagina e vediamo come adattare il software.

L'ho presa sempre qui sul forum...ecco qui la discussione: VarSpeedServo - a modified Servo library with speed control - Motors, Mechanics, Power and CNC - Arduino Forum

Un'altra idea che mi è venuta in mente oggi per il problema dei tempi di servo1 rispetto a servo2 è questa: se esiste la possibilità, tramite qualche comando particolare, si potrebbe impostare il movimento finale di servo1 dopo che servo2 ha terminato il suo movimento.

Ciao
Antonio

Se adoperi lo stratagemma di suddividere lo spostamento in diversi tratti in modo da avere una velocità diversa durante le varie fasi dello spostamento, il sistema sarà impegnato a controllare questi movimenti ed uscirà dalla funzione di cui parlavamo solo alla fine dello spostamento. Quindi, se costruisci così la funzione per lo spostamento, basta dare il comando per servo1 dopo quello che richiama la funzione per spostare servo2.
In alternativa, come ti avevo scritto prima, puoi sfruttare il contatto elettrico delle rotaie.
Qui chiedo a te, infatti le mie esperienze sono ferme ai trenini di quando avevo pochi anni e forse i sistemi per modellismo sono diversi. Ricordo che c'erano le due rotaie metalliche che portavano la corrente alle locomotive. Una era il polo +, l'altra era il polo -.
Se è così, allora ti potrebbe anche tornare utile far sapere al software quale delle tre linee ferroviarie è stata inserita e puoi farlo grazie al fatto che una volta inserita vi sarà la tensione elettrica fra le due rotaie.
La sequenza diventa

  • apri il fermo (servo1)
  • ruota (servo2)
  • appena leggi il collegamento elettrico della linea, chiudi il fermo (servo1)

Purtroppo il discorso di prendere corrente dalle rotaie non è fattibile...è vero che in prossimità delle uscite c'è corrispondenza tra quelle positive e tra quelle negative, ma durante la rotazione per forza di cose finirebbero in corto, infatti la piattaforma ha un'alimentazione separata che viene dal perno di rotazione e nel punto di transizione tra piattaforma e "terraferma" le rotaie hanno 1,5mm di distanza tra loro.
Per quanto riguarda il suggerimento di suddividere il movimento del servo in piccoli spostamenti, al momento va al di fuori delle mie basiche capacità di programmazione.
Mi chiedo invece se esiste la possibilità di far memorizzare ad Arduino la posizione precedentemente raggiunta dal servo, in modo da poter programmare le 16 possibilità di movimento in base alla pressione dei tasti...in quel caso la questione sarebbe sì laboriosa, ma con movimenti dedicati e precisi al secondo (o frazione di...).

Ciao
Antonio

Certo che puoi.
C'è il servo.read() che ti dice la posizione del servo (credo vada a prendere l'ultimo .write() ).
Ma in ogni caso... in linea di principio, di tutto quello che controlli puoi memorizzare lo stato.

paulus1969:
Certo che puoi.
C'è il servo.read() che ti dice la posizione del servo (credo vada a prendere l'ultimo .write() ).
Ma in ogni caso... in linea di principio, di tutto quello che controlli puoi memorizzare lo stato.

Grazie per l'informazione...mi potresti dire come impostare lo sketch inserendo il servo.read()?
Nel senso, come faccio a scriverlo volendogli dire: se premo questo bottone e la posizione del servo è questa, le azioni da fare sono queste altre e così via per gli altri if.

Grazie 1000 per l'aiuto
Antonio

Fammi un esempio di cosa vuoi fare con un pulsante, poi è immediato estenderlo a 4 pulsanti.

Per i pulsanti, potresti usare uno degli schemi che trovi nella raccolta ABC (vedi megatopic) che consente di usare 4 pulsanti su un ingresso analogico. Per un mio progetto, ho recentemente usato questo sistema + un display LCD 16x2 caratteri alfanumerico ed un buzzer piezoelettrico. Il buzzer emette un bip quando premi un pulsante, cosa che ti consente di avere un feedback della pressione e ti funga anche da antirimbalzo se incorpori nel beep un piccolo delay.
Ho costruito una funzione int pulsante() che restituisce un valore da 1 a 4 a seconda di quale pulsante è stato premuto, rimanendo in attesa della pressione di un pulsante con un semplice while (not keypressed) dove keypressed è una boolean che passa a true quando ne premi uno.
A questo punto conosci il numero corrispondente al pulsante con pulsnum = pulsante(), assegni ad un'altra variabile il servo.read() con posizservo = servo1.read() e vai con un IF (pulsnum == 1 && posizservo == 60) ... ecc ecc dove && rappresenta l'AND, quindi l'IF è vero se sono verificate ENTRAMBE le condizioni.

paulus1969:
Fammi un esempio di cosa vuoi fare con un pulsante, poi è immediato estenderlo a 4 pulsanti.

Per i pulsanti, potresti usare uno degli schemi che trovi nella raccolta ABC (vedi megatopic) che consente di usare 4 pulsanti su un ingresso analogico. Per un mio progetto, ho recentemente usato questo sistema + un display LCD 16x2 caratteri alfanumerico ed un buzzer piezoelettrico. Il buzzer emette un bip quando premi un pulsante, cosa che ti consente di avere un feedback della pressione e ti funga anche da antirimbalzo se incorpori nel beep un piccolo delay.
Ho costruito una funzione int pulsante() che restituisce un valore da 1 a 4 a seconda di quale pulsante è stato premuto, rimanendo in attesa della pressione di un pulsante con un semplice while (not keypressed) dove keypressed è una boolean che passa a true quando ne premi uno.
A questo punto conosci il numero corrispondente al pulsante con pulsnum = pulsante(), assegni ad un'altra variabile il servo.read() con posizservo = servo1.read() e vai con un IF (pulsnum == 1 && posizservo == 60) ... ecc ecc dove && rappresenta l'AND, quindi l'IF è vero se sono verificate ENTRAMBE le condizioni.

Dovrò rileggermi un paio di volte il tuo post per comprendere bene tutte le informazioni (in pratica mi devo andare a studiare tutto :slight_smile: ).
Comunque, vorrei dare al sistema una sorta di inteligenza riguardante il posizionamento della piattaforma.
Partendo dall'uscita 1 del treno, considerando il tasto 1 corrispondente a quell'uscita e così via per 2,3 e 4, vorrei impostare quanto segue (i valori dei gradi e dei secondi sono puramente indicativi):

Se premo 1 ed il ponte si trova all'uscita 1 non succede nulla.
Se premo 2 ed il ponte si trova all'uscita 1 si apre il catenaccio, avviene la rotazione di 30 gradi e dopo 3 secondi si chiude il catenaccio.
Se premo 3 ed il ponte si trova all'uscita 1 si apre il catenaccio, avviene la rotazione di 60 gradi e dopo 6 secondi si chiude il catenaccio.
Se premo 4 ed il ponte si trova all'uscita 1 si apre il catenaccio, avviene la rotazione di 90 gradi e dopo 9 secondi si chiude il catenaccio.

E così via, programmando tutte le 16 probabilità di movimento che ci sono, in modo da poter avere dei tempi di movimento perfetti e tempi dedicati ad ogni singola azione, rendendo tutto molto realistico.

Di nuovo grazie per la disponibilità.

Ciao
Antonio

Basta tradurre in codice quello che hai detto a parole!
Un po' di IF e di AND (&&).
Potresti anche creare una funzione che calcola tutto in base a posizione attuale - posizione finale e restituisce i parametri che ti servono, viene un codice più pulito e lineare.
In questi casi è utile ragionare "a blocchi".
Di cosa hai bisogno?
Di tre blocchi.

Primo blocco: attende che sia premuto un tasto e ti dice quale tasto è stato premuto

Secondo blocco: in base al tasto premuto ed alla posizione attuale, ti dice a quale angolo dovrà ruotare il motore, quanto tempo ci impiegherà, come devi suddividere lo spostamento se vuoi fare un movimento più lento all'inizio ed alla fine e più veloce al centro, dopo quanto tempo dovrà essere chiuso il fermo

Terzo blocco: apre il fermo, ruota, chiude il fermo

Forse quello leggermente più complicato è il secondo blocco.

Discussione interessante. :wink:

paulus1969:
Basta tradurre in codice quello che hai detto a parole!
Un po' di IF e di AND (&&).

Il mio problema è che non riesco ad impostare tutto lo sketch perchè non so cosa devo scrivere nella parte iniziale, nel setup e nel loop...vado per tentativi ma ovviamente sbaglio. Mi aiuti?

Ciao
Antonio