Affichage des articles dont le libellé est Processing. Afficher tous les articles
Affichage des articles dont le libellé est Processing. Afficher tous les articles

24/01/2018

Apprendre à coder, Par où commencer quand on n'y connait rien ?

Ce qui est sympa quand on apprend à coder, c'est de voir les changement rapidement.
Le logiciel avec lequel je me suis le plus éclaté, c'est Processing, conçu à la fois pour l'éducation et les performances artistiques...

J'ai commencé avec "Learning Processing" (et ses exemples) et "The Nature Of Code" écrits par Daniel Shiffman, qui sévi maintenant en vidéos :  http://shiffman.net/videos/ .

Le logiciel Arduino  pour ceux qui s'intéressent aux objets connectés ou à la robotique est également basé sur Processing (avec quelques exemples).
Il existe même une version Android pour ceux qui souhaiteraient pouvoir coder sur leur smartphone en attendant leur bus ( rigolez pas, ça m'est arrivé), il suffit de télécharger l'appli APDE sur le PlayStore.

Processing permet de créer des programmes sur votre ordi (Windows / Linux ou MAC), APDE des applis Android, mais celui qui me parait le plus intéressant actuellement c'est P5JS.

Vous pouvez soit télécharger le fichier ZIP (P5JS COMPLETE), et modifier l'exemple, soit utiliser directement l'éditeur en ligne  https://alpha.editor.p5js.org/.
 (on doit pouvoir enregistrer son travail en s'abonnant, sinon copiez votre code avec Ctrl+c et collez le dans un fichier texte sur votre ordi avec Ctrl+v ).
Pour retrouver facilement l'éditeur en ligne, recherchez "p5js online" sur Google .

Commencez par P5js online, (allez voir notamment les exemples dans 'Files/Example').

On va commencer par faire quelques tests, mais pour aller plus loin (image, video, gestion de plusieurs fichiers, utilisation de librairies, il faudra téléchargez 'P5JS Complete' pour travailler sur votre ordi.
On y reviendra, mais sous son allure très simple, P5js permet de faire des choses monstrueuses. Vous ne me croyez pas ? Allez donc jeter un oeil sur Open Processing, cliquez sur l'un des exemples, au hasard, son code est accessible en cliquant sur l'icône '</>' et pour executer le code, c'est sur le triangle 'play' (ça sera toujours le même que ce soit sur Processing, APDE, P5JS)...

Ce qui peut vous être utile, si vous bloquez, ou pour avoir des exemples d'utilisation des fonctions,  c'est d'aller voir les "REFERENCES" ou "EXEMPLES" de chacun des logiciels :

P5JS : REFERENCE & EXEMPLES
PROCESSING : REFERENCE & EXEMPLES
APDE : https://github.com/Calsign/APDE/wiki


Les librairies sont des extensions du logiciel.


Programmer , c'est quoi ? A la base, c'est écrire du code pour créer une application, automatiser une tâche, transformer des données, mais avec Processing et toute la série ( je parlerais de Processing de manière générale), ça va un peut plus loin, et on peut pousser le côté créatif / artistique assez facilement, enfin ça ça dépend de chacun... Fonctionnel / Visuel...


Pour une première approche, on va se concentrer sur l'éditeur online de P5js que vous pouvez ouvrir en cliquant ici  : https://alpha.editor.p5js.org/

Par défaut, dans chacun de ses logiciels, on trouve deux fonctions :
- une fonction 'setup' qui permet de mettre en place l'environnement
- un fonction 'draw' (ou 'loop' dans le logiciel Arduino) qui boucle à l'infini tant qu'on ne l'arrête pas expressément.

Quelques notions essentielles sont nécessaires :
- une variable est un espace de stockage en mémoire dans laquelle on va pouvoir stocker une chaine de caractères, un nombre, un objet;
- une fonction est une action que l'on va pouvoir appeler à plusieurs endroits sans être obligé de la réécrire.

Une fonction commence par le mot 'function' , suivi du nom de la fonction, suivi d'un couple de parenthèse, suivi d'un couple d'accolades. Les parenthèses seront utilisées pour envoyer des paramètres à la fonction, et les accolades contiendront le corps de la fonction, ce qu'elle fera réellement.


Bien maintenant, nous avons tout pour commencer.
En ouvrant l'editeur P5js online, vous pouvez cliquer sur le 'triangle PLAY' pour lancer le programme présent, et le carré 'STOP' pour arrêter.

function setup() { 
  createCanvas(400, 400);
} 

function draw() { 
  background(220);
}


En fait on a déjà, ici 4 fonctions : setup et draw, les deux fonctions que nous venons de voir et
- la fonction 'createCanvas(400,400);' qui créé notre espace de travail de 400 pixels de largeur par 400 pixels de hauteur. le premier pixel, de coordonnées (0,0) se situant en haut à gauche, et le dernier pixel, de coordonnées (400,400) si situant en bas à droite, et vers le milieu de notre espace de travail, on aura le pixel (200,200) jusque là, tout va bien ????

[vous pouvez remplacer les 400 par d'autres valeurs, pour modifier la taille de votre espace de travail.]

Ok, on continue...
Noter que chaque commande se termine par un point-virgule, vous risquez d'en oublier parfois, mais vous vous en rendrez vite compte : votre programme ne se lancera pas, et vous aurez un message d'erreur, dans la console (cadre gris, en bas à gauche) vous indiquant un truc du style :


Uncaught SyntaxError: missing ; after argument list (sketch: line 2)

la fonction 'backgound (220);'
est la seule action exécutée actuellement, et elle remplit le fond (et oui, background = fond, on est obligé de comprendre un peu l'anglais quand on veut programmer !)
On remplit ici le fond avec la couleur (220);

Pour les couleurs, une seule valeur, indique un niveau de gris : 0 = noir, 255 = blanc.
Pour un peu plus de fun, les couleurs peuvent aussi s'écrire comme un triplet de valeurs (rouge, vert, bleu) allant de 0 à 255.
ainsi la couleur (255, 0, 0) vous donnera du rouge.

Pour vérifiez, modifiez le code ainsi :

function setup() { 
  createCanvas(400, 400);
} 

function draw() { 
  background(255, 0, 0);
}

Votre fond devrait devenir rouge. Pour trouver vos couleurs favorites allez voir ici et noter les valeurs RVB de vos couleurs.

STROKE & FILL


Deux autres fonctions qui vous seront très utiles, et qui se placent avant les éléments que l'on veut dessiner:
- 'stroke()' détermina la couleur de votre trait,
- et 'fill()' la couleur de remplissage.

J'en ajoute deux tant qu'on est là, mais ces deux-ci ne devraient pas vous pposer de problèmes :
- rect() permet de dessiner un rectangle, et
- ellipse() permet de dessiner une ellipse.... Wahou, trop dur....

Vous pouvez tester le code suivant qui vous dessinera, sur fond rouge, un rectangle vert dont le contour est jaune et une ellipse jaune dont le contour est vert :

function setup() { 
  createCanvas(400, 400);
function draw() { 
  background(255, 0, 0);
stroke(255,255,0); 
fill(42,156,23);
rect(50, 50, 75, 100);
stroke(42,156,23);
fill(255,255,0); 
ellipse(250, 350, 75, 100);
}

Pour 'rect' et 'ellipse', on utilise 4 paramètres : les deux premiers, sont les coordonnées, l'endroit où l'on va dessiner les éléments, le troisième correspond à la largeur, et le quatrième à la hauteur.

On a déjà bien avancé, non ? Profitez-en pour faire une pause, vous pouvez ajouter des cercles, des rectangles et pourquoi pas un triangle ???

Vous pouvez aussi vous inspirer de ZOOG, le personnage de  "Learning Processing"



notez la notation des commenatires par la double barre oblique // et les commentaires sur plusieurs lignes entourés par /*......*/

// un commentaire sur une ligne
function setup() { 
/* ici 
un 
commentaire
sur
plusieurs
lignes */
  createCanvas(400, 400);
function draw() { 
  background(121, 248, 248);
ellipseMode(CENTER);
rectMode(CENTER); 
// Corps
stroke(0);
fill(247,255, 56);
rect(240, 145, 20, 100);
// Tête
fill(0, 255, 0);
ellipse(240, 115, 60, 60); 
// Yeux
fill(0); 
ellipse(221, 115, 16, 32); 
ellipse(259, 115, 16, 32);
// Jambes
stroke(0);
line(230, 195, 220, 205);
line(250, 195, 260, 205); 
}

Pas la peine que je vous explique à quoi servent les deux fonctions, je vous laisse découvrir par vous même leur utilité ou allez voir la "référence" :

ellipseMode(CENTER);  --> https://p5js.org/reference/#/p5/ellipseMode
rectMode(CENTER); --> https://p5js.org/reference/#/p5/rectMode

Interaction
Un peu plus de fun ? Ajoutons de l'interaction, en utilisant la position de la souris, elle est définie par deux variables prédéfinies : 'mouseX' et 'mouseY', et poussons le bouchon, en utilisant la position précédente de la souris 'pmouseX' et 'pmouseY', ajoutons une action lors du clic 

function setup() { 
  createCanvas(400, 400);
// La vitesse est réglée 30 images par seconde.
  frameRate(30);
function draw() { 
  background(121, 248, 248);
ellipseMode(CENTER);
rectMode(CENTER); 
// Corps
stroke(0);
fill(247,255, 56);
rect(mouseX, mouseY, 20, 100);
// Tête
fill(0, 255, 0);
ellipse(mouseX, mouseY-30, 60, 60);
// La couleur des yeux est déterminée en fonction de la position de la souris.
  fill(mouseX/2, 0, mouseY/2); 
  ellipse(mouseX-19, mouseY-30, 16, 32); 
  ellipse(mouseX+19, mouseY-30, 16, 32); 
// Jambes
stroke(0);
// Les jambes sont dessinées en fonction de la position de la souris et de sa position précédente.
  line(mouseX-10, mouseY+50, pmouseX-10, pmouseY+60);
  line(mouseX+10, mouseY+50, pmouseX+10, pmouseY+60);
}
function mousePressed() {
  console.log("Mets moi dans ta poche!");
}

référence : http://learningprocessing.com/examples/chp03/example-03-06-interactive-zoog


Les yeux de ZOOG changent de couleur en fonction de la position de la souris, les jambes semblent trainer, et quand on clique ( function moussePressed )  un message est écrit dans la console ( cadre gris en bas à gauche).


Jetez aussi un oeil sur cette page pour voir quelques interactions avec le clavier .

Notez au passage l'utilisation des conditions:
- si (condition) alors faire {...} sinon faire {...}

function setup(){

 createCanvas(480, 270);

}



function draw() {

  background(255);

  stroke(0);

  line(240, 0, 240, 360);

  line(0, 135, 640, 135);



  // Fill a black color

  noStroke();

  fill(0);



  // Depending on the mouse location, a different rectangle is displayed.    

  if (mouseX < 240 && mouseY < 135) {

    rect(0, 0, 240, 135);

  } else if (mouseX > 240 && mouseY < 135) {

    rect(240, 0, 240, 135);

  } else if (mouseX < 240 && mouseY > 135) {

    rect(0, 135, 240, 135);

  } else if (mouseX > 240 && mouseY > 135) {

    rect(240, 135, 240, 135);

  }

}


et des boucles :
Pour 'i' de 0 à la moitié de la largeur (width/2) , avec un pas de 10, dessiner un rectangle commencant à la position (i,10), de largeur 3 et de hauteur 200+i.

function setup(){

createCanvas(500,500);

}



function draw(){

fill(255,115,143);



for(var i=0; i<width/2 ; i+=10){

    rect(i, 10, 3, 200+i);

  }

}

[ 'width' correspond à la largeur de l'environnement de travail, ou canvas, 'height' correspond à la hauteur ]

On a encore plein de trucs à voir comme les Objets/ Classes, les tableaux d'objets, ou comment utiliser des librairies externes, mais vous devriez maintenant être armé pour aller triturer
quelques démos sur Open Processing ou modifier le code du jeu basé sur  slither.io

En cas de doute, consultez la REFERENCE P5JS





11/12/2017

Code slither.io pour P5js

Un petit bout de code pour p5.js,  inspiré de slither.io, pour les CM1 /CM2 de l'école la Rédemption qui apprennent à coder.
Allez voir le tutoriel, pour comprendre la logique de ce code et pouvoir l'adapter selon vos envies .

code à copier dans l'interface p5js editor :

  1. sélectionnez tout le code ci-dessous
  2. Ctrl+C pour copier
  3. rendez-vous ensuite sur l'éditeur  p5js editor : 
  4. Ctrl+V pour coller
  5. puis lancez en cliquant sur le triangle.
  6. vous pouvez ensuite modifier ce code pour adapter le jeu selon vos envies, n'oubliez pas le site p5.js pour quelques explications sur les fonctions utilisées : P5JS / Référence
       
var serpent;
var positions = [];
var nourriture;
//var mechantSerpent; //pour créer un adversaire ?

function setup() {
  fullscreen();
  createCanvas(800, 600);
  serpent = new Serpent();
//  mechantSerpent = new Serpent();
  nourriture = new Nourriture();
  positions[positions.length] = {mouseX,mouseY};
  textSize(32);
}

function draw() {
  background(0);
  tick();
  serpent.move();
  serpent.display();
  nourriture.display();
  nourriture.croque();
  fill(0, 102, 153);
  text( serpent.corps.boules.length-1, width-100, 30);
}

// serpent
function Serpent() {
  this.x = random(width);
  this.y = random(height);
  //  positions[0].x = this.x;
  //  positions[0].y = this.y;
  this.diameter = random(10, 30);
  this.speed = 1;
 this.corps = new Corps();
 var tete = new Boule(mouseX, mouseY,  color(random(0,255),random(0,255),random(0,255)));
 this.corps.boules[0] = tete;
 console.log(this.corps);

  this.move = function() {
    var boules = this.corps.boules;
  for (var i = 0; i < boules.length; i++) {
    var b = boules[i];
     if (i == 0){
          b.x = positions[0].x;
        b.y = positions[0].y;
          if (positions.length > 1 ){
            positions.shift();
          }
   }else{
        var precedente = boules[i-1];
        var d = int(dist(b.x, b.y, precedente.x, precedente.y));
        if(d > 10){
          if(b.x - precedente.x > 0){
            b.x = (3*b.x+ precedente.x)/4 ; // b.x-1-(d/20);
          }else if (b.x - precedente.x < -0) {
            b.x = (3*b.x+precedente.x)/4; //b.x+1+(d/20);
          }
          if (b.y - precedente.y > 0){
            b.y = (3*b.y + precedente.y)/4; //b.y-1-(d/20);
          }else if(b.y - precedente.y < -0){
            b.y = (3*b.y+precedente.y)/4; //b.y+1+(d/20);
          }
        }
   }
  }
  };

  this.display = function() {
    var boules = this.corps.boules;
  for (var i = 0; i < boules.length; i++) {
      var b = boules[i];
        fill(b.color);
        ellipse(b.x, b.y, this.diameter, this.diameter);
      }
      fill(boules[0].color);
      strokeWeight(1);
      stroke(color(0));
      ellipse(boules[0].x, boules[0].y, this.diameter*1.2, this.diameter*1.2);
      ellipse(boules[0].x, boules[0].y+this.diameter/4, this.diameter/3, this.diameter/4);
   }
}


function Corps(){
 this.boules = [];
}

function Boule(_x,_y, _color){
 this.x = _x;
 this.y = _y;
  this.color = _color;
}

function tick(){
  var longueurPos = 40;
  if (positions.length > longueurPos ){
    positions.shift();
  }
  if ((positions[positions.length-1].x != mouseX)&& (positions[positions.length-1].y != mouseY)){
    var pos = {};
    pos.x = mouseX;
    pos.y = mouseY;
    positions[positions.length] = pos;
  }else{
    if (positions.length > 1 ){
      positions.shift();
    }
  }
}

function Nourriture(){
  this.x = random (10, width-10);
  this.y = random (10, height-10);
  this.color =  color(random(0,255),random(0,255),random(0,255));

  this.display = function(){
    stroke(color(random(0,255),random(0,255),random(0,255)));
    strokeWeight(1);
    fill(this.color);
    rect(this.x, this.y, 5,5);
    noStroke();
  }

   this.croque = function(){
    var serpentTete = serpent.corps.boules[0];
    var d = int(dist(this.x, this.y, serpentTete.x, serpentTete.y));
    if (d < 10 ){
      var nouveau = new Boule(mouseX, mouseY, this.color);
      var longueur = serpent.corps.boules.length;
      serpent.corps.boules[longueur] = nouveau;
      nourriture = new Nourriture();
    }
  }
}

22/07/2016

03/04/2016

Convertir sqlite, xls ou ods en RDF

L'un des buts du graph http://rdf-smag0.rhcloud.com/smag.html est d'interconnecter les bases de données disparates.

Une amie m'indique qu'elle est en train de monter une base de données sur les robots... intéressant, c'est exactement ce qu'il me faut pour mon projet Smag0, et la gestion des ressources, des objets connectes...
Voyons comment l’intégrer au graph RDF :
1 : qui va mettre à jour cette base ? Cette amie ? les constructeurs ? les utilisateurs ???
-> intégrer les données au serveur Fuseki, ou un lien vers la source de données mise à jour ailleurs ?
2 : la source actuelle est un fichier robot.db
 --> comment la transformer au format RDF ? on a deux tables robots et devices
--> j'avais dejà fait des tests pour transformer XLS et ODS en RDF , donc la première idée est d'extraire les deux tables en csv, de les convertir en ods, puis d'adapter l'outil que j'avais déjà utiliser pour repasser au format rdf... --> pas simple
--> autre option a étudier : sql.js https://github.com/kripken/sql.js 


outil d'extraction ODS vers RDF basé sur Processing 2 ( pas testé avec 3) avec :
[Une librairie permettant de gérer les fichiers ODS : https://incubator.apache.org/odftoolkit/downloads.html]
La librairie Jena Apache

26/02/2016

Dreamcatcher V3 integration dans Meteor

in English, at the bottom of this post ;-)

La version hébergée sur Météor n'est plus, préférez la version "Autonome Collaboratif "







Nous en sommes maintenant à la V3 de Dreamcatcher : les plus curieux peuvent déjà y jeter un oeil : http://smag0.meteor.com/

  1.  v1 : http://smag-smag0.rhcloud.com/DreamCatcher/
    1. explications : http://smag0.blogspot.fr/2015/08/dreamcatcher-lattrape-reves.html
    2. Fonctionnalités :
      1. test de la bibliothèque de la bibliothèque traer pour processingjs pour gérer un environnement physique de répulsion, attraction des noeuds, deplacement à la souris, zoom avec les flèches (voir notice/help)
      2. test de drag&drop
      3. test d'integration, export de fichier rdf , n3
      4. sauvegarde de graphe avec touche "s" chargement des données depuis un serveur Fuseki, un sparql endpoint, via une requete sparl ou un service web hebergé ailleurs avec Java/Jena
      5. Creation de sessions pour travailler à plusieurs sur un graphe
    3. utilisationhttp://smag-smag0.rhcloud.com/DreamCatcher/help.html
  2. v2 : http://smag-smag0.rhcloud.com/DreamCatcher1/
    1. explications : http://smag0.blogspot.fr/2015/12/dreamcatcher-ou-comment-partager-des.html
    2. Fonctionnalités : une evolution de la V1, avec prise en charge de la 3D, grâce à P5JS, test égalment avec three.js 
    3. utilisation
      1. cliquer sur "charger Demo" puis sur afficher texte
      2. les curseurs permettent de zoomer, déplacer la caméra
  3. v3 ; http://smag0.meteor.com/
    1. explications : http://smag0.blogspot.fr/2016/02/dreamcatcher-v3-integration-dans-meteor.html
    2. besoin; besoin d'une identification, authentification pour partager, donner des droits sur les graphes, les sessions
    3. dépôt github : https://github.com/scenaristeur/dreamcatcher-smag0
    4. utilisation
      1. en arrivant sur la page d'accueil, on peut s'authentifier, ou créer son compte, 
      2. ensuite on peut créer un post ou participer aux post existants
        1. creer un post : rien de plus simple (attention aux lien, s'il existe déjà, ça empeche de créer le post)
        2. ajouter un triplet ou statement à un post existant, en insérant un sujet, une propriete, et un objet, et en validant. ce triplet/statement devrait s'insérer dans le graphe en haut de page . si aucun graphe n'apparait : revenez sur la page d'accueil, puis actualisez, ( tous les fichiers ne semblent pas encore chargés si on atterit directement sur un post)



version anglaise : 

Dreamcatcher V3 integration in Meteor
We are now in V3 Dreamcatcher: the curious can already have a look: http://smag0.meteor.com/

 v1: http://smag-smag0.rhcloud.com/DreamCatcher/
explanations http://smag0.blogspot.fr/2015/08/dreamcatcher-lattrape-reves.html
features:
test library for the library traer processing.js to manage a physical environment repulsion, attraction knots, displacement mouse, zoom with the mouse (see manual / help)
drag & drop test
integration testing, rdf file export, n3
Backup graph with "s" key data loading from a Fuseki server, endpoint sparql, via a request sparl or a hosted web service elsewhere with Java / Jena
Creation of sessions to work together on a graph
Use: http://smag-smag0.rhcloud.com/DreamCatcher/help.html
v2: http://smag-smag0.rhcloud.com/DreamCatcher1/
explanations http://smag0.blogspot.fr/2015/12/dreamcatcher-ou-comment-partager-des.html
Features: an evolution of the V1, with support of 3D, thanks to P5JS, égalment test Three.js
use :
click "Load Demo" and then display text
sliders allow you to zoom, move the camera
v3; http://smag0.meteor.com/
explanations http://smag0.blogspot.fr/2016/02/dreamcatcher-v3-integration-dans-meteor.html
need; require identification, authentication to share, give rights on graphs, sessions
use :
arriving on the home page, you can authenticate or create an account,
then you can create a post or participate in existing post
create a post: nothing simpler (watch link, if it exists, it prevents creating the post)
add a triplet or statement to an existing post, inserting a subject, a property and an object, and validating. this triplet / statement should be part of the graph to the top. if no graph will appear: return to the home page and then refresh, (all files do not yet appear directly responsible if it lands on a post)

06/12/2015

Concours Developpeur Sphero Js java Mathematique Machine Learning

[Article en cours de rédaction]
Pour être informé de la date officielle d'ouverture du concours, devenir partenaire du concours, proposer un prochain concours ou toute autre suggestion, envoyer nous un mail , ou un twit @DFaveris .
<bouton inscription concours >(prochainement)
Smag0 organise prochainement un concours ouvert aux développeurs, mathématiciens, adeptes du machine learning, bidouilleurs, hackers, inventeurs, makers... dans l'objectif de dénicher ses futurs talents.
Le premier concours Smag0 a pour objectif d'améliorer et de finaliser le code existant d'un projet. Vous pouvez former des équipes ou développer en solo.
Historique :
Smag0 est un système complexe dont la finalité et de construire " un robot qui range ma chambre", ou au moins de faire avancer la recherche dans ce sens.
Dans ce contexte et avec tous les gadgets et objets connectés qui arrivent sur le marché, il parait judicieux pour plusieurs raisons, de décomposer l'objectif : ne pas developper "un robot qui range ma chambre", mais plusieurs modules, robots, objets connectés, qui collaboreraient, échangeraient des informations, et contribueraient à faciliter le rangement d'une chambre ou toute autre tâche domestique.
Smag0 est donc conçu en ce sens, comme un système multi-agents. Ces agents autonomes partagent des informations sur leur environnement, ou sur les habitudes des utilisateurs, et agissant sur cet environnement.
Le Projet :
Ces robots, objets connectés, modules robotiques, agents, doivent pour pouvoir agir au mieux sur leur environnement, posséder un minimum de connaissance sur celui-ci.
Sphero-Carto est un sous-projet de Smag0, un module dont le but est d'établir la cartographie d'un environnement, d'une maison, d'une pièce et de rendre cette cartographie accessible aux autres robots et objets connectés.
Le Principe :
Pour établir la cartographie, le principe qui a été choisi est de faire évoluer un robot dans l'environnement, de récupérer ses coordonnées et les points de collision.
Outils & Matériel :
Pour sa simplicité d'utilisation (un jeu d'enfant), la facilité avec laquelle on peut se le procurer <lien vers comment se procurer Sphero>, nous avons pris l'option d'utiliser le robot Sphero d'Orbotix.
Mais il ne faut pas oublier que les principes essentiels du projet Smag0 sont la modularité et l'evolutivité. Vous pouvez donc très bien utiliser un autre robot, objet connecté du marché, ou que vous fabriquerez.
Objectif :
Établir la cartographie d'une pièce comme celle-ci <exemple de carte>
En faisant évoluer un/des robot(s) dans notre pièce, on récupère ses coordonnées et les points de collision dessinant ainsi les meubles ou obstacles rencontrés.
En enregistrant la carte d'une pièce "rangée" comme référence, on peut comparer la carte actuelle et déduire l'emplacement des objets qu'il faut ranger et envoyer d'autres modules / robots pour les ranger.
Le Code Existant :
Pour le moment, le projet Sphero-Carto à suivi deux pistes, mais d'autres possibilités s'offrent à vous <lien vers les différentes API orbotix> :
Une interface Android < lien vers le code> et une interface web (nodejs, Sphero.js, p5js, socket).
Le code actuel est très "pourri" c'est un premier jet, un code pour tester le concept, mais il remplit les fonctionnalités suivantes (que vous pourrez certainement ameliorer ) :
- proposer une interface pour calibrer, contrôler le robot en manuel et le faire passer en mode automatique pour qu'il parcours l'environnement tout seul.
- proposer une interface pour visualiser le parcours, définir des zones repérées (meubles, obstacles, portes...)
- récupérer le parcours du robot et détecter les points de collision, l'alignement des points de collision dessinant un obstacle.
Vous pouvez proposer :
- des améliorations du code existant ( optimisation, correction... )
- un algorithme de correction d'erreurs, des paramétrages (vitesse, seuil...), des fonctions qui permettront d'optimiser le résultat ---> c'est notre GROS PROBLEME À RESOUDRE (voir le paragraphe "gros problème à résoudre", plus bas dans cet article).
- d'autres solutions avec d'autres robots, d'autres interfaces, comme une arduino avec des capteurs infrarouges ou un drone avec une caméra...
- toute amélioration sur la procédure de cartographie comme par exemple : poser Sphero dans un angle, déterminer manuellement dans un premier temps les limites de l'environnement, calculer la rotation induite par une collision, proposer une autre idée pour le stockage du parcours, et le partagé des informations ( rdf, rdfstream, BigData, cloud...XML,JSON), utiliser plusieurs Sphero, surveillance par un autre robot muni d'un caméra...
Technique :
Sphero se connecté en bluetooth
Les Api Sphero proposent des fonctions ( datastream, locator, collision) pour récupérer, après calibrage, les coordonnées du robot. Ces coordonnées sont calculées en fonction de la distance parcourue dans une direction, et peuvent être definies dans un repère orthonormé < image du repère >.
Sphero détecté les collisions et peut également envoyer leur position dans ce repère.
Grâce à ses fonctionnalités, on peut récupérer les informations primordiales des positions (x,y,collision), sous forme de liste indiquant pour chaque point(x,y) si le robot à rencontré un obstacle ou non.
(0,0,false)
(5,0,false)
(10,3,false)
(15,9,true)
(15,5,false)...
En récupérant les informations du Sphero, on peut même détecter de quel côté de la boule était la collision, avec quelle force cette collision a eu lieu, et qu'elle était la vitesse de Sphero à ce moment  --> API sphero àofficielle
Pour l'instant, nous utilisons l'API officielle mais il existe d'autres possibilités spheron.js, cyclon.js...
LE GROS PROBLEME À RESOUDRE :
Lors de l'utilisation de Sphero-Carto avec le code présenté ci dessus ( Android et web), un problème se pose.
Lors des collisions, la boule Sphero semble affectée par le choc et l'on peut noter deux sources d'erreurs pour le résultat final :
- la boule perd le nord et subit une déviation
- la boule qui rencontre un mur continue d'envoyer des positions différentes, comme si elle traversait l'obstacle.
<insérer des screenshoots>
Votre boulot, si vous l'acceptez est de corriger les coordonnées envoyées par Sphero par tout moyen (évaluation d'erreurs, machine learning, parcours optimisé...)
Le Gagnant :
Le gagnant sera celui/celle (équipe ou solo) qui pourra proposer un code offrant la cartographie partageable la plus proche de la réalité.
Votre application, interface, service devrait être d'une utilisation simple, pour un coût faible.
Votre solution devra fournir code et indications (en français ou en anglais) pour se procurer les outils nécessaires, pré-requis, materiel,... devra être open,  accessible au plus grand nombre, et permettre à chacun de reproduire votre solution.
[Article en cours de rédaction]

11/11/2015

Sphero-Carto

Le but de cet article est de proposer une utilisation du robot Sphero d'Orbotix pour établir la cartographie d'un environnement.
le code est disponible là : https://drive.google.com/file/d/0B0zEK4yLB5C6TUhLc0FRbVVKMVU/view?usp=sharing

En effet dans le projet Smag0, et d'après les trucs que j'ai pu lire un peu partout (SMA Ferber...), un robot/agent autonome se doit d'avoir une représentation de son environnement. J'avais donc envisagé il y a longtemps, d'utiliser le robot Sphero afin d'établir une cartographie de l'environnement en le faisant se balader dans l'environnement, en récupérant ses coordonnées, et en notant les points de collision. Cela pourrait permettre, par exemple à déterminer si une pièce est rangée et s'il faut envoyer d'autres robots autonomes pour la ranger.

Exemple d'application au projet Smag0 : Sphero établit une cartographie un jour où la chambre est rangée et la stocke comme étant la cartographie de référence avec points de collision pour les pieds de meubles, et autres objets. Lorsque les jours suivants, Sphero repasse, on compare les deux cartographies, si les points de collision se sont déplacés : les objets ont bougé, et il faut envisager de les ranger, ou déterminer si leur nouvelle position est respectable.

Après cette brève introduction, passons au fourneau :


LA RECETTE DU SPHERO-CARTO.
Ingrédients :
- 1 script sphero.js permettant d'accéder au fonctionnalités du robot Sphéro : https://github.com/orbotix/sphero.js  (les fonctionnalités sont là : https://github.com/orbotix/sphero.js/blob/master/lib/devices/sphero.js )
- 1 serveur node.js
- quelques workers JS...


"C'est dans les vieux pots qu'on fait la meilleure soupe"
Pensons maintenant notre appli comme un système multi-agents, comme des programmes autonomes qui communiquent entre eux, nous aurons par exemple :
- un module (ou worker en javascript) qui s'occupera du serveur de fichier,
- un autre de la gestion des commandes du robot sphero,
- un autre de l'interface visuelle,
- un autre s'occupera de la conception de la carte,
- on pourra en trouver un qui se chargera du stockage de l'information,
- un déterminera l'algorithme à utiliser pour une meilleure exploration de l'environnement...
- ... (on pourra en ajouter d'autre selon le besoin et les fonctionnalités envisagées)


Le serveur de fichier : 
Commençons par installer nodejs si ce n'est pas encore fait, puis installez le module sphero : "$ npm install sphero" comme préconisé ici https://github.com/orbotix/sphero.js

le module socket.io et le module keypress "npm install socket.io",  `npm install keypress`

Ensuite, je me suis basé sur le tutoriel du nouveau site P5.js pour l'interaction entre le serveur node.js et le module d'affichage p5.js : http://p5js.org/tutorials/ ou https://github.com/processing/p5.js/wiki/p5.js,-node.js,-socket.io

Choix a faire : Socket ? worker ?

code  de base du serveur serveur.js , à lancer avec la commande : node serveur.js


// http://smag0.blogspot.fr/2015/11/sphero-carto.html
// HTTP Portion
var http = require('http');
// URL module
var url = require('url');
var path = require('path');
// Using the filesystem module
var fs = require('fs');
// make sure you install this first - `npm install keypress`
var keypress = require("keypress");
var server = http.createServer(handleRequest);
server.listen(8080);
console.log('Server started on port 8080');
//SPHERO
var sphero = require("sphero");
var orb = sphero("COM3");
var stop = orb.roll.bind(orb, 0, 0),
  roll = orb.roll.bind(orb, 150);
var detectionCollisionInterval; //interval pour la detection de collision
var collisionBool=0;
var xSphero,ySphero=0;
var direction=0;
function handleRequest(req, res) {
  // What did we request?
  var pathname = req.url;
  // If blank let's ask for index.html
  if (pathname == '/') {
    pathname = '/index.html';
  }
  // Ok what's our file extension
  var ext = path.extname(pathname);
  // Map extension to file type
  var typeExt = {
    '.html': 'text/html',
    '.js': 'text/javascript',
    '.css': 'text/css'
  };
  // What is it?  Default to plain text
  var contentType = typeExt[ext] || 'text/plain';
  // User file system module
  fs.readFile(__dirname + pathname,
    // Callback function for reading
    function(err, data) {
      // if there is an error
      if (err) {
        res.writeHead(500);
        return res.end('Error loading ' + pathname);
      }
      // Otherwise, send the data, the contents of the file
      res.writeHead(200, {
        'Content-Type': contentType
      });
      res.end(data);
    }
  );
}
// WebSocket Portion
// WebSockets work with the HTTP server
var io = require('socket.io').listen(server);
var socketA;
// Register a callback function to run when we have an individual connection
// This is run for each individual user that connects
io.sockets.on('connection',
  // We are given a websocket object in our function
  function(socket) {
    console.log("We have a new client: " + socket.id);
socketA=socket;
    // When this user emits, client side: socket.emit('otherevent',some data);
    socket.on('mouse',
      function(data) {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'mouse' " + data.x + " " + data.y);
        // Send it to all other clients
        socket.broadcast.emit('mouse', data);
        // This is a way to send to everyone including sender
        // io.sockets.emit('message', "this goes to everyone");
      }
    );
    socket.on('color',
      function(data) {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'color' " + data.r + " " + data.g + " " + data.b);
        orb.color({
          red: data.r,
          green: data.g,
          blue: data.b
        });
        // Send it to all other clients
        // socket.broadcast.emit('mouse', data);
        // This is a way to send to everyone including sender
        // io.sockets.emit('message', "this goes to everyone");
      }
    );
    socket.on('start',
      function() {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'start' ");
        start();
      }
    );
    socket.on('stop',
      function() {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'stop' ");
        clearInterval(detectionCollisionInterval);
        orb.roll(0, 0);
      }
    );
    socket.on('startCalib',
      function() {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'startCalib' ");
        console.log("::START CALIBRATION::");
        orb.startCalibration();
      }
    );
    socket.on('stopCalib',
      function() {
        // Data comes in as whatever was sent, including objects
        console.log("Received: 'stopCalib' ");
        initialiseLocator();
        console.log("::FINISH CALIBRATION::");
        orb.finishCalibration();
      }
    );
    socket.on('disconnect', function() {
      console.log("Client has disconnected");
    });
  }
);
////////////////////////////////
// SPHERO
////////////////////////////////
orb.connect(function() {
  listen();
  orb.color("green");
  //option configurelocator
  initialiseLocator();
  orb.getBluetoothInfo(function(err, data) {
    console.log("bluetooth info fetched");
    if (err) {
      console.error("err:", err);
    }
    console.log("data:", data);
  });
   orb.detectCollisions();
  // orb.streamVelocity();
  // orb.streamGyroscope();
  var opts = {
    n: 200,
    m: 1,
    mask1: 0x00000000,
    pcnt: 0,
    mask2: 0x0D800000
  };
  orb.setDataStreaming(opts);
  orb.on("velocity", function(data) {
    console.log("::STREAMING VELOCITY::");
    console.log("  data:", data);
  });
  orb.on("dataStreaming", function(data) {
    //console.log("streaming data packet recieved");
 //  console.log("  data:", data);
 xSphero=data.xOdometer.value;
 ySphero=data.yOdometer.value*-1;
 collisionBool=0;
    console.log(data.xOdometer.value+" "+data.yOdometer.value);
 var dataToSend = {
    x: xSphero,
    y: ySphero,
z: collisionBool // 0 car pas de collision
  };
 socketEmit("position", dataToSend);
  });
  orb.on("gyroscope", function(data) {
    console.log("::STREAMING GYROSCOPE::");
    console.log("  data:", data);
  });
  orb.on("collision", function(data) {
    console.log("collision detected");
   // console.log("  data:", data);
    orb.color("red");
    collisionBool=1;
 var dataToSend = {
    x: xSphero,
    y: ySphero,
z: collisionBool // 0 car pas de collision
  };
 socketEmit("position", dataToSend);
    setTimeout(function() {
      orb.color("green");
   collisionBool=0;
    }, 1000);
  });
});
function socketEmit(entete , data){
if(socketA!=null){
socketA.emit(entete, data);
}
};
function start() {/*
orb.roll(200, direction);
//Determinons les premieres limites
//limite haute
while(collisionBool==0){
setInterval(function() {
orb.roll(200, direction);
}, 2000);
}
direction=direction*-1; //demi-tour
//limite basse
while(collisionBool==0){
setInterval(function() {
orb.roll(200, direction);
}, 2000); 
}
*/
if(collisionBool==0){
detectionCollisionInterval =setInterval(function() {
   //direction = Math.floor(Math.random() * 360);
    orb.roll(150, direction);
  }, 2000);
}else{
console.log("collision ne peut avancer");
direction = Math.floor(Math.random() * 360);
}
  // roll orb in a random direction, changing direction every second
 /* detectionCollisionInterval = setInterval(function() {
    var direction = Math.floor(Math.random() * 360);
    orb.roll(100, direction);
    //  readLocator()
  }, 2000);*/
};
function initialiseLocator() {
  var opts = {
    flags: 0x01,
    x: 0x0000,
    y: 0x0000,
    yawTare: 0x0
  };
  orb.configureLocator(opts, function(err, data) {
    console.log(err || "CONFIGURE LOCATOR data: " + data);
  });
  orb.setHeading(0, function(err, data) {
      console.log(err || "Heading réglé sur 0: " + data);
    });
direction=0;
};
function readLocator() {
  orb.readLocator(function(err, data) {
    if (err) {
      console.log("error: ", err);
    } else {
      console.log("dataLocator:");
      console.log("  xpos:", data.xpos);
      console.log("  ypos:", data.ypos);
      console.log("  xvel:", data.xvel);
      console.log("  yvel:", data.yvel);
      console.log("  sog:", data.sog);
      console.log(data);
      console.log(data[0]);
      console.log("");
    }
  });
};
function handle(ch, key) {
  var stop = orb.roll.bind(orb, 0, 0),
    roll = orb.roll.bind(orb, 60);
  if (key.ctrl && key.name === "c") {
    process.stdin.pause();
    process.exit();
  }
  if (key.name === "e") {
    orb.startCalibration();
  }
  if (key.name === "q") {
    orb.finishCalibration();
  }
  if (key.name === "up") {
    roll(0);
  }
  if (key.name === "down") {
    roll(180);
  }
  if (key.name === "left") {
    roll(270);
  }
  if (key.name === "right") {
    roll(90);
  }
  if (key.name === "space") {
    stop();
    clearInterval(detectionCollisionInterval);
  }
}
function listen() {
  keypress(process.stdin);
  process.stdin.on("keypress", handle);
  console.log("starting to listen for arrow key presses");
  process.stdin.setRawMode(true);
  process.stdin.resume();
}


fichier sketch.js

//les exemples pour SPHERO : https://github.com/orbotix/sphero.js/blob/master/examples/
//https://github.com/orbotix/sphero.js

/*
 * @name Slider
 * @description You will need to include the 
 * <a href="http://p5js.org/reference/#/libraries/p5.dom">p5.dom library</a>
 * for this example to work in your own project.<br><br>
 * Move the sliders to control the R, G, B values of the background.
 */
var rSlider, gSlider, bSlider,speedSlider;
var myDiv0;
var rougeDiv;
var vertDiv;
var bleuDiv;
// Keep track of our socket connection
var socket;
var xSphero, ySphero;
var positionsSphero = [];
var positionSphero;

function setup() {
  // create canvas
  createCanvas(windowWidth, windowHeight);
  background(0);
  positionSphero = createVector(0, 0, 0);
  // Start a socket connection to the server
  // Some day we would run this server somewhere else
  socket = io.connect('http://localhost:8080');
  // We make a named event called 'mouse' and write an
  // anonymous callback function
  socket.on('mouse',
    // When we receive data
    function(data) {
      console.log("Got: " + data.x + " " + data.y);
      // Draw a blue circle
      fill(0, 0, 255);
      noStroke();
      ellipse(data.x, data.y, 80, 80);
    }
  );

  socket.on('position',
    // When we receive data
    function(data) {
      console.log("Position: " + data.x + " " + data.y + " " + data.z);
      // Draw a blue circle
      // fill(100, 100, 255);
      // noStroke();
      // ellipse(data.x, data.y, 10, 10);
      xSphero = data.x;
      ySphero = data.y;
      collision = data.z;
      positionSphero = createVector(xSphero, ySphero, collision);
      append(positionsSphero, positionSphero);
    }
  );

  textSize(15)
  noStroke();

  // create sliders
  rSlider = createSlider(0, 255, 255);
  rSlider.position(720, 20);
  gSlider = createSlider(0, 255, 230);
  gSlider.position(720, 50);
  bSlider = createSlider(0, 255, 107);
  bSlider.position(720, 80);
  speedSlider= createSlider(0, 255, 66);
  speedSlider.position(720, 110);

  //boutons
  button = createButton('start Calibration');
  button.position(20, 120);
  button.mousePressed(startCalib);
  button = createButton('stop Calibration');
  button.position(150, 120);
  button.mousePressed(stopCalib);
  button = createButton('start');
  button.position(20, 150);
  button.mousePressed(start);
  button = createButton('stop');
  button.position(100, 150);
  button.mousePressed(stop);
    button = createButton('clear');
  button.position(180, 150);
  button.mousePressed(clear);

  //affiche
  rougeDiv = document.getElementById("rouge");
  vertDiv = document.getElementById("vert");
  bleuDiv = document.getElementById("bleu");
  xDiv = document.getElementById("positionX");
  yDiv = document.getElementById("positionY");
}

function draw() {
  var r = rSlider.value();
  var g = gSlider.value();
  var b = bSlider.value();
  var speed=speedSlider.value();
  background(r, g, b);
  text("red", 800, 20);
  text("green", 800, 50);
  text("blue", 800, 80);
   text("speed", 800, 110);
   
  // console.log(r, g, b);
  if ((r != rougeDiv.innerHTML) || (g != vertDiv.innerHTML) || (b != bleuDiv.innerHTML)) {
    rougeDiv.innerHTML = r;
    vertDiv.innerHTML = g;
    bleuDiv.innerHTML = b;
    sendRGB(r, g, b);
  }
  xDiv.innerHTML = xSphero;
  yDiv.innerHTML = ySphero;
  translate(width / 2, height / 2);
  fill(0);
  ellipse(xSphero, ySphero, 10, 10);
  for (i in positionsSphero) {
    var position = positionsSphero[i];
    if (position.z == 0) {
      fill(0, 255, 0);
    } else {
      fill(255, 0, 0);
    }
    ellipse(position.x, position.y, 3, 3);
  }
}


function start() {
  console.log("start p5");
  socket.emit('start', null);
}

function stop() {
  console.log("stop p5");
  socket.emit('stop', null);
}

function clear() {
  console.log("clear");
  positionsSphero = [];
  positionSphero = createVector(0, 0, 0);
 // socket.emit('clear', null);
}

function startCalib() {
  console.log("start calib");
  socket.emit('startCalib', null);
}

function stopCalib() {
  console.log("stop calib");
  socket.emit('stopCalib', null);
}


/*
function mouseDragged() {
  // Draw some white circles
  fill(255);
  noStroke();
  ellipse(mouseX, mouseY, 80, 80);
  // Send the mouse coordinates
  sendmouse(mouseX, mouseY);
}*/
//ENVOI de la couleur
function sendRGB(r, g, b) {
  // Make a little object with rgb 
  var data = {
    r: r,
    g: g,
    b: b
  };
  // Send that object to the socket
  socket.emit('color', data);
}
// Function for sending to the socket
function sendmouse(xpos, ypos) {
  // We are sending!
  console.log("sendmouse: " + xpos + " " + ypos);

  // Make a little object with  and y
  var data = {
    x: xpos,
    y: ypos
  };

  // Send that object to the socket
  socket.emit('mouse', data);
}



fichier index.html

 <!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Sphero</title>
    <script src="libraries/p5.js" type="text/javascript"></script>

    <script src="libraries/p5.dom.js" type="text/javascript"></script>
    <script src="libraries/p5.sound.js" type="text/javascript"></script>

    <style> body {padding: 0; margin: 0;} canvas {vertical-align: top;} </style>

   <!-- <script language="javascript" type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.2.9/p5.min.js"></script>-->
    <script language="javascript" type="text/javascript" src="sketch.js"></script>
    <script language="javascript" type="text/javascript" src="/socket.io/socket.io.js"></script>
  </head>
  <body>
  <!-- <div id="connected" style="width:100px;height:100px">not connected</div> -->
  <div id="rouge">r</div>
  <div id="vert">b</div>
  <div id="bleu">v</div>
  <div id="positionX">X</div>
   <div id="positionY">Y</div>
  </body>
</html>

le code est disponible là : https://drive.google.com/file/d/0B0zEK4yLB5C6TUhLc0FRbVVKMVU/view?usp=sharing


Pour la gestion des webworkers avec nodejs : npm install webworker