logo

Laboratorio di Intelligenza Artificiale

A.A. 2006/2007

The Mindstorm Project

il robot

Giuseppe Fotino

Marco Rivoltella

Indice

Il progetto

Scopo del progetto è costruire un agente autonomo intelligente, che opererà in un ambiente costituito da stanze (realizzate in cartone) collegate tra di loro attraverso delle aperture nelle pareti. L'agente dovrà essere in grado di elaborare la strategia migliore per spostarsi da una posizione di partenza ad una posizione di arrivo, eseguire passo per passo il piano calcolato e reagire di fronte a particolari condizioni di errore. Il robot lavora nell'ipotesi di conoscere esattamente la posizione, le dimensioni di tutte le stanze e come queste sono collegate tra di loro; il robot non conosce la posizione degli eventuali ostacoli presenti lungo il percorso (anche questi realizzati con del cartone), pertanto dovrà rilevarne la presenza attraverso un sensore ed eventualmente elaborare un piano alternativo per giungere alla destinazione prefissata.

L'ipotesi di conoscenza completa

In logica, l'ipotesi di conoscenza completa (o del mondo chiuso) afferma che tutti gli atomi veri siano stati comunicati al sistema. L'agente, quindi, considererà falsi tutti gli atomi che non sono stati espressamente dichiarati veri o per i quali la dimostrazione fallisca in un numero finito di passi.
Possiamo affermare che il robot lavora sotto questa ipotesi poiché prima di elaborare ed eseguire il piano conosce esattamente la sua posizione e quella di tutte le stanze; questa condizione si verifica esattamente quando l'agente non incontra alcun ostacolo sul suo cammino. Nel caso in cui siano presenti degli ostacoli, il robot deve acquisire informazioni circa la loro posizione, quindi opera in una situazione di informazione incompleta.

L'agente

Il robot è stato realizzato utilizzando i mattoncini programmabili della serie Lego MindStorm NXT ® ricalcando la struttura del tribot, ovvero il modello di robot dotato di due ruote motrici e direttrici completamente indipendenti e di una terza ruota più piccola per la stabilità. Il tribot è predisposto per l'utilizzo di quattro diversi sensori; per i nostri scopi è stato sufficiente montare il sensore ad ultrasuoni accoppiato ad un terzo motore per estenderne il raggio d'azione.

Linguaggi di Programmazione

Insieme al Mindstorm viene fornito NXT-G, un software di programmazione grafica basato su LabVIEW della National Instruments. Questo software è adatto per una programmazione di base, come il controllo dei motori e dei sensori e per fare calcoli; usa un interfaccia grafica che sfrutta il drag & drop per creare dei flussi di lavoro; in alternativa si possono usare l' NBC (Next Byte Code) o l' NXC (Not eXactly C): il primo è un linguaggio assembler-like, quindi a basso livello, il secondo è un linguaggio C-like.
Dopo alcuni tentativi di utilizzo del linguaggio NXC, a causa delle forti limitazioni che questo imponeva (non consentiva l'utilizzo di struct, l'allocazione dinamica della memoria e la multiprogrammazione) si è scelto di utilizzare LeJOS per le funzionalità offerte rispetto agli altri linguaggi, per esempio la possibilità di lanciare processi concorrenti, per l'ampio pacchetto di librerie disponibili e non ultimo, per la facilità di programmazione (essenzialmente si tratta di Java).
LeJos fu inizialmente concepito come TinyVM da José Solórzano che, nel 1999, iniziò per hobby a sviluppare un progetto open source. Si tratta di un firmware sostitutivo per il Mindstorm, che incorpora una Java Virtual Machine e quindi permette di eseguire programmi scritti in Java. Il nome LeJOS deriva dall'acronimo di Java Operating System (JOS) e legOS.

LeJOS offre tra le altre cose:

Rappresentazione del mondo

