Raspberry PI + Servo

Ansteuerung eines Standard Servos mit dem Raspberry PI 3.

Für die spätere Verwendung auf einem Roboter soll der Servo in +/- Grad mit Bezug zur Mittelstellung gesteuert werden.

Weiterhin soll für den Servo, da die beiden Hardware PWMs schon für einen anderen Zweck verwendet werden, der Software PWM zur Anwendung kommen.

Auf die Funktionsweise eines Servos gehe ich hier nicht ein, dies kann bei Bedarf bei http://rn-wissen.de/wiki/index.php/Servos oder anderen Websites nachgelsen werden.


Hardware

PI3 (oder älter bitte auf die richtigen Pins achten)
Standard-Servo
Diverse Kabel ( Vorgefertigte Pin-Header Kabel)


Aufbau

GPIOs

  • Ansteuerung des Servos GPIO 27

(Die GPIOs gelten nur in Verbindung mit pi4j und wirePi)

 

Plan

 


Software

Entwicklungsumgebung

Als Entwicklungsumgebung verwende ich BlueJ auf dem PI selbst. Hiermit habe ich die folgenden Klassen erstellt.


Code

Erste Testklasse

Hier die erste Testklasse welche alle Positionen des Servos mal abfährt.

233-2

import com.pi4j.wiringpi.SoftPwm;

public class FirstTest
{
    static int max = 27;
    static int min = 4;
    static int pin = 27;

    static void setMin(int min){
        FirstTest.min = min;
    }

    static void setMax(int max){
        FirstTest.max = max;
    }

    public static void main(String[] args) throws InterruptedException {

        // initialize wiringPi library
        com.pi4j.wiringpi.Gpio.wiringPiSetup();

        // create soft-pwm pins (min=0 ; max=100)
        SoftPwm.softPwmCreate(pin, 0, 200);

        int j = 0;
        // continuous loop
        while (j < 10) {
            // fade LED to fully ON
            for (int i = min; i <= max; i++) {
                SoftPwm.softPwmWrite(pin, i);
                Thread.sleep(100);
            }

            // fade LED to fully OFF
            for (int i = max; i >= min; i--) {
                SoftPwm.softPwmWrite(pin, i);
                Thread.sleep(100);
            }
            j++;
        }

        // make sure to stop software PWM driver/thread if you done with it.
        //
        SoftPwm.softPwmStop(1);
    }
}

Servo-Klasse

Klasse zur Steuerung des Servos. Die zwei wichtigsten Methoden sind:

  • setPos – Setzen der Position, Wertebereich von posMin bis posMax wobei (posMax-posMin+1)  die Mittelstellung ist.
  • setDeg – Diese Methode projeziert die eingegebende Gradzahl auf die möglichen positionen (sehr ungenau 🙁 )

229-2

import com.pi4j.wiringpi.SoftPwm;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.RaspiPin;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioPin;

/**
 * Control class for servo motors
 * @author Stefan Fambach (With help of many others)
 * @version 0.1
 * 
 * This class can either set position by values between posMin and posMax
 * or it can caluculate the position by an angle where degMin <= angle <= degMax
 * 
 * No warranty free for use see GPL v3.
 */
public class Servo
{
    Pin pin;

    int degMin = -90;
    int degMax = 90;

    int posMin = 4; // to have a center
    int posMax = 25;
    int pos = -1;

    /**
     * range between min and max degree
     */
    double degDelta = degMax - degMin;

    /**
     * range between min and max position
     */
    double posDelta = posMax - posMin;

    /**
     * multiplier for degree to pos calculation
     */
    double multiplier = posDelta/degDelta;

    /**
     * Time in ms to wait after new position is set
     */
    int timeout = 1000;

    /**
     * Default constructor, default pin is GPIO_27
     */
    public Servo(){
        this(RaspiPin.GPIO_27);
    }

    /**
     * Constructor for objects of class Servo
     */
    public Servo(Pin controlPin) 
    {
        this.pin = controlPin;
        // initialize wiringPi library
        com.pi4j.wiringpi.Gpio.wiringPiSetup();

        // create soft-pwm pins (min=0 ; max=100)
        SoftPwm.softPwmCreate(this.pin.getAddress(), 0, 200);

        moveCenter();

    }

    /** 
     * set position in degree, only 20 positions are possible, each step will be calculated
     */
    boolean setDeg(int deg) throws Exception {
        if(deg > degMax){
            throw new Exception( "Entered deg: "+ deg + "is greater maxDeg "+degMax);
        }

        if(deg < degMin){
            throw new Exception( "Entered deg: "+ deg + "is smaller maxDeg "+degMin);            
        }

        if(deg < 0){
            deg = (-degMin)+ deg;
        }else{
            deg -= degMin;
        }

        return setPos((int) (deg * multiplier));
    }

    /** 
     * set position posMin - posMax (default 0-21)
     */
    public boolean setPos(int pos)throws Exception{
        if(pos < 0 ){
            throw new Exception (pos + " Pos to small (<0) ");
        }

        if(pos > posMax ){
            throw new Exception (pos + " Pos to big > "+posMax+")");
        }
        if(pos != this.pos){
            this.pos = pos;
            SoftPwm.softPwmWrite(pin.getAddress(), posMin + pos);
            sleep(timeout);
            SoftPwm.softPwmWrite(pin.getAddress(), 0);
            return true;
        }
        return false;
    }

    /** 
     * set Servo to center position
     */
    public void moveCenter(){
        try{
            setPos((posMax-posMin+1)/2);
        } catch (Exception ex){
            // will hopefully never happen
        }
    }

    /** 
     * set Servo to min position
     */
    public void moveToMinPos(){
        try{
            setPos(0);
        } catch (Exception ex){
            // will hopefully never happen
        }
    }

