Kategoriat
LEGO Projektit WeDo

Processing ja LEGO WeDo

Esimerkki HelloWeDo näyttää kuinka LEGO WeDo:n moottoria ja antureita voi ohjata Processing-ohjelmointikielellä. WeDo rakennussarjan avulla voi rakentaa ja ohjelmoida yksinkertaisia tietokoneeseen kytkettäviä LEGO-malleja. Se sisältää yli 150 osaa sisältäen moottorin, etäisyysanturin, liike- ja kallistusanturin sekä USB-aseman. Mahdollisuuksia siis on!

WeDo:n ohjelmointiin Windows- ja Mac-tietokoneissa voi käyttää myös MIT Media Labissa kehitettyä Web-selaimissa toimivaa avointa Scratch-ympäristöä.

HelloWeDoEsimerkkikoodi saa aikaan sen, että tietokoneen näytölle piirtyy muutama ympyrä (ellipse), neliö (rect) ja kaksi viivaa (line). Näiden kokoa, sijaintia ja väriä kontrolloidaan hiiren painikkeella ja WeDo:n liike-ja kallistusanturilla.

Toiminta on lyhyesti seuraava (ks. video). Kun hiiren kursori on ison punaisen ympyrän päällä painettuna, ympyrän väri muuttuu vihreäksi ja moottori käynnistyy liike- ja kallistusnanturilta tulevan tiedon mukaiseen suuntaan, eteen- tai taaksepäin.

Esimerkkiohjelmassa toiminta on jaettu pääohjelmaan (HelloWeDo.pde) ja laitteen käsittelyluokkaan WeDo (WeDo.pde). Valmiit lähdekoodit käännetyn WeDo-kirjaston kanssa on ladattavissa myös GITHubista.

HelloWedo.pde

Processing-ohjelmakoodissa pääohjelmalla on aina kaksi funktiota, setup() ja draw(). Funktio setup() suoritetaan ohjelman käynnistyksen yhteydessä. Tämän sisällä luomme uuden olion wedo luokasta WeDo. Funktio draw() on eräänlainen toistolauseke, joka toistaa itseään nopeudella 60 kertaa sekunnissa (oletusarvo). Kaikki piirtäminen (näytettävä) tehdään sen sisällä.

/**
 * HelloWeDo
 * An example using Lego WeDo in Processing.
 * by Tero Avellan
 */

WeDo wedo;
float angle = 0.5;
float radius = 125;
float speed = 0.5;

void setup() {
  size(500,500);
  wedo = new WeDo(this);
}

void draw() {
  background(color(255));
  fill(color(225,0,0));
  if (mousePressed && (dist(width/2, height/2, mouseX, mouseY)<125)) { 
       fill(color(0,255,60));
       if(wedo.getDirection(false) == "BACKWARD") { 
         speed = -0.5;
         wedo.setMotor(true,-50); 
       }
       else { 
         speed = 0.5;
         wedo.setMotor(true,50); 
       }
  } else {
    speed = 0;
    wedo.reset();
  }
  ellipse(width/2,height/2,250,250);
  
  float pallo_x = width/2 + cos(radians(angle))*(radius);
  float pallo_y = height/2 + sin(radians(angle))*(radius);
  
  if (angle<360) {
    angle = angle + speed;
  } else {
    angle = 0;
  }
  
  fill(0);
  strokeWeight(10);
  line(width/2,height/2,pallo_x, pallo_y);
  strokeWeight(3);
  ellipse(pallo_x, pallo_y, 30, 30);
  println(wedo.getTilt(false));
  strokeWeight(6);
  line(10,height-50,10+cos(radians(90-wedo.getTilt(false)))*50, (height-50)+sin(radians(270-wedo.getTilt(false)))*50);
  fill(255);
  rectMode(CENTER);
  rect(10+cos(radians(90-wedo.getTilt(false)))*50, (height-50)+sin(radians(270-wedo.getTilt(false)))*50, 30, 30);
}

Videossa näkyy liike- ja kallistusanturin vaikutus kuvassa.

WeDo.pde

WeDo:n USB-aseman, moottorin ja anturien toimintaa ohjaavan Java -kirjaston käyttöönotto (alustus) on tehty omaan luokkaansa. Näin USB-aseman käyttöönotto tapahtuu hallitusti kun luokasta luodaan uusi olio pääohjelman funktiossa setup(). Luokassa on jäsenfunktio (metodi) jokaista anturia ja moottoria varten. Anturimetodit palauttavat anturin arvon, kun taas moottorimetodilla säädetään moottorin nopeutta ja suuntaa.

/**
 * Example showing how to use Java library for LEGO WeDo
 * https://github.com/kjkoster/lego-wedo-java
 * Library requires HIDAPI (HID library for Java) and Guava (Google Core Libraries for Java).
 */

// HIDAPI
import com.codeminders.hidapi.*;

