Hier eine kurze Vorstellung samt einiger Links von BrickPi3 Shields von Dexter Industries, als Alternative für Lego EV3:
Hersteller-Seite: https://www.dexterindustries.com/brickpi/
Die wichtigsten technischen Änderungen zu den früheren BrickPi-Versionen:
Die Kommunikation von den Brickpi3 Shields zum Pi läuft nun über SPI und nicht mehr wie früher über UART, dadruch wurde die Stapelbarkeit erst ermöglicht. Daneben arbeiten nun als Prozessoren ARM M0 (SAMD21) auf den BrickPi3, keine Atmel AVRs mehr, und dadurch arbeiten sie schneller als die Vorversionen.
An weiteren Programmiersprachen werden außer C++ u.a. auch Python und Scratch unterstützt, ich will mich hier aber auf die C++ API beschränken. Die C++ Bibliotheken werden per gcc/gpp Compiler programmiert, der in der Standard-Raspbian-Distribution (z.B. jessie) bereits standardmäßig vorinstalliert ist.
Link zur github-Repo: https://github.com/DexterInd/BrickPi...ter/Software/C
Man kann je Shield 4 Lego-Motoren (reine DC- oder auch integrierte DC-Encodermotoren) und je 4 Lego-Sensoren (NXT, EV3) anschließen (z.B. Taster, die NXT Licht + Ultraschall- und die EV3 IR-Sensoren), sie lassen sich mit einfacher C/C++ Syntax programmieren (nicht ganz so einfach wie NXC, aber annähernd), und wenn man Geany als IDE direkt auf dem Raspi verwendet, muss man sich auch nicht mit ssh und puTTY, mit makefile oder mit Monster-Crosscompilern wie Eclipse herumschlagen:
Einfach HDMI Bildschirm an den Pi anschließen (5", 7", 10", 15", 24", was man grade halt da hat, ggf. auch Touchscreens, es skaliert sich automatisch) und eine Maus und ein keyboard (auch wireless), und man kann sofort loslegen.
BrickPi3 ist wie erwähnt (nur) per SPI über die GPIO Steckerleiste mit dem Pi verbunden, ist stapelbar (ich meine mich zu erinnern: mindestens 8 Stück) und man hat so ohne weiteres 32 Lego-Sensoren und 32 Lego-Motoren zum Ansteuern (ggf sogar mehr), und das mit 1 einzigen Raspi.
Außerdem hat jedes Shield je 1 Grove Stecker für i2c-Sensoren, von denen man über 100 Stück einfach zusätzlich aneinanderhängen kann (wie bei I2C üblich).
Daneben sind die Raspi i2c- und UART ports und die meisten normalen digitalen GPIOS unbenutzt und weiter frei verfügbar, und man kann also jede Menge Raspi-Standard-Sensoren (Taster, Multiplexer, i2c-Sensoren etc.pp.) wie bei einem ganz normalen Raspi zusätzlich weiter verwenden, es können sogar noch weitere Bus-Ports wie 1-Wire, USB, und sogar der versteckte i2c-0 Bus genutzt werden.
Quick-Installation:
einfach durch terminal command:
Code:
sudo curl -kL dexterindustries.com/update_brickpi3 | bash
für C++:
es werden die C++ Libs ebenfalls mit übertragen und dabei BrickPi3.h und BrickPi3.cpp nach /usr/local/include kopiert. Dadurch sind für den Linker keine weiteren Linker-Flags nötig, einfach nur per
#include "BrickPi3.cpp"
im eigenen Code einbinden.
Funktionen der BrickPi3 class:
Code:
/*
* https://www.dexterindustries.com/BrickPi/
* https://github.com/DexterInd/BrickPi3
*
* Copyright (c) 2017 Dexter Industries
* Released under the MIT license (http://choosealicense.com/licenses/mit/).
* For more information, see https://github.com/DexterInd/BrickPi3/blob/master/LICENSE.md
*/
class BrickPi3{
public:
// Default to address 1, but the BrickPi3 address could have been changed.
BrickPi3(uint8_t addr = 1);
// Confirm that the BrickPi3 is connected and up-to-date
int detect(bool critical = true);
// Get the manufacturer (should be "Dexter Industries")
int get_manufacturer(char *str);
// Get the board name (should be "BrickPi3")
int get_board(char *str);
// Get the hardware version number
int get_version_hardware(char *str);
// Get the firmware version number
int get_version_firmware(char *str);
// Get the serial number ID that is unique to each BrickPi3
int get_id(char *str);
// Control the LED
int set_led(uint8_t value);
// Get the voltages of the four power rails
int get_voltage_3v3 (float &voltage);
float get_voltage_3v3 ();
int get_voltage_5v (float &voltage);
float get_voltage_5v ();
int get_voltage_9v (float &voltage);
float get_voltage_9v ();
int get_voltage_battery(float &voltage);
float get_voltage_battery();
// Configure a sensor
int set_sensor_type(uint8_t port, uint8_t type, uint16_t flags = 0, i2c_struct_t *i2c_struct = NULL);
// Configure and trigger an I2C transaction
int transact_i2c(uint8_t port, i2c_struct_t *i2c_struct);
// Get sensor value(s)
int get_sensor(uint8_t port, void *value);
// Set the motor PWM power
int set_motor_power(uint8_t port, int8_t power);
// Set the motor target position to run to
int set_motor_position(uint8_t port, int32_t position);
// Set the motor speed in degrees per second. As of FW version 1.4.0, the algorithm regulates the speed, but it is not accurate.
int set_motor_dps(uint8_t port, int16_t dps);
// Set the motor limits. Only the power limit is implemented. Use the power limit to limit the motor speed/torque.
int set_motor_limits(uint8_t port, uint8_t power, uint16_t dps);
// Get the motor status. State, PWM power, encoder position, and speed (in degrees per second)
int get_motor_status(uint8_t port, uint8_t &state, int8_t &power, int32_t &position, int16_t &dps);
// Offset the encoder position. By setting the offset to the current position, it effectively resets the encoder value.
int offset_motor_encoder(uint8_t port, int32_t position);
// Get the encoder position
int get_motor_encoder(uint8_t port, int32_t &value);
int32_t get_motor_encoder(uint8_t port);
// Reset the sensors (unconfigure), motors (float with no limits), and LED (return control to the firmware).
int reset_all();
};
Beispiel für Sensor-API-Funktionen:
Code:
/*
* https://www.dexterindustries.com/BrickPi/
* https://github.com/DexterInd/BrickPi3
*
* Copyright (c) 2017 Dexter Industries
* Released under the MIT license (http://choosealicense.com/licenses/mit/).
* For more information, see https://github.com/DexterInd/BrickPi3/blob/master/LICENSE.md
*
* This code is an example for reading the encoders of motors connected to the BrickPi3.
*
* Hardware: Connect NXT sensors to the sensor ports. Color sensor to PORT_1. Ultrasonic sensor to PORT_2. Light sensor to PORT_3. Touch sensor to PORT_4 (EV3 or NXT touch sensor).
*
* Results: When you run this program, you should see the values for each sensor.
*
* Example compile command:
* g++ -o program "sensors_nxt.c"
* Example run command:
* sudo ./program
*
*/
#include "BrickPi3.cpp" // for BrickPi3
#include <stdio.h> // for printf
#include <unistd.h> // for usleep
#include <signal.h> // for catching exit signals
BrickPi3 BP;
void exit_signal_handler(int signo);
int main(){
signal(SIGINT, exit_signal_handler); // register the exit function for Ctrl+C
BP.detect(); // Make sure that the BrickPi3 is communicating and that the firmware is compatible with the drivers.
int error;
BP.set_sensor_type(PORT_1, SENSOR_TYPE_NXT_COLOR_FULL);
BP.set_sensor_type(PORT_2, SENSOR_TYPE_NXT_ULTRASONIC);
BP.set_sensor_type(PORT_3, SENSOR_TYPE_NXT_LIGHT_ON);
BP.set_sensor_type(PORT_4, SENSOR_TYPE_TOUCH);
sensor_color_t Color1;
sensor_ultrasonic_t Ultrasonic2;
sensor_light_t Light3;
sensor_touch_t Touch4;
while(true){
error = 0;
if(BP.get_sensor(PORT_1, &Color1)){
error++;
}else{
printf("Color sensor (S1): detected %d red %4d green %4d blue %4d ambient %4d ", Color1.color, Color1.reflected_red, Color1.reflected_green, Color1.reflected_blue, Color1.ambient);
}
if(BP.get_sensor(PORT_2, &Ultrasonic2)){
error++;
}else{
printf("Ultrasonic sensor (S2): CM %5.1f Inches %5.1f ", Ultrasonic2.cm, Ultrasonic2.inch);
}
if(BP.get_sensor(PORT_3, &Light3)){
error++;
}else{
printf("Light sensor (S3): reflected %4d ", Light3.reflected);
}
if(BP.get_sensor(PORT_4, &Touch4)){
error++;
}else{
printf("Touch sensor (S4): pressed %d ", Touch4.pressed);
}
if(error == 4){
printf("Waiting for sensors to be configured");
}
printf("\n");
usleep(20000);
}
}
// Signal handler that will be called when Ctrl+C is pressed to stop the program
void exit_signal_handler(int signo){
if(signo == SIGINT){
BP.reset_all(); // Reset everything so there are no run-away motors
exit(-2);
}
}
Beispiel für Motor-API-Funktionen (inkl. eingebauter Rotationsencoder):
Code:
/*
* https://www.dexterindustries.com/BrickPi/
* https://github.com/DexterInd/BrickPi3
*
* Copyright (c) 2017 Dexter Industries
* Released under the MIT license (http://choosealicense.com/licenses/mit/).
* For more information, see https://github.com/DexterInd/BrickPi3/blob/master/LICENSE.md
*
* This code is an example for reading the encoders of motors connected to the BrickPi3.
*
* Hardware: Connect EV3 or NXT motor(s) to any of the BrickPi3 motor ports.
*
* Results: When you run this program, you should see the encoder value for each motor. By manually rotating motor A, the other motor(s) will be controlled. Motor B power will be controlled, Motor C speed will be controlled, and motor D position will be controlled.
*
* Example compile command:
* g++ -o program "motors.c"
* Example run command:
* sudo ./program
*
*/
#include "BrickPi3.cpp" // for BrickPi3
#include <stdio.h> // for printf
#include <unistd.h> // for usleep
#include <signal.h> // for catching exit signals
BrickPi3 BP;
void exit_signal_handler(int signo);
int main(){
signal(SIGINT, exit_signal_handler); // register the exit function for Ctrl+C
BP.detect(); // Make sure that the BrickPi3 is communicating and that the firmware is compatible with the drivers.
// Reset the encoders
BP.offset_motor_encoder(PORT_A, BP.get_motor_encoder(PORT_A));
BP.offset_motor_encoder(PORT_B, BP.get_motor_encoder(PORT_B));
BP.offset_motor_encoder(PORT_C, BP.get_motor_encoder(PORT_C));
BP.offset_motor_encoder(PORT_D, BP.get_motor_encoder(PORT_D));
while(true){
// Read the encoders
int32_t EncoderA = BP.get_motor_encoder(PORT_A);
int32_t EncoderB = BP.get_motor_encoder(PORT_B);
int32_t EncoderC = BP.get_motor_encoder(PORT_C);
int32_t EncoderD = BP.get_motor_encoder(PORT_D);
// Use the encoder value from motor A to control motors B, C, and D
BP.set_motor_power(PORT_B, EncoderA < 100 ? EncoderA > -100 ? EncoderA : -100 : 100);
BP.set_motor_dps(PORT_C, EncoderA);
BP.set_motor_position(PORT_D, EncoderA);
// Display the encoder values
printf("Encoder A: %6d B: %6d C: %6d D: %6d\n", EncoderA, EncoderB, EncoderC, EncoderD);
// Delay for 20ms
usleep(20000);
}
}
// Signal handler that will be called when Ctrl+C is pressed to stop the program
void exit_signal_handler(int signo){
if(signo == SIGINT){
BP.reset_all(); // Reset everything so there are no run-away motors
exit(-2);
}
}
Wie gesagt, da fürs BrickPi3 Shield nur SPI benutzt wird (GPIO BCM 7, 9, 10, und 11), lässt sich zusätzlich auch jeder weitere Code ergänzen und einschließen, um die Raspi-GPIOs wie üblich per wiringPi, pigpio oder Linux/bcm-Funktionen auszulesen oder zu steuern, als digitale, pwm, i2c, 1-Wire, (ggf. sogar weitere SPI über den 2. SPI_SS) etc.
Einer "ganz normalen" Raspi-Programmierung von Standard-Hardware steht daher nichts im Wege.
Auch Multithreading z.B. via pthread ist möglich, doch sollten zeitintensive GPIO-write-Funktionen durch Mutexe geschützt werden, falls diese zeitkritisch auch in Parallel-Threads korrekt gelesen oder geschrieben werden sollen. Man benötigt dazu im .c Programm nur:
Code:
#include <pthread.h> // POSIX pthread multithreading, link flag: -pthread
pthread_mutex_t mutexBP; // Mutex variable
int main() {
// *SNIP*
pthread_mutex_init (&mutexBP, NULL); // init a mutex
// *SNIP*
}
// Und dann im Quellcode vor/hinter jeweils allen zu schützenden statements:
pthread_mutex_lock (&mutexBP); // lock the following variable operations by the mutex
// (statements)
pthread_mutex_unlock (&mutexBP); // release the mutex to write/read by different threads
//
Um mehrere gestapelte Shields gleichzeitig/nebeneinander anzusprechen, wird jedes Shield über seine ID initialisiert.
Ein paar Hersteller-Anleitungen, da nicht immer dort einfach zu finden:
Verbindungstest (Hardware/Firmware/Driver):
Zum Verbindungstest kann dieses Python-Programm verwendet werden:
Code:
python3 /home/pi/Dexter/BrickPi3/Software/Python/Examples/Test_Connected.py
Achtung: Es gibt auch einen Fehler aus, wenn nur die Firmware Version zu alt ist, auch wenn sonst alles ok sein sollte.
Check / Update Firmware:
To check the firmware version, you can either run the python example "Read_Info.py" or you can compile and run the C program "info.c".
You can update the firmware as described here:
https://github.com/DexterInd/BrickPi...ware/README.md
check the firmware version:
Code:
python3 /home/pi/Dexter/BrickPi3/Software/Python/Examples/Read_Info.py
update the firmware (bei mir war es ver 1.0.1, verlangt wird 1.4.x, )
Code:
sudo bash /home/pi/Dexter/BrickPi3/Firmware/brickpi3samd_flash_firmware.sh
Was positiv ist:
- die nicht standard-typisch aufgebauten Lego-Sensoren können ohne große Schaltungs- und Programmier-Akrobatik mit einfachen "high-level"-Funktionen abgefragt werden (wer das schon einmal z.B. mit Arduinos selber probiert hat, weiß wovon ich rede).
- man muss sich nicht ums Auslesen der einzelnen Rotationsencoder für die Ermittlung von Drehung, Geschwindigkeit, aktueller und Ziel-Stellung der Motoren kümmern, und auch die Motor-Regulation erfolgt einfach über die eingebauten PD-Regler. Sie funktionieren übrigens auch mit handelsüblichen Rotationsencodern bei einer Drehgeschwindigkeit von bis zu über 2000 Encoderticks pro Sek.
- Der Raspi kann ohne Zusatzhardware nicht mehr als etwa 3 Encodermotren auslesen und regeln (4-5 pins pro Motor!), und dann bleibt kaum etwas für weitere dig. GPIO r/w übrig. Dadurch, dass die brickPi3 stapelbar sind, sind Vielfache von 4 Motoren möglich (von Dexter getestet: bis zu 32).
Nachteile bisher:
- es werden keine 3rd-Party Sensoren z.B. von Hitechnic oder Mindsensors supportet, auch manche Lego NXT Sensoren nicht, wie z.B. der Lego I2C Temperatursensor (da muss man dann ggf. ganz andere verwenden), und auch nicht die cmuCam5 wie mit den Pixy-Lego-Treibern bzw. in der Mindsensors-Vision v4-Variante.
- es fehlen in der Motor-API einfach handhabbare Funktionen für ramp-up/down, z.B. vergleichbar mit den Lego lms2012 (X3, EV3Basic) oder Java (leJOS)-Motor-APIs, um allzu abruptes Beschleunigen oder Abstoppen zu verhindern.
- Bauartbedingt beanspruchen die Lego-RJ11-Steckbuchsen sehr viel Platz, wodurch leider nur ein 26er GPIO-Header durchgeschleift wird, nicht alle 40, daher müssen GPIOs 27-40 anders abgegriffen werden (z.B. mit Jumperkabeln).
- Außerdem sind durch die Lego-Buchsen die Montagelöcher auf der USB/LAN-Seite des Raspis nicht für Shields zugänglich, was die Montage v.a. von gestapelten Shields erschwert.
Lesezeichen