    /** 
     * set Servo to max position
     */
    public void moveToMaxPos(){
        try{
            setPos(posMax-posMin);
        } catch (Exception ex){
            // will hopefully never happen
        }
    }

    public int getPos(){
        return this.pos;
    }

    public int getDegMin(){
        return degMin;
    }

    public int getDegMax(){
        return degMax;
    }

    public void setDegMin(int min){
        this.degMin = min;
        calculateVariables();
    }

    public void getDegMax(int max){
        this.degMax = max;
        calculateVariables();
    }

    public void setPosMin(int min){
        this.posMin = min;
        calculateVariables();
    }

    public void setPosMax(int max){
        this.posMax = max;
        calculateVariables();
    }

    public int getPosMin(){
        return  this.posMin;
    }

    public int getPosMax(){
        return  this.posMax;
    }

    /**
     * calculate the ranges for deg and pos 
     * as well as the multiplier for degree positioning of the servo
     */
    private void calculateVariables(){
        degDelta = degMax - degMin;
        posDelta = posMax - posMin;
        multiplier = posDelta/degDelta;
    }

    private void sleep(int msec){
        try
        {
            Thread.sleep(msec);
        }
        catch ( InterruptedException e)
        {
        }
    }

    public void release(){
        SoftPwm.softPwmStop(pin.getAddress());
        GpioController gpio = GpioFactory.getInstance();
        gpio.unprovisionPin(new GpioPin[]{gpio.getProvisionedPin(pin)});


    }
}

Test-Klasse

Klasse mit Main-Methode zum Testen des Servos. In der Main-Methode werden alle Positionen abgefahren wobei die Positionierung über den Winkel relativ zur 0 Position erfolgt.

231-2

import com.pi4j.io.gpio.RaspiPin;


public class TestServo
{

    public static void main(String args[]){

        // Create the range sensor
        Servo servo = new Servo(RaspiPin.GPIO_27); 

        int j = 0;

        do {
            System.out.println("***** Start degree test from left to right *****");     

            int curPos = servo.getDegMin()-1;
            // from left to right
            for(int i = servo.getDegMin(); i < servo.getDegMax(); i++){
                try{
                    servo.setDeg(i);
                }catch(Exception e){
                    System.out.println(e.getMessage());
                }
                int cpos = servo.getPos();
                if(cpos > curPos){
                    curPos = cpos;
                    System.out.print("\n "+curPos+" : ");
                }

                System.out.print(i+" , ");

            }

            System.out.println("***** Start degree test from right to left *****");  
            curPos = servo.getDegMax() + 1;
            // from left to right
            for(int i = servo.getDegMax(); i > servo.getDegMin(); i--){
                try{
                    servo.setDeg(i);
                }catch(Exception e){
                    System.out.println(e.getMessage());
                }
                int cpos = servo.getPos();
                if(cpos < curPos){
                    curPos = cpos;
                    System.out.print("\n "+curPos+" : ");
                }
                System.out.print(i+" , ");

            }
            j++;
        } while (j < 5);

        servo.release();
    }
}

Tips

Das Positionieren über die PI4j Lib ist sehr ungenau für die 180° Bewegungspielraum des Servos stehen gerade mal rund 20 (+-2) Positionen zur Verfügung. Das sind rund 9° pro Position.  Evtl. sollte man vorher testen welche Positionen für den Aktuellen Zweck am besten geeignet sind. Sollte der genaue Winkel wichtig sein, ist es besser diesen vorab zu messen als ihn über die setDeg Methode berechnen zu lassen.

Es gibt noch weitere Lib’s (Servoblast, … ) die evtl genauer sind, evtl müsste man auch überlegen die Programmiersprache zu wechseln und dann den Ensprechenden Code über das Nativ-Interface anzusprechen bzw. irgendwie anders 😉 .


Einen anderen Weg gehen …

Ich habe versucht den Software PWM innerhalb eine Threads laufen zu lassen, jedoch ohne Erfolg. Der Servo weigert sich zu funktionieren.  Nach diversen Tests mit unterschiedlichen Frequenz-Einstellungen habe ich mich entschlossen ServoBlaster od.  besser die PI variante (PiBits) zu verwenden. Die Installation gestaltet sich einfach wie folgt.

258-2

# holen der sourcen
wget https://github.com/richardghirst/PiBits/zipball/master

#umbenennen
mv master pibits.zip

#entpacken
unzip pibits.zip

#bauen
cd richardghirst-PiBits-96014c8/ServoBlaster/user
sudo make install 

#ändern des timeout von 2000 auf 500#
sudo nano /etc/init.d/servoblaster 

#eine reboot ist nicht unbedingt erforderlich 
sudo reboot

Jetzt muss der ServoBlaster nur noch über Java angesprochen werden. Hier ein einfaches Beispiel zum testen.

… kommt noch


Tips für den neuen Weg

Wenn die exec Funktion von Java nix macht ist hier ein guter Link zum Bugfixing.

http://www.javaworld.com/article/2071275/core-java/when-runtime-exec—won-t.html

Weiterhin könnte es helfen nicht die Println Methode zu verwenden, da zumindest mein PI den Abschluss der Zeile nicht richtig interpretieren konnte. (Beispiel „5=100\n)“ sollte funktionieren).

 

Quellen

http://razzpisampler.oreilly.com/ch05.html
https://github.com/Pi4J/pi4j/blob/master/pi4j-example/src/main/java/MaestroServoExample.java
https://www.bluej.org/raspberrypi/ServoMotor.html
https://phyks.me/2015/05/controlling-servomotors-on-a-raspberry-pi.html
http://rn-wissen.de/wiki/index.php/Servos
https://github.com/richardghirst/PiBits/tree/master/ServoBlaster

Raspberry Pi Servo Motor Steuerung

Between 0 and 1 – PWM with Raspberry Pi

 


