Archiv der Kategorie: Aktoren

Aktoren und Peripherie die zum ansteuern von Aktoren nötig ist

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