Il mondo è formato da un insieme di stanze collegate tra loro, realizzate con del cartone e del nastro adesivo. Le stanze comunicano tra di loro attraverso delle aperture nelle pareti (porte). Gli ostacoli sono delle pareti di cartone che è possibile spostare a piacere.

Le Stanze

L'ambiente in cui il robot opera è suddiviso in stanze: ogni stanza è un'area rettangolare delimitata da quattro pareti.
All'interno di ciascuna stanza è poi definita “un'area di sicurezza” (PADDING) il cui scopo sarà descritto in seguito. Di tutte le stanze sono note le coordinate del vertice superiore sinistro e le dimensioni espresse in pollici.

  1. public class Stanza
  2. {
  3. /* Il vertice superiore sinistro avrà coordinate (x,y)
  4. * La stanza avrà dimensioni w (larghezza) per h
  5. * (altezza) mentre il centro della stanza si troverà nel
  6. * punto (a,b).
  7. * Le dimensioni sono espresse in pollici.
  8. * Il vettore V conterrà la lista delle stanze adiacenti
  9. */
  10. private static final float PADDING = 4;
  11. private int x,y,a,b,w,h;
  12. private Vector v;
  13. private int padre;
  14. private double costo;
  15. /* Costruttore della classe Stanza */
  16. public Stanza(int x, int y, int w, int h)
  17. {
  18. this.x = x;
  19. this.y = y;
  20. this.h = h;
  21. this.w = w;
  22. this.a = x + w/2;
  23. this.b = y + h/2;
  24. v = new Vector();
  25. }

Il Mondo

L'insieme delle stanze S e delle coppie di stanze comunicanti C ⊂ P(C) costituisce un grafo connesso non orientato. Si è scelto di rappresentare tale grafo mediante un array di elementi di tipo stanza, ognuno dei quali conterrà una lista di riferimenti a quelli adiacenti. Il peso di ciascun arco (il cammino tra due stanze comunicanti) sarà stimato attraverso la distanza tra i loro centri.

Le Aree Non Accessibili

La descrizione dell'ambiente viene trasmessa al robot attraverso alcuni file di configurazione.
Secondo l'ipotesi di conoscenza completa, l'agente considererà inaccessibile ogni area non mappata nel file stanze.txt. Ogni riga di questo file descrive completamente una stanza attraverso la quadrupla composta dalle due coordinate del suo vertice superiore sinistro e sue dalle dimensioni.
Due stanze i e j saranno considerate non comunicanti tra loro se nella matrice contenuta in collegamenti.txt (un file di testo che contiene la matrice di adiacenza del grafo delle stanze) non c'è un 1 in posizione i, j.

  1. import java.util.*;
  2. import java.io.*;
  3. public class Mondo
  4. {
  5. /* v è l'array delle stanze
  6. * (xi, yi) sono le coordinate della sorgente
  7. * (xf, yf) sono le coordinate della destinazione */
  8. private Vector v;
  9. public int xi, yi, xf, yf;
  10. public Mondo() throws Exception
  11. {
  12. FileInputStream f = new FileInputStream(newFile("config.txt"));
  13. this.xi = readInt(f);
  14. this.yi = readInt(f);
  15. this.xf = readInt(f);
  16. this.yf = readInt(f);
  17. f.close();
  18. if(xi == -1 || yi == -1 || xf == -1 || yf == -1)
  19. throw new Exception("file non valido");
  20. f = new FileInputStream(new File("stanze.txt"));
  21. v = new Vector();
  22. int[] array = new int[4];
  23. int i = 0;
  24. while((array[i] = readInt(f)) != -1)
  25. {
  26. if (i == 3)
  27. {
  28. Stanza s = new Stanza(array[0], array[1], array[2], array[3]);
  29. v.addElement(s);
  30. i = 0;
  31. }
  32. else i ++;
  33. }
  34. f.close();
  35. f = new FileInputStream(new File("collegamenti.txt"));
  36. for (i = 0; i < v.size(); i++)
  37. {
  38. Vector a = new Vector();
  39. for(int j = 0; j < v.size(); j++)
  40. {
  41. a.addElement(new Integer(readInt(f)));
  42. Stanza s = (Stanza) v.elementAt(i);
  43. s.setVector(a);
  44. }
  45. f.close();
  46. }
  47. /* Metodo per la lettura di un intero positivo da file */
  48. private int readInt(FileInputStream f) throws IOException
  49. {
  50. int b;
  51. int tmp = 0;
  52. while((b = f.read()) != -1)
  53. {
  54. if(b == 32 || b == 13) //CR o BLANK
  55. return tmp;
  56. else
  57. if (b < 48 || b > 57 )
  58. continue; //ignorati i caratteri non numerici
  59. else
  60. tmp = (tmp * 10) + (b - 48);
  61. }
  62. return -1;
  63. }

Gli Ostacoli

Gli “ostacoli” (pareti mobili in cartone) costituiscono delle particolari condizioni di errore nell'esecuzione del piano, poiché l'agente non è a conoscenza della loro eventuale disposizione. L'interazione tra agente ed ostacoli sarà descritta in maniera più approfondita nel paragrafo relativo al sensing.

Pianificazione

L'Algoritmo

L'algoritmo per l'elaborazione del piano altro non è che una visita del grafo.
In particolare, operando su un grafo finito e di ridotte dimensioni e conoscendo il peso di ogni arco, si è scelto di implementare un algoritmo che, sebbene oneroso dal punto di vista della memoria occupata, garantisce un risultato ottimo.

LOWEST-COST FIRST:

  1. INPUT(grafo G, nodo Sorgente, nodo Destinazione)
  2. Per ogni n, nodo del grafo G
  3. marca il n come non visitato
  4. imposta il costo di n a ∞
  5. Costruisci Q, coda di priorità
  6. Inserisci Sorgente in Q
  7. Marca Sorgente come visitato
  8. Imposta il costo di Sorgente a 0
  9. Finché Q non è vuota
  10. estrai p, primo elemento di Q
  11. per ogni elemento v nella lista degli elementi adiacenti
  12. di p
  13. se v non è ancora stato visitato
  14. marca v come visitato
  15. imposta p come padre di v
  16. imposta il costo di v uguale al costo di p
  17. sommato al peso dell'arco pv
  18. inserisci v in Q nella posizione corretta
  19. Crea PATH, lista di nodi
  20. Inserisci Destinazione in PATH
  21. Finché non arrivi a Sorgente
  22. prendi u ultimo nodo inserito
  23. inserisci p, padre di u in PATH
  24. OUTPUT(PATH)

Si tratta di una visita in ampiezza del grafo delle stanze, avente come nodo sorgente la stanza in cui il robot si trova all'inizio delle operazioni e come nodo destinazione quella in cui è situato il punto di arrivo. La frontiera viene gestita come una coda di priorità ordinata secondo il costo del cammino parziale tra la sorgente ed il nodo che viene inserito di volta in volta; vengono espansi per primi i cammini con costo più basso.

  1. /* Metodo della classe Mondo che implementa l'algoritmo
  2. * di visita */
  3. public Stack visita(int inizio, int fine)
  4. {
  5. Stanza stanza;
  6. PriorityQueue q;
  7. Stack s;
  8. for(int i = 0; i< v.size(); i++)
  9. {
  10. stanza = (Stanza) v.elementAt(i);
  11. stanza.setPadre(-1);
  12. stanza.setCosto(-1);
  13. }
  14. q = new PriorityQueue();
  15. q.insert((Stanza) v.elementAt(inizio));
  16. stanza = (Stanza) v.elementAt(inizio);
  17. stanza.setPadre(-2);
  18. stanza.setCosto(0);
  19. while(!q.isEmpty())
  20. {
  21. Stanza tmp = (Stanza) q.pop();
  22. Vector adiacenze = tmp.getVector();
  23.       for(int i = 0; i < adiacenze.size(); i++)
  24.       {
  25.         Integer j = (Integer) adiacenze.elementAt(i);
  26.         if (!j.toString().equals("0"))
  27.         {
  28.           stanza = (Stanza) v.elementAt(i);
  29.           if(stanza.getPadre() == -1)
  30.           {
  31.             stanza.setPadre(v.indexOf(tmp));
  32. stanza.setCosto(stanza.distanza(tmp) + tmp.getCosto());
  33.             q.insert((Stanza) v.elementAt(i));
  34.           }
  35.         }
  36.       }
  37. }
  38. s = new Stack();
  39. for(int index = fine; index != -2;)
  40. {
  41.       stanza = (Stanza) v.elementAt(index);
  42.       s.push(stanza);
  43.       index = stanza.getPadre();
  44. }
  45. return s;
  46.   }

Esecuzione Del Piano

Una volta calcolato il piano è sufficiente scorrere a rovescio la lista dei padri per ottenere il cammino più breve dalla sorgente alla destinazione.

  1.   /* Metodo della classe Movimenti che muove il robot al
  2. *centro della stanza */
  3.   private void goRoom(Stanza s)
  4.   {
  5.     tn.goTo(s.getA(), s.getB());
  6.   }
  7.   /* Metodo della classe Movimenti che esegue il piano */
  8.   public void run()
  9.   {
  10. /* calcolo del piano */
  11. int inizio = mondo.owns((int)tn.getX(), (int)tn.getY());
  12. int fine = mondo.owns(mondo.xf, mondo.yf);
  13. Stack s = mondo.visita(inizio, fine);
  14. /* il piano viene eseguito passo per passo fino al suo
  15.     * completamento o al presentarsi di una condizione di
  16.     * errore */
  17. while(!killed && !s.empty())
  18. {
  19.       try
  20.       {
  21.         Stanza room = (Stanza) s.pop();
  22.         goRoom(room);
  23.       }
  24.       catch(Exception e)
  25.       {
  26.         LCD.drawString("Movimenti", 2, 4);
  27.         LCD.refresh();
  28.       }
  29. }
  30. if(!killed)
  31. {
  32.       tn.goTo(mondo.xf, mondo.yf);
  33.       Sens.finish = true;
  34. }
  35.   }
  36.   /* Arresta il robot */
  37.   public void halt()
  38.   {
  39. left.stop();
  40. right.stop();
  41.   }
  42.   /* Segnala la condizione di errore */
  43.   public void kill()
  44.   {
  45. killed = true;
  46. tn.updatePosition();
  47.   }

Sensing

Le operazioni di Sensing, attraverso le quali l'agente acquisisce informazioni sullo stato del mondo, devono essere eseguite con continuità durante l'esecuzione del piano, per poter rilevare la presenza di eventuali ostacoli lungo il percorso. Qualora la rilevazione individuasse un ostacolo “troppo vicino” al robot, questo deve interrompere ogni altra azione cercare di aggirarlo (o quantomeno fermarsi per evitare l'urto !!).
Queste esigenze impongono di strutturare il codice come un insieme di processi correlati tra di loro.

  1.   import lejos.nxt.*;
  2.   import lejos.navigation.*;
  3.   import java.util.*;
  4.   public class Sens extends Thread
  5.   {
  6.     private static final int BETA = 30;
  7.     public static final int MIN = 30;
  8.     private static final int SIGHT = 45;
  9. public static boolean finish = false;
  10. public static int offset;
  11. public static boolean enable = true;
  12. private UltrasonicSensor uss;
  13. private Movimenti movthread;//processo del piano generale
  14. private Drill drill;//processo della reazione
  15. private Mondo mondo;
  16. private Motor sensormotor;
  17. private boolean dead;
  18. private Stanza attuale;
  19. private TachoNavigator tn;
  20. private int distance;
  21. public void run()
  22. {
  23.       while(!finish)
  24.       {
  25.         distance = sensing(); //rilevazione
  26.         if(movthread.isAlive())
  27.         {
  28.   /* se un ostacolo viene rilevato il piano principale
  29.   * viene interrotto */   
  30.           if(distance < MIN)  
  31.           {
  32.             movthread.halt(); 
  33.             attuale = mondo.getStanza(mondo.owns(movthread.getX(),movthread.getY()));
  34. tn = movthread.getTN();
  35.             movthread.kill(); 
  36.           }
  37.       }
  38.       else
  39.       {
  40.         /* avvio del comportamento reattivo */
  41.         if(drill == null)
  42.         {
  43.           float theta = triangola(distance, tn);
  44.           drill = new Drill(tn, theta, sensormotor, attuale);
  45.           drill.start();
  46.         }
  47.         else
  48.         {
  49.   /* terminazione della reazione – breccia trovata */
  50.           if(!drill.finished)
  51.           {
  52.             if(distance > SIGHT)
  53.             { 
  54.               drill.halt(); /* entra */
  55.               tn = drill.kill();     
  56.               tn.stop();
  57.               tn.updatePosition();          
  58.             }
  59.           }
  60.           else
  61.           {
  62.             /* ripianificazione */
  63.             drill = null;
  64.             movthread = new Movimenti(mondo, tn);              
  65.             movthread.start(); 
  66.           }
  67.         }
  68.       }
  69.   }

Rilevazione Di Ostacoli

  1.   /* Metodo della classe Sens che legge dal sensore ad
  2.   * ultrasuoni */  
  3.   private int sensing()
  4.   {
  5. if(enable) //la lettura può essere disabilitata
  6. {
  7.       int distance = uss.getDistance();
  8.       try
  9.     {
  10.         this.sleep(250); //una lettura ogni 250 ms
  11.     }
  12.     catch(InterruptedException ie){}
  13.     return distance;
  14.   }
  15.   return MIN;
  16. }

Reazione

Non appena il sensore ad ultrasuoni rileva un ostacolo, viene immediatamente fermata l'esecuzione del piano principale. A questo punto l'agente reagisce e cerca di aggirare la parete. La reazione si può dividere in quattro fasi principali.

triangolazione

Una procedura determina con precisione (possono comunque verificarsi errori dovuti al sensore, agli attuatori ed alla rapresentazione interna dei numeri a virgola mobile) la posizione dell'ostacolo, ovvero l'angolo che la parete forma con l'asse delle ascisse.

  1.   public float triangola(int distance, TachoNavigator TN)
  2.   {
  3. /* situazione del robot "al momento dell'impatto" 
  4.     * comprensiva delle coordinate x ed y e dell'angolo
  5.     * tra il robot e l'asse delle ascisse */
  6. int d = distance; /distanza tra robot ed ostacolo
  7. float x = TN.getX();
  8.   float y = TN.getY();
  9.   float alpha = TN.getAngle();    
  10.   int beta = BETA;
  11.   sensormotor.rotate(beta);  // ruota il sensore di beta gradi
  12.   int r = uss.getDistance(); //lettura dal sensore
  13.   try
  14.   {
  15.     this.sleep(250);
  16.   }
  17.   catch(InterruptedException ie){}
  18.   /* se la lettura non ha dato un esito soddisfacente
  19. * (lettura nella breccia oppure parete troppo distante) 
  20. * viene ripetuta dall'altra parte */
  21.   if (r > SIGHT) //SIGTH è un'altra costante
  22.   {
  23.     beta = -beta;
  24.     sensormotor.rotate(2 * beta);
  25.     r = uss.getDistance();
  26.     try
  27.     {
  28.       this.sleep(250);
  29.     }
  30.     catch(InterruptedException ie){}
  31.   } 
  32.   sensormotor.rotateTo(offset); // riallinea il sensore
  33. /* un semplice calcolo trigonometrico */
  34.   float gamma = (float)Math.toDegrees
  35.       (Math.atan((r * Math.sin(Math.toRadians(alpha + beta)) -         
  36.   d * Math.sin(Math.toRadians(alpha))) 
  37.   / (r * Math.cos(Math.toRadians(alpha + beta)) - 
  38.    d * Math.cos(Math.toRadians(alpha)))));
  39.   return gamma;
  40. }
trigonometria
ricerca della breccia

Il robot orienta il sensore perpendicolarmente all'ostacolo e si muove parallelamente a questo, cercando un passaggio attraverso il quale passare. Se dovesse arrivare al bordo della stanza senza averlo trovato, riprenderà a muoversi in direzione opposta per controllare anche dall'altra parte.
Il robot conosce in ogni momento la sua posizione perché questa viene costantemente aggiornata. Poiché il sensore è impegnato nella ricerca del passaggio, non è possibile utilizzarlo per evitare le pareti della stanza (che il robot conosce). Per evitare che sbatta è stato introdotto un PADDING, ovvero un margine entro il quale il robot si ferma.

  1.   /*Metodo run della classe Drill che fa muovere il mindstorm   
  2. * per tutta la lunghezza  dell'ostacolo */  
  3.   public void run()
  4.   {
  5. Sens.enable = false; 
  6. sx = riposiziona(gamma); 
  7. Sens.enable = true;  
  8. while(!killed)  /* finchè il thread non viene ucciso */           
  9. {
  10.       if (forward && stanza.in(tn.getX(), tn.getY()))
  11.       /* se vado avanti e sono nella stanza */ 
  12.       {
  13.       tn.forward();
  14.         tn.updatePosition();
  15.       }
  16.       else
  17.       {
  18.         tn.stop();
  19.         forward = false;
  20.       }
  21.       if(!forward && stanza.in(tn.getX(), tn.getY()))
  22.       /*  sono arrivato in fondo ma non c'è il passaggio */
  23.       {
  24.         tn.backward();
  25.         tn.updatePosition();
  26.       }
  27.       else tn.stop();
  28.   } 
  29.   tn.stop();   
  30.   if(!killed) /* non c'è nessun buco e il thread è attivo */
  31.   {
  32.   try
  33.   { 
  34.     throw new Exception("TU NON PUOI PASSARE!");
  35.   } 
  36.   catch(Exception e)
  37.   {
  38.     Sens.finish = true; /* blocco anche il sensore */
  39.     tn.pilot.stop();
  40.     LCD.drawString(e.getMessage(), 2, 2);
  41.     LCD.refresh();
  42.   }
attraversamento

Una volta che il passaggio è stato individuato, posiziono il robot in modo che sia perpendicolare alla parete e lo attraverso.

  1.   /* Quando il sensore rileva un passaggio, uccido il thread, 
  2.   * ma prima faccio attraversare il passaggio al robot */
  3.   public TachoNavigator kill()
  4.   {
  5. killed = true; /* dico al thread di uscire dai cicli */
  6. if(sx) /* ruoto di + o – 90 gradi */
  7.       tn.rotate(-270);
  8. else 
  9.       tn.rotate(270); 
  10.   tn.updatePosition();
  11.   sensormotor.rotateTo(Sens.offset); /* giro il robot */
  12.   tn.travel(8);      /* attraverso il passaggio */
  13.   finished = true;  
  14.   return tn;
  15. }
Ripianificazione

Una volta superato l'ostacolo il robot ricalcola le sue coordinate ed elabora un nuovo piano verso la stessa destinazione.

  1.   drill = null; /* inizializzo drill a null, in modo da *
  2.                 * poter evitare successivi ostacoli */
  3.   movthread = new Movimenti(mondo, tn);
  4.   /* faccio ripartire movthread con le nuove coordinate */             
  5.   /* movthread si ricalcolerà il piano */
  6.   movthread.start();

Difficoltà incontrate

L'agente in azione

Riferimenti bibliografici

Sito ufficiale di Lego Mindstorm NXT ®
Prove tecniche del sensore ad ultrasuoni.
Analisi meccanica dei servomotori.
LeJOS, Java per Lego Mindstorm.
Sito ufficiale Java
Libusb, driver usb per il mindstorm

Appendice

Caratterisiche Tecniche

Il Brick

Il corpo del robot è costituito da un mattoncino programmabile della serie Lego Mindstorms NXT.

brick
specifiche tecniche
Il Sensore ad Ultrasuoni

Il sensore ad ultrasuoni è lo strumento di cui si serve il robot per analizzare lo spazio circostante.

sensor
specifiche tecniche
note
Gli Attuatori
motor

Il robot può gestire fino ad un massimo di tre servomotori. Due di questi montano le ruote e vengono utilizzati per gli spostamenti all'interno dell'ambiente. La possibilità di controllare in modo indipendente la velocità ed il verso di rotazione di ogni motore permette alle ruote motrici di essere anche direttrici del movimento (similmente a quanto accade per i cingoli di un carro armato).
Il terzo motore, invece, viene utilizzato per estendere il “campo visivo” del sensore ad ultrasuoni ruotandolo in una direzione specifica.

note tecniche

Per una trattazione più completa delle problematiche di ambito meccanico (analisi del rapporto tra coppia applicata e velocità di rotazione
dell'albero motore) si può fare riferimento al materiale fornito da Lego oppure a quello presente in rete.

Istruzioni di Installazione

Installazione di Java sul proprio PC

Scaricate installate Java JDK 6 Update 5.
Una volta conclusa l'installazione cliccate con il tasto destro del mouse su risorse del computer e scegliete proprietà: nuova schermata selezionate avanzate, poi variabili d'ambiente.
Cliccate nuovo e nella nuova finestra impostate una nuova variabile che si chiamerà JAVA_HOME avente come valore il percorso completo di java (di default è C:\programmi\java\jdk1.6.0_05) e confermate. Cercate la variabile PATH modificatela aggiungendo alla fine della riga ; ed il percorso della cartella java\bin (di default è C:\programmi\java\jdk1.6.0_05\bin).

Installare i driver USB per LEGO ® sul PC

L' NXT può essere collegato al computer via USB con il cavetto fornito dal costruttore oppure via Bluetooth. La comunicazione via USB è più affidabile e veloce, ma richiede l'installazione di alcuni.
Scaricate e installate il driver; una volta conclusa l'installazione riavviate il pc.

Installare Lejos sul proprio PC

Scaricate LeJOS (è un archivio zip) decomprimetelo.
Copiate la cartella di lejos in C:\ .
Cliccate con il tasto destro del mouse su risorse del computer e scegliete proprietà: nella nuova schermata selezionate avanzate, poi variabili d'ambiente.
Cliccate nuovo e nella nuova finestra impostate una nuova variabile che si chiamerà NXJ_HOME avente come valore il percorso completo di lejos (per esempio C:\lejos_nxj) e confermate.
Cercate la variabile PATH modificatela aggiungendo alla fine della riga ; ed il percorso della cartella lejos\bin (C:\lejos_nxj\bin).

Aggiornamento del Firmware dell' NXT

Per utilizzare LeJOS sul Mindstorm è necessario sostituire il firmware originale con il firmware di LeJOS: questa operazione installerà una Java Virtual Machine sul brick. Collegate l'NXT al pc con il cavo USB, accendetelo e aspettate che venga riconosciuto dal computer.
Con una graffetta premete il pulsante di reset per almeno 4 secondi: l' NXT emetterà un lieve suono ed entrerà in modalità aggiornamento firmware. Aprite il prompt dei comandi (start » tutti i programmi » accessori » prompt dei comandi) e nella nuova finestra digitate nxjflash.

reset