Anhang

Pin-Belegung

 

Pi4J + BlueJ

Da ich am liebsten im Java programmiere, verwende ich die Bibliothek Pi4J zur Ansteuerung der GPIOs am Raspberry Pi 2 und 3.

Auf die Installation der Bibliothek werde ich nicht näher eingehen, da dies auf den folgenden Webseiten schon erschöpfend beschrieben wurde.

http://pi4j.com/install.html
http://pi4j.com/utility/pi4j.html
http://www.forum-raspberrypi.de/Thread-tutorial-ansteuerung-der-gpio-ports-mit-pi4j

Auch wichtig zu wissen ist, dass Pi4J auf der wiringPI Bibliothek aufbaut und diese natürlich mit installiert werden muss.

Als Entwicklungsumgebung verwende ich BlueJ, weil man hier auch gleich noch ein Klassendiagramm mit bekommt.

Also Doku gibts genug, warum dann dieser Beitrag?

Ich möchte nach und nach eine kleine FaQ erstellen mit den Problemen die ich während der Nutzung dieser beiden Tools hatte, habe und haben werde.

Die Ports machen nix

In der alten Version vom BlueJ, scheint in den Einstellungen das Flag zur Auführung als Root nicht richtig initialisiert zu sein. Nach dem Start von BlueJ einfach das Flag entfernen und wieder rein machen dann gehts. Dieses Problem scheint in der aktuellen Version behoben zu sein.

Sollte es dann immer noch nicht klappen einfach mal die IOs auf der Console testen. Hilfe gibts hier oder hier.

Soweit ich weiss muss man zur Ansteuerung der GPIOs root (od. sudo) rechte besitzen. Der Benutzer pi ist ensprechend gut ausgestattet und eignet sich am Besten für erste Tests. (Man soll sich ja nicht mehr als root anmelden ;( ).

Nach dem Upgrade geht nix mehr

Ich habe meinen Raspberry auf die neuste Version aktualisiert. Hierbei wurde auch der Kernel auf die Version 4.9.32 aktualisiert. Danach gingen die GPIOs nicht mehr. Ein Downgrade des Kernels auf die Version 4.4.50 hat das Problem erstmal gelöst. Hilfe hierzu gibt es hier und hier.

Software PWM beim Servo

Für ca. 180° stehen beim Software PWM leider nur 20 Stellungen/Schritte für den Servo zur Verfügung was ca. 9° pro Schritt entspricht.
Weiterhin gibt es durch andere Funktionen im Betriebssystem oft gewackel am Servo (jitter). Man sollte ihn so programmieren, dass die entsprechende Stellung angefahren wird und dann den PWM wieder ausschalten.

Eine bessere Möglichkeit ist natürlich einen Hardware PWM zu verwenden, wenn man noch einen frei hat ;).

Andere Bibliotheken die besser funktionieren sollen habe ich noch nicht getestet hier eine kleine Auflistung, diese müssten dann gegebenen Falls mit dem Java Native Interface angesteuert werden:

Weiteres zu PWM [1] [2] [3]

Weiter Probleme werden sicherlich folgen ….

Raspberry PI 3 + Motorsteuerung

Ziel ist es mit dem Raspberry PI 3 unter Zuhilfenahme einer Motorbrücke, 2 Gleichstrommotoren zu betreiben.

Dies soll später genutzt werden um einen zweirädrigen Roboter anzutreiben.


Hardware

PI3 (oder älter bitte auf die richtigen Pins achten)
Motorbrücke L9110s
2x DC Motor
Diverse Kabel ( Vorgefertigte Pin-Header Kabel)


Testaufbau

GPIOs am Raspberry

  • Motor1 Richtung GPIO 26
  • Motor1 Geschwindigkeit GPIO 1
  • Motor2 Richtung GPIO 24
  • Motor3 Geschwindigkeit GPIO 23

(Die GPIOs gelten nur in Verbindung mit pi4j und wirePi)

Plan


Software

Entwicklungsumgebung

Als Entwicklungsumgebung verwende ich BlueJ auf dem PI selbst. Hiermit habe ich vier Klassen erstellt. Diese werden im folgenden Abschnitt kurz erklärt.


Code

Da ich anfänglich ein paar Probleme hatte und die Motorbrücke nicht wie erwartet reagierte, habe ich mich langsam an das Thema herangetastet.

Digitale Ansteuerung

Zuerst habe ich eine kleine Testklasse geschrieben, welche die Motoren digital ansteuert. Hiermit konnte ich die richtige Verkabelung sicherstellen. Mit diesem Test ist noch keine Geschwindigkeitsregulierung möglich.

198-2

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.GpioPin;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class MotorPinTest
{

    public static void main (String args[]){
       GpioController gpio = GpioFactory.getInstance();
        
        
       final GpioPinDigitalOutput pin1 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, "MyLED", PinState.LOW);
       final GpioPinDigitalOutput pin2 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_26, "MyLED", PinState.LOW);
       
       final GpioPinDigitalOutput pin3 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_23, "MyLED", PinState.LOW);
       final GpioPinDigitalOutput pin4 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_24, "MyLED", PinState.LOW);
       
       
       // play 
       
       int i = 0;
       while (i < 10){
           // forward
           pin1.high();
           pin2.low();
           pin3.high();
           pin4.low();           
           sleep(2000);
           
           // stop
           pin1.low();
           pin2.low();
           pin3.low();
           pin4.low();
           sleep(2000);  
           
           // backward
           pin1.low();
           pin2.high();
           pin3.low();
           pin4.high();

           sleep(2000);           
           
           // stop
           pin1.low();
           pin2.low();
           pin3.low();
           pin4.low();
           
           i++;
           sleep(2000);
        }
        gpio.shutdown();
        gpio.unprovisionPin(new GpioPin[]{pin1,pin2,pin3,pin4});
    }
    
    public static void sleep(long s){
        try{
            Thread.sleep(s);
        } catch (Exception e) {
            // do nothing shit happends
        }
    }
}