// Guava
import com.google.common.annotations.*;
import com.google.common.base.*;
import com.google.common.base.internal.*;
import com.google.common.cache.*;
import com.google.common.collect.*;
import com.google.common.escape.*;
import com.google.common.eventbus.*;
import com.google.common.hash.*;
import com.google.common.html.*;
import com.google.common.io.*;
import com.google.common.math.*;
import com.google.common.net.*;
import com.google.common.primitives.*;
import com.google.common.reflect.*;
import com.google.common.util.concurrent.*;
import com.google.common.xml.*;
import com.google.thirdparty.publicsuffix.*;

// WeDo
import org.kjkoster.wedo.usb.*;
import org.kjkoster.wedo.bricks.*;
import org.kjkoster.wedo.*;
import org.kjkoster.wedo.activities.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

class WeDo {
  
  PApplet parent;
  WeDoBricks weDoBricks = null;
  boolean verbose = false;

  // Constructor
  WeDo(PApplet p) {
    parent = p;
    try {
        final Usb usb = new Usb(verbose);
        weDoBricks = new WeDoBricks(usb, verbose);
    } catch (Exception e) { }
  }
  
  Map<Handle, Brick[]> listHubs() {
    final Map<Handle, Brick[]> hubs = weDoBricks.readAll();
    if(verbose) {
      if (hubs.size() == 0) {
          println("No LEGO WeDo hubs found.");
      } else {
          for (final Map.Entry<Handle, Brick[]> hub : hubs.entrySet()) {
              println(hub.getKey().getProductName());
          }
      }
    }
    return hubs;
  }
  
  float getDistance(final boolean isA) {
      final Map<Handle, Brick[]> hubs = listHubs();
      Brick brick;
      float sensorData = 0;
      for (final Map.Entry<Handle, Brick[]> hub : hubs.entrySet()) {
        if (isA) { brick = hub.getValue()[0]; }
        else { brick = hub.getValue()[1]; }
        switch (brick.getType()) {
          case DISTANCE:
            sensorData = brick.getDistance().getCm();
            break;
          case TILT:
          default:
            sensorData = 0;
        }
      }
      return sensorData;
  }
  
  void setMotor(final boolean isA,int speed) {
    if (isA) { weDoBricks.motorA(parseByte(speed)); }
    else { weDoBricks.motorB(parseByte(speed)); }
  }
  
  float getTilt(final boolean isA) {
    final Map<Handle, Brick[]> hubs = listHubs();
    Brick brick;
    float sensorData = 0;
    for (final Map.Entry<Handle, Brick[]> hub : hubs.entrySet()) {
      if (isA) { brick = hub.getValue()[0]; }
      else { brick = hub.getValue()[1]; }
      switch (brick.getType()) {
        case TILT:
          sensorData = brick.getTilt().getValue() & 0xff;
          break;
        case DISTANCE:
        default:
          sensorData = 0;
      }
    }
    return sensorData;
  }
  
  String getDirection(final boolean isA) {
    String sensorData = null;
    float tilt = getTilt(isA);
    if (tilt < 4) { sensorData = "NO_TILT"; } else if (tilt > 10 && tilt < 40) { sensorData = "BACKWARD"; } else if (tilt > 60 && tilt < 90) { sensorData = "RIGHT"; } else if (tilt > 117 && tilt < 140) { sensorData = "NO_TILT"; } else if (tilt > 151 && tilt < 190) { sensorData = "FORWARD"; } else if (tilt > 203 && tilt < 240) { sensorData = "LEFT"; }
    if(verbose) { println(sensorData); }
    return sensorData;
  }

  void reset() { weDoBricks.reset(); }
}

USB-laitteiden käyttöoikeudet (Linux)

Linuxissa on todennäköistä törmätä ongelmaan, jossa tietokone ei pysty lukemaan tietoja laitteelta. Lego WeDo käyttää HID (Human Interface Device) -protokollaa, jolloin se on rinnastettavissa laitteena esimerkiksi hiireen tai peliohjaimeen. Nämä laitteet eivät periaatteessa tarvitse koneessa mitään erikseen asennettavia ajureita toimiakseen, sillä ohjaus tapahtuu USB-standardissa määrättyjä protokollia käyttäen.

Omia ohjelmia tehtäessä ongelmaksi saattaa muodostua se, että vain root -käyttäjällä on oikeus näihin laitteisiin. Siksi joudumme tekemään uuden säännön muita käyttäjiä varten. Tätä varten luomme uuden tiedoston /etc/udev/rules.d/99-wedo.rules komennolla:

sudo nano /etc/udev/rules.d/99-wedo.rules

Tiedostoon tulee seuraava rivi:

ATTRS{idVendor}=="0694", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666"

Ladataan udev-säännöt uudelleen:

sudo udevadm trigger

Tämän jälkeen LEGO USB-asema tulee irroittaa ja liittää tietokoneeseen uudelleen.

Linkit

Kirjoittanut TeroA

Isä, insinööri ja opettaja, joka on erityisen kiinnostunut ihmisten ja teknologian välisestä vuorovaikutuksesta, kommunikoinnista ja visuaalisesta oppimisesta. Ikuinen optimisti ja haaveilija...