PWM Ansteuerung

Als nächstes habe ich eine Testklasse erstellt, die den Hardware PWM benutzt und ebenfalls die Richtungen wechselt.

206-2

import com.pi4j.io.gpio.*;
import com.pi4j.util.CommandArgumentParser;
import com.pi4j.util.Console;

public class MotorPwmTest
{
    public static void main (String args[]){
        GpioController gpio = GpioFactory.getInstance();

        GpioPinPwmOutput pwm1 = gpio.provisionPwmOutputPin(RaspiPin.GPIO_01);
        // argument array to search in

        //GpioPinPwmOutput pwm2 = gpio.provisionPwmOutputPin(RaspiPin.GPIO_26);
        final GpioPinDigitalOutput pin2 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_26, "MyLED", PinState.LOW);

        GpioPinPwmOutput pwm3 = gpio.provisionPwmOutputPin(RaspiPin.GPIO_23);
        final GpioPinDigitalOutput pin4 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_24, "MyLED", PinState.LOW);

        com.pi4j.wiringpi.Gpio.pwmSetMode(com.pi4j.wiringpi.Gpio.PWM_MODE_MS);
        com.pi4j.wiringpi.Gpio.pwmSetRange(1000);
        com.pi4j.wiringpi.Gpio.pwmSetClock(500);

        // play 
        int j = 0;
        while (j < 10){
            // forward
            pin2.low();
            pin4.low();

            for(int i = 0; i < 1000; i++){
                pwm1.setPwm(i);
                pwm3.setPwm(i);
                sleep(10);
            }
            sleep(2000);

            // stop
            pwm1.setPwm(0);
            pwm3.setPwm(0);
            pin2.low();
            pin4.low();
            sleep(2000);

            // backward
            pin2.high();
            pin4.high();
            for(int i = 0; i < 1000; i++){
                pwm1.setPwm(1000-i);
                pwm3.setPwm(1000-i);
                sleep(10);
            }

            // stop
            pwm1.setPwm(0);
            pwm3.setPwm(0);
            pin2.low();
            pin4.low();
            sleep(2000);

            // turn left
            pin2.low();
            pin4.high();
            for(int i = 0; i < 1000; i++){
                pwm1.setPwm(i);
                pwm3.setPwm(1000-i);
                sleep(10);
            }
            
            // stop
            pwm1.setPwm(0);
            pwm3.setPwm(0);
            pin2.low();
            pin4.low();
            sleep(2000);
            
            // turn right
            pin2.high();
            pin4.low();
            for(int i = 0; i < 1000; i++){
                pwm1.setPwm(1000-i);
                pwm3.setPwm(i);
                sleep(10);
            }
            sleep(2000);           

            // stop
            pwm1.setPwm(0);
            pin2.low();
            pwm3.setPwm(0);
            pin4.low();
            j++;
            sleep(2000);
        }
        gpio.shutdown();
        gpio.unprovisionPin(new GpioPin[]{pwm1,pin2,pwm3,pin4});
    }

    public static void sleep(long s){
        try{
            Thread.sleep(s);
        } catch (Exception e) {
            // do nothing shit happends
        }
    }
}

Funktionalität in Klasse gepackt

Als letzten Schritt habe ich eine Klasse Drive zur Steuerung beider Motoren und die dazu gehörende Testklasse erstellt.

Drive Klasse

Klasse zur Steuerung der zwei Motoren.

208-2

import com.pi4j.io.gpio.*;
public class Drive
{
    public static final int STOP = 0;
    private int pwmMax = 1000;

    private GpioPinPwmOutput leftPwm;
    private GpioPinPwmOutput rightPwm;

    private GpioPinDigitalOutput leftDir;
    private GpioPinDigitalOutput rightDir;

    private int speedMin = 200; // with smaller values the engines are not spinning
    private int speedMax = pwmMax-speedMin;
    
    public int getSpeedMax(){
        return speedMax;
    }

    public Drive (Pin leftPwmPin, Pin leftDirPin, Pin rightPwmPin, Pin rightDirPin){
        GpioController gpio = GpioFactory.getInstance();
        
        leftPwm = gpio.provisionPwmOutputPin(leftPwmPin);
        rightPwm = gpio.provisionPwmOutputPin(rightPwmPin);

        com.pi4j.wiringpi.Gpio.pwmSetMode(com.pi4j.wiringpi.Gpio.PWM_MODE_MS);
        com.pi4j.wiringpi.Gpio.pwmSetRange(pwmMax);
        com.pi4j.wiringpi.Gpio.pwmSetClock(500);

        leftDir =  gpio.provisionDigitalOutputPin(leftDirPin, "Left Direction", PinState.LOW);
        rightDir =  gpio.provisionDigitalOutputPin(rightDirPin, "Right Direction", PinState.LOW);

    }
    public void setMinSpeed(int speed) throws Exception{
        if(speed <= STOP){
            throw new Exception("Min Speed to small!");
        }else if (speed > pwmMax){
            throw new Exception("Min Speed to big!");
        }

        speedMin = speed;
        speedMax = pwmMax-speedMin; 
    }

    public void turnLeft(int speed)throws Exception{
        int half = speed/2;// turn speed should be half of the speed 
        leftBackward(half);
        rightForward(half);

    }

    public void turnRight(int speed)throws Exception{
        int half = speed/2; // turn speed should be half of the speed
        leftForward(half);
        rightBackward(half);

    }

    public void backward(int speed)throws Exception{
        leftBackward(speed);
        rightBackward(speed);

    }

    public void forward(int speed)throws Exception{
        leftForward(speed);
        rightForward(speed);

    }

    public void leftForward(int speed)throws Exception{
        stopLeft();
        checkSpeed(speed);
        leftDir.low();
        leftPwm.setPwm(speed+speedMin);
    }

    public void rightForward(int speed)throws Exception{
        stopRight();
        checkSpeed(speed);
        rightDir.low();
        rightPwm.setPwm(speed+speedMin);
    }

    public void leftBackward(int speed)throws Exception{
        stopLeft();
        checkSpeed(speed);
        leftDir.high();
        leftPwm.setPwm(pwmMax- speed-speedMin);

    }

    public void rightBackward(int speed)throws Exception{
        stopRight();
        checkSpeed(speed);
        rightDir.high();
        rightPwm.setPwm(pwmMax-speed-speedMin);

    }

    public void stopLeft()throws Exception{
        leftPwm.setPwm(STOP);
        leftDir.low();
    }

    public void stopRight()throws Exception{
        rightPwm.setPwm(STOP);
        rightDir.low();
    }

    public void stop()throws Exception{
        stopLeft();
        stopRight();
    }

    public void release(){
        GpioController gpio = GpioFactory.getInstance();
        gpio.unprovisionPin(new GpioPin[]{leftPwm,rightPwm,leftDir,rightDir});
    }

    public boolean checkSpeed(int speed) throws Exception{
        if(speed> speedMax){
            throw new Exception("Speed value to high! " + speed +" >"+ speedMax);
        } else if(speed < STOP){
            throw new Exception("Speed value to low!");            
        }
        return true;
    }

}

Test-Drive Klasse

Klasse zum Testen der Motoren. Diese testet, wie auch im vorhergehenden Beispiel,  unterschiedliche Geschwindigkeiten und Richtungen.

210-2

import com.pi4j.io.gpio.*;
public class TestDrive
{
    public static void main (String args[]){
        Drive d = new Drive(com.pi4j.io.gpio.RaspiPin.GPIO_01,RaspiPin.GPIO_26,RaspiPin.GPIO_23,RaspiPin.GPIO_24);
        try{
            // play 
            int j = 0;
            while (j < 10){

                // forward
                System.out.print("forward: ");
                for(int i = 0; i < d.getSpeedMax(); i++){
                    d.forward(i);
                    sleep(10);
                }
                sleep(2000);
                System.out.println("...");
                // stop
                System.out.println("Stop");
                d.stop();
                sleep(2000);

                // backward
                System.out.println("backward");
                for(int i = 0; i < d.getSpeedMax(); i++){
                    d.backward(i);
                    sleep(10);
                }

                // stop
                System.out.println("Stop");
                d.stop();
                sleep(2000);

                // turn left
                System.out.println("turn left");
                for(int i = 0; i < d.getSpeedMax(); i++){
                    d.turnLeft(i);
                    sleep(10);
                }

                // stop
                System.out.println("Stop");
                d.stop();
                sleep(2000);

                // turn right
                System.out.println("turn right");
                for(int i = 0; i < d.getSpeedMax(); i++){
                    d.turnRight(i);
                    sleep(10);
                }

                sleep(2000);           

                // stop
                System.out.println("Stop");
                d.stop();
                j++;
                sleep(2000);
            }
        } catch(Exception e){
            // nothing to do wrong speed value
            System.out.println(e.getMessage());
        } finally {
            GpioController gpio = GpioFactory.getInstance();
            gpio.shutdown();
            d.release();
        }
    }

    public static void sleep(long s){
        try{
            Thread.sleep(s);
        } catch (Exception e) {
            // do nothing shit happends
        }
    }
}

 


Tips

Motoren drehen nicht in die richtige Richtung

Sollten die Motoren nicht in die richtige Richtung drehen, einfach die polarität am Anschluss an der Motorbücke vertauchen bis es passt. Sollte das nicht möglich sein, die Methoden forwardLeft/Right() und backwardLeft/Right() entsprechend anpassen.

Raspberry rebooted bei Aktivierung/Betrieb der Motoren

Sollte beim Einschalten/Betrieb der Motoren der Raspberry neu booten, kann dies am hohen Stromverbrauch der Motoren liegen. Dann sollte man überlegen die Motoren entweder über eine zusätzliche Spannungsquelle zu versorgen oder zumindest die Motoren mit einer Spule zu entstören. Condensatoren können zusätzlich Spannungspitzen abfangen.

Mehr zur Entstörung:

http://www.bnhof.de/~ho1645/entstoer.htm
https://www.mikrocontroller.net/articles/Motoransteuerung_mit_PWM

Schaltung mit zusätzlicher Spannungsquelle:

Hier wird der L9110 mit einem LiPo versorgt, wichtig ist es die Masse des Lipo und der des Raspberries zu verbinden, sonst funktioniert es nicht. Der Lipo sollte im Bereich 3.7v und < 12V liegen (Beschränkung des L9910).

 


Quellen

Beispiel auf GitHub
PI4j Webseite
PWM + Motoren allgemein beim RoboterNetz

Einige andere Webseiten waren auch noch nützlich, einfach mal Google bemühen.


Anhang

Pin-Belegung

Motorbrücke L9110s

Hier ein paar Informationen über die Motorbrücke L9110s. Ich habe ein Modul mit zwei dieser Treiber hier im Einsatz. Ein Hauptvorteil dieses Moduls ist der geringe Preis. Ein Nachteil ist die geringe Stromabgabe die ihn für größere Motoren unbrauchbar macht.

Zur Steuerung der Motoren sind jeweils zwei Pins pro Motor nötig.

Laut dateblatt ist die Beschaltung wie folgt:

Vorwärts:

Pin1 – PWM-Signal für die Geschwindigkeit
Pin2 – Auf Masse

Rückwärts:

Pin1 – Auf Massse
Pin2 – PWM-Signal für die Geschwindigkeit

! Die Beschaltung wie bei Anderen Motorbrücken z.B. L293 .. :

Pin1 – gibt die Geschwindigkeit mit Hilfe einen PWM Signals vor
Pin2 – gibt die Richtung mit digital High/Low vor.

Hat mit diesem Modul nicht funktioniert.

Es gibt jedoch einen Trick um trotzdem nur 2 PWM Pins verwenden zu können:

Vorwärts:

Pin1 – PWM-Signal für die Geschwindigkeit
Pin2 – Auf Masse

Rückwärts:

Pin1 – PWM-Max – PWM-Signal für die Geschwindigkeit
Pin2 – Auf VCC.

PWM-Max ist hierbei der maximale Wert bei dem Der PWM über die komplette Periodendauer auf High ist. Beispiel PWM von 0-255. dann ist PWM-Max = 255.

Technische Daten

BeschreibungWert
Anz. Motor2
TreibershipL9110s
VCCmin2,5V
VCCmax12V
Imax0,8A
Größe2,8 cm x 2,1 cm
Preis~ 0,60€

Beschaltung des Modules

Beschaltung aus dem Datenblatt

Vorwärts

A-1A – PWM signal gibt die Geschwindigkeit an
A-1B – auf Masse

B1A – PWM signal gibt die Geschwindigkeit an
B1B- auf Masse

Rückwärts

A-1A – auf Masse
A-1B – PWM signal gibt die Geschwindigkeit an

B1A – auf Masse
B1B- PWM signal gibt die Geschwindigkeit an

Links? drehen

A-1A – PWM signal gibt die Geschwindigkeit an
A-1B – auf Masse

B1A – auf Masse
B1B- PWM signal gibt die Geschwindigkeit an

Rechts? Drehen

A-1A – auf Masse
A-1B – PWM signal gibt die Geschwindigkeit an

B1A – PWM signal gibt die Geschwindigkeit an
B1B – auf Masse

Mit nur 2 PWMs

PWM-Max = 1000

Vorwärts

A-1A – PWM Signal gibt die Geschwindigkeit an
A-1B – auf Masse

B1A – PWM Signal gibt die Geschwindigkeit an
B1B- auf Masse

Rückwärts

A-1A – 1000 – PWM Signal gibt die Geschwindigkeit an
A-1B – auf Vcc

B1A – 1000 PWM Signal gibt die Geschwindigkeit an
B1B- auf Vcc

Links? drehen

A-1A – PWM Signal gibt die Geschwindigkeit an
A-1B – auf Masse

B1A – 1000 – PWM Signal gibt die Geschwindigkeit an
B1B – auf Vcc

Rechts? Drehen

A-1A – 1000 – PWM Signal gibt die Geschwindigkeit an
A-1B – auf Vcc

B1A – PWM Signal gibt die Geschwindigkeit an
B1B – auf Masse

Quellen

Datenblatt l9110
Datenblatt Platine

Anhang

Mit dem Raspberry PI 3 den Entfernungsmesser US-100 ansteuern

Ansteuerung eines Ultraschall-Entfernungsmessers US-100 mit dem Raspberry PI3 ( oder früher). Ich habe nur Tutorials für den SR-04 gefunden, dieser wird allerdings analog zum US-100 eingebunden.


Hardware

PI3 (oder älter bitte auf die richtigen Pins achten)
US-100 ( Ultraschallsensor)
Brot-Board (zum leichteren Aufbau)
Diverse Kabel ( Vorgefertigte Pin-Header Kabel)


Aufbau

Der US-100 arbeitet mit 3.3V womit man ihn direkt am Raspberry betreiben kann.

Verdrahtung

  • Trigger Pin am GPIO 28
  • Echo Pin an den GPIO 25

(Die GPIOs gelten nur in Verbindung mit pi4j und wirePi)

 


Software

Entwicklungsumgebung

Als Entwicklungsumgebung verwende ich BlueJ auf dem PI selbst. Hiermit habe ich zwei Klassen erstellt.


Code

Distance-Klasse

Klasse zur Steuerung des US-015, mit den Methoden

  • getDistanceMM – Entfernung in Millimetern
  • getDistanceCM – Entfernung in Zentimetern

157-2

import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPinDigitalOutput;

public class Distance
{
    // instance variables - replace the example below with your own

    
    
    public static final int     DIVISOR               = 58200;
    public static final int     ERROR_NO_MEASUREMENT  = -1;
    public static final int     ERROR_DIST_TO_SHORT   = -2;
    
    private GpioPinDigitalInput     echo        = null;
    private GpioPinDigitalOutput    trigger     = null;
    private long                     timeOut     = 250000000; // in ns/ time for 4m for 20°C ~232
    private long                     triggerTime = 2;   // 10ms for SR04, 2 for US-100
    private long                     initTime    = 2;    // default 2 ms
      
    /**
     * Constructor for objects of class Distance
     */
    public Distance(GpioPinDigitalOutput trigger, GpioPinDigitalInput echo)
    {
        this.echo = echo;
        this.trigger = trigger;

    }
    
    /**
     * get the distance in mm
     */
    public double getDistMM()
    {
        return aquire()*1000;
    }
    
     /**
     * get the distance in cm
     */
    public double getDistCM()
    {
        return aquire();
    }
    
    /** 
     * returns the time in milli xeconds
     */
    public double aquire(){
        long start = 0;
        double diff = 0;

        try {
           
          trigger.low();
          Thread.sleep(initTime);
          trigger.high();
          Thread.sleep(triggerTime);
          trigger.low();
    
          while (echo.isLow()) {
            start = System.nanoTime();
          }
    
          while (echo.isHigh()) {
              //System.out.print((System.nanoTime() - start)+",");
              if((System.nanoTime() - start) > timeOut){
                  return ERROR_NO_MEASUREMENT;
              }
          }
    
          // check if distance is to short and return errror
          diff =  ((System.nanoTime() - start) / DIVISOR );
          return diff;
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        return -1.0;
    }
}

Test-Klasse

Klasse mit Main-Methode zum Testen der Distance-Klasse. In der Main-Methode wird alle 5 Sekunde ein neuer Wert vom Sensor abgefragt und auf die Console geschrieben.

159-2

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPin;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;
import com.pi4j.io.gpio.PinPullResistance;

public class TestDistance
{

    public static void main(String args[]){
        final GpioController gpio = GpioFactory.getInstance();

        //range sensor pins
        GpioPinDigitalOutput sensor_trigger = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_28, 
                "Sensor Trigger", PinState.LOW);

        GpioPinDigitalInput sensor_echo = gpio.provisionDigitalInputPin(RaspiPin.GPIO_25, 
                "Sensor Echo", PinPullResistance.PULL_DOWN);

        // Create the range sensor
        Distance rangesensor = new Distance(sensor_trigger, sensor_echo);
        int i = 0;
        do {
            i++;
            // Get the range
            double distance = rangesensor.getDistCM();
            
            System.out.println("RangeSensorresult =" + distance + "cm");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } while (i < 1000);
        

        gpio.unprovisionPin(gpio.getProvisionedPins().toArray(new GpioPin[]{sensor_trigger, sensor_echo}));
        
    }
}

Tips

Bei größerer Beanspruchung des PI ist zu beachten, dass Java die zeitlichen Abfolgen nicht garantiert. Somit kann es zu verfälschten oder keinen Ergebnissen bei der Messung kommen.

Quellen

https://www.modmypi.com/blog/hc-sr04-ultrasonic-range-sensor-on-the-raspberry-pi

http://t1m0b0t.blogspot.de/2014/04/using-ultrasonic-range-sensor-on.html

http://www.lediouris.net/RaspberryPI/HC-SR04/readme.htmlhttps://github.com/OlivierLD/raspberry-pi4j-samples/tree/master/RangeSensor

http://pi4j.com/pins/model-3b-rev1.html

Interfacing HC-SR04 Ultrasonic Sensor with Raspberry Pi

 

Entfernung messen mit Ultraschallsensor HC-SR04 – Raspberry Pi

Anhang

Pin-Belegung

 

Raspberry PI 3 LED ansteuern

Hier der erste Versuch eine LED mit dem Raspberry PI 3 anzusteuern. Aufgebaut wird das Ganze auf einem Brotboard mit Freiluft-Verkabelung.


Hardware

PI3 (oder älter bitte auf die richtigen Pins achten)
LED (Ich habe ne grüne Standard-Led 1,5V)
220 Ohm (Vorwiderstand für die LED)
Brot-Board (zum leichteren Aufbau)
Diverse Kabel ( Vorgefertigte Pin-Header Kabel)


Aufbau

Schaltplan

Verdrahtung


Software

Entwicklungsumgebung

Als Entwicklungsumgebung habe ich zum ersten mal BlueJ auf dem PI verwendet. Mir gefällt daran, dass man hier mit Klassendiagrammen erst einmal die Struktur definiert und im nächsten Schritt mit dem gleichen Tool die Implementierung realisieren kann.

Ein weiterer Vorteil bietet die Testumgebung. Es ist möglich Instanzen von Klassen per Knopfdruck zu erstellen und deren Methoden mit ein paar Klicks zu testen.


Code

LED-Klasse

Klasse zur Steuerung einer LED mit den Methoden On, Off und Toggle.

111-2

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.GpioPin;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class Led
{
    // instance variables - replace the example below with your own
    private GpioPinDigitalOutput pin;

    /**
     * Constructor for objects of class Led
     */
    public Led(Pin pinId)
    {
        
        if(pinId == null){
            pinId = RaspiPin.GPIO_01;
        }
        
        GpioController gpio = GpioFactory.getInstance();
        try{        // create gpio controller

               pin = gpio.provisionDigitalOutputPin(pinId);
            
        } catch(Exception e){ 

            // sollte er noch offen sein schliess mal alles und versuche es nochmal
            gpio.shutdown();
            gpio.unprovisionPin(gpio.getProvisionedPins().toArray(new GpioPin[]{}));
            gpio = GpioFactory.getInstance();
            
            pin = gpio.provisionDigitalOutputPin(pinId);
            
        }

    }
    
    void on(){
         pin.high();
    }

    void off(){
         pin.low();
    }

    
    void toggle(){
        if(pin.isHigh()){
            pin.low();
        }else {
            pin.high();
        }
    }
    
    public void close(){
        GpioController gpio = GpioFactory.getInstance();
        gpio.unprovisionPin(gpio.getProvisionedPins().toArray(new GpioPin[]{pin}));
    }
}

Der Try/Catch Block im Konstruktor ist dem Umstand geschuldet, dass meine ersten Tests auch mal schief gingen und dann die GPIOs nicht frei gegeben wurden.
Die Freigabe mache ich aktuell wenig elegant im Konstruktor. Schöner wäre vorher abzufragen ob der Pin schon initialisiert wurde und dann entsprechend weiter zu machen.

Test-Klasse

Klasse mit Main-Methode zum Testen der LED Klasse. In der Main-Methode ist eine Endlosschleife, welche die LED in einem Intervall von 2 Sekunden an und wieder aus schaltet.

115-2

public class TestLED
{
    // instance variables - replace the example below with your own
    private int x;

    /**
     * Constructor for objects of class TestLED
     */
    public void main(String args[])
    {
        Led led = new Led(null);
        while (true){
            led.toggle();
            try{
                Thread.sleep(2000);
            } catch(InterruptedException ex){
                // do nothing
            }
        }
    }
}

Tips

Starten des Programms als Root

Die GPIOs müssen als Root angesprochen werden. Im BlueJ gibt es in den Einstellungen einen Button aber dieser scheint nicht richtig initialisiert zu sein. Diesen einfach nochmal rausnehmen und wieder setzten, die Einstellungen speichern, dann sollte es gehen.

Fehlende pi4j Klassen

Wenn es beim compilieren Probleme mit fehlen pi4j Klassen gibt muss das entsprechende Paket installiert werden. (Hier gibt es Hilfe)

LED geht nicht an

Die LED geht einfach nicht an, evtl. mal mit GPIO auf der Console testen. Vielleicht stimmt die Verdrahtung nicht,  es fehlen Einstellungen oder die Pinbelegung stimmt nicht (Hier gibt es Hilfe dazu)

Quellen

http://pi4j.com/example/control.html

Raspberry Pi 2 – mit Java die GPIO ausreizen

 

US-100

Der US-100 ist ein Ultraschall-Enfernungsmesser mit einer Versorgungsspannung von 3V – 5V.  Er kann somit direkt an modernen Microcontrollern mit 3.3V Versorgungspannung betrieben werden.

Allgemein

Technische Daten

BeschreibungWert
Messung [mm]20 - 3500
Winkel [Grad]15
Genauigkeit [mm]3
Spannung [V]3-5
Strom [mA]2

Technische Daten US-100

Pin-Belegung

  1.  Versorgungsspannung 3-5V
  2. Trigger (Auslöser)
  3. Echo („Empfänger“)
  4. Masse
  5. Masse

Besonderheiten

Der US-100 kann in zwei Modi betrieben werden

  1. Im seriellen modus, Jumper auf der Rückseite ist gesetzt.
  2. Entfernungsmessung per Puls, der Jumper auf der Rückseite ist nicht gesetzt.
Serieller Modus

Hier wird der Trigger-Pin dan den TX port der UART-Schnittstelle (Serielle Schnittstelle) und der Echo-Pin an den RX-Port der UART-Schnittstelle angeschlossen.

Um den serielle port mit einem Microcontroller auslesen zu können muss dieser auf 9600 boud, 8 Datenbits, keine Parität und 1 Stopbit eingestellt werden.

Um die Messung zu starten muss ein einzelnes Byte 0x55 über den seriellen Port an den US-100 gesendet werden. Dananch kann die Enfernung mit 2 Bytes gelesen werden (High und Low byte). Um die Entfernung in Millimeter zu berechnen kann die folgenden Formel vewendet werden.

EntfernungInMM = ErstesByte * 256 + ZweitesByte

Durch senden eine Byte 0x50 kann zusätlich die Temperatur abgefragt werden. Die Anwort ist in diesem Fall 1 Byte lang und die Tempatur kann wie folgt berechnet werden

Temperatur °C  = GelesenesByste – 45

Pulsmessung

Hierfür müssenen der Trigger Pin und der Echo pin mit zwei digitalen Pins des Mircocontrollers verbunden werden.

Um die Entfernung abzufragen muss muss der Trigger für mindestens 50 micorsekunden auf low gesetzt werden.

Danach wird der Echo-Pin auf High gesetzt bis das Ultraschallsignal zum Hinderniss und wieder zurück gereist ist. Durch die Messung der Zeit kann die Entfernung mit der folgenden Formel errechnet werden.

Entfernung [cm] = Reisedauer [ms] / 58,2

Quellen

Datenblatt

SR-04

Der SR-04 ist einer der weit verbreitetsten Entfernungssensoren und dementsprechend günstig.

Allgemein

Technische Daten

BeschreibungWert
Messung [mm]20 - 4000
Winkel [Grad]15
Genauigkeit [mm]3
Spannung [V]5
Strom [mA]<2

Technische Daten SR-04

Pin-Belegung

  1.  Versorgungsspannung 5V
  2. Trigger (Auslöser)
  3. Echo („Empfänger“)
  4. Masse

Quellen

Datenblatt

Guter Einführung in den SR04

Super Einführungsvideo in englisch (eher technisch)

US-015

Der US-015 ist eine Ultraschall Entfernungsmesser, er kann Reichweiten bis zu 7m messen und ist gering im Stromverbrauch.

Allgemein

 

Technische Daten

BeschreibungWert
Messung [mm]20 - 7000
Winkel [Grad]15
Genauigkeit [mm]3
Spannung [V]5
Strom [mA]2

Technische Daten US-015

Pin-Belegung

  1.  Versorgungsspannung 5V
  2. Trigger (Auslöser)
  3. Echo („Empfänger“)
  4. Masse

Quellen

Tech Blog

Sensoren zur Entfernungsmessung

Hier sollen einige Sensoren zur Entfernungsmessung gezeigt werden. Die Aufstellung erhebt keinen Anspruch auf Vollständigkeit. Es werden nur die Sensoren beschrieben, die ich in meinen Projekten gerade verwende.


Ultraschallsensoren

Ultraschallsensoren senden einen Schallimpuls aus und warten dann auf eine Reflektion des Impulses durch ein Hinderniss. Trifft der Impuls nicht auf ein Hinderniss, wird er auch nicht zurück geworfen und der Sensor läuft auf eine Timeout. Dies ist bei der Verwendung dieser Sensoren zu berücksitigen.

SR-04 – Ultraschall Entfernungsmesser

US-015 Ultraschall Entfernungsmesser

US-100

 

 IR-Sensoren

Gegenüberstellung

SensornameEntfernung [mm]Genauigkeit [mm]Winkel [Grad]Spannung [V]Strom [mA]
US-01520 - 7000 3 155<2mA
SR-0420 - 40003155<2mA
US-10020 - 35003153-5<2mA