Bouw een Real-time Digitale Kompas met Arduino & Processing
In dit Arduino-project laten we zien hoe we deze coole kompas kunnen maken met een Arduino, een MEMS-magnetometer en de Processing IDE. Hier is een demonstratievideo van het kompas:
Overzicht
Voor dit project heb je slechts een MEMS-magnetometer nodig voor het meten van het aardmagnetisch veld, een Arduino-bord en enkele jumper-draden. Als voorbeeld gebruik ik de HMC5883L, een 3-as magnetometer geïntegreerd in de GY-80 breakout-board.
Hoe de Kompas Werkt
Arduino Deel
Eerst moeten we de gegevens van de sensor ophalen met het Arduino-bord via het I2C-protocol. Vervolgens berekenen we met de X-as- en Y-as-waarden van de sensor de koers en sturen we de waarde naar de Processing IDE via de seriële poort. De volgende code doet dat:
/* Arduino Kompas
*
* door Dejan Nedelkovski,
* www.HowToMechatronics.com
* */
#include <Wire.h> // I2C Arduino Bibliotheek
#define Magnetometer_mX0 0x03
#define Magnetometer_mX1 0x04
#define Magnetometer_mZ0 0x05
#define Magnetometer_mZ1 0x06
#define Magnetometer_mY0 0x07
#define Magnetometer_mY1 0x08
int mX0, mX1, mX_out;
int mY0, mY1, mY_out;
int mZ0, mZ1, mZ_out;
float heading, headingDegrees, headingFiltered, declination;
float Xm, Ym, Zm;
#define Magnetometer 0x1E // I2C 7-bit adres van HMC5883
void setup() {
// Seriële en I2C communicatie initialiseren
Serial.begin(115200);
Wire.begin();
delay(100);
Wire.beginTransmission(Magnetometer);
Wire.write(0x02); // Selecteer modus register
Wire.write(0x00); // Continue meetmodus
Wire.endTransmission();
}
void loop() {
//---- X-as
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mX1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mX0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mX0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mX1 = Wire.read();
}
//---- Y-as
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mY1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mY0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mY0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mY1 = Wire.read();
}
//---- Z-as
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mZ1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mZ0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // Transmitteer naar apparaat
Wire.write(Magnetometer_mZ0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer, 1);
if(Wire.available() <= 1) {
mZ1 = Wire.read();
}
//---- X-as
mX1 = mX1 << 8;
mX_out = mX0 + mX1; // Ruwe data
// Vanuit de datasheet: 0.92 mG/digit
Xm = mX_out * 0.00092; // Gauss eenheid
//* Aardmagnetisch veld varieert van 0.25 tot 0.65 Gauss, dus dit zijn de waarden die we ongeveer moeten krijgen.
//---- Y-as
mY1 = mY1 << 8;
mY_out = mY0 + mY1;
Ym = mY_out * 0.00092;
//---- Z-as
mZ1 = mZ1 << 8;
mZ_out = mZ0 + mZ1;
Zm = mZ_out * 0.00092;
// ==============================
// Koers berekenen
heading = atan2(Ym, Xm);
// De koers corrigeren met de declinatiehoek afhankelijk van je locatie
// Je kunt je declinatiehoek vinden op: https://www.ngdc.noaa.gov/geomag-web/
// Op mijn locatie is het 4.2 graden => 0.073 rad
declination = 0.073;
heading += declination;
// Corrigeren wanneer tekens omgekeerd zijn
if(heading < 0) heading += 2*PI;
// Corrigeren door toevoeging van declinatiehoek
if(heading > 2*PI) heading -= 2*PI;
headingDegrees = heading * 180/PI; // De koers in graden
// Uitgangshoek gladstrijken / Laagdoorlaatfilter
headingFiltered = headingFiltered * 0.85 + headingDegrees * 0.15;
// De koerswaarde verzenden via de seriële poort naar Processing IDE
Serial.println(headingFiltered);
delay(50);
}
Als je meer details nodig hebt over hoe de MEMS-magnetometer werkt en hoe je de gegevens eruit haalt, kun je mijn MEMS-sensoren-tutorial bekijken.
Processing IDE Deel
Hier moeten we eerst de koerswaarden ontvangen die van de seriële poort komen. Voor meer details over hoe dit gedaan wordt, kun je mijn Arduino en Processing-tutorial bekijken.
Het kompas is eigenlijk een afbeelding, of preciezer, het bestaat uit meerdere transparante afbeeldingen die in de Processing IDE worden geladen. De afbeeldingen moeten zich in de werkmap van de sketch bevinden. Na het definiëren van de afbeeldingsobjecten in de draw()-sectie laden we met de image()-functie de achtergrondafbeelding (wat optioneel is, je kunt ook gewoon een eenvoudige kleur voor de achtergrond gebruiken). Vervolgens wordt de kompasafbeelding geladen, die met de rotateZ()-functie wordt gedraaid met de waarden van de koers. Bovenop wordt de kompas-pijl afbeelding geladen.
Hier is de Processing IDE-code:
/* Arduino Kompas
*
* door Dejan Nedelkovski,
* www.HowToMechatronics.com
* */
import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;
Serial myPort;
PImage imgCompass;
PImage imgCompassArrow;
PImage background;
String data = "";
float heading;
void setup() {
size(1920, 1080, P3D);
smooth();
imgCompass = loadImage("Compass.png");
imgCompassArrow = loadImage("CompassArrow.png");
background = loadImage("Background.png");
myPort = new Serial(this, "COM4", 115200); // Start seriële communicatie
myPort.bufferUntil('\n');
}
void draw() {
image(background, 0, 0); // Laadt achtergrondafbeelding
pushMatrix();
translate(width/2, height/2, 0); // Verplaatst coördinatensysteem naar midden scherm, zodat rotatie in het midden plaatsvindt
rotateZ(radians(-heading)); // Draait kompas om Z-as
image(imgCompass, -960, -540); // Laadt kompasafbeelding en omdat coördinatensysteem verplaatst is, moeten we afbeelding op -960x, -540y zetten (halve schermgrootte)
popMatrix(); // Brengt coördinatensysteem terug naar oorspronkelijke positie 0,0,0
image(imgCompassArrow, 0, 0); // Laadt kompas-pijl afbeelding die niet beïnvloed wordt door rotateZ() vanwege popMatrix()
textSize(30);
text("Koers: " + heading, 40, 40); // Print waarde van koers op scherm
delay(40);
}
// Start met lezen van data van de seriële poort
void serialEvent(Serial myPort) {
data = myPort.readStringUntil('\n'); // Leest data van seriële poort en zet in String variabele "data"
heading = float(data); // Converteert String-waarde naar Float-waarde
}
Hier kun je de projectbestanden downloaden, de afbeeldingen en de broncodes:
Productieproces
- Bouw een indrukwekkend monitor Ambilight-systeem met Arduino
- Bouw een Ultrasonische Zweefapparaat met Arduino – Stap-voor-stap Handleiding
- Universele Android-afstandsbediening bouwen met Arduino, 1Sheeld en IR-besturing
- Een slimme voltmeter bouwen met Arduino en smartphone – Eenvoudig DIY-project
- Nauwkeurige Frequentie- en Duty Cycle-meting met Arduino UNO
- Maak een Aangepaste 'Punchable' Toetsenbordknop met Arduino: Stap-voor-Stap Handleiding
- Realtime afstandsmeting met Arduino Sonar & Processing visualisatie
- Slimme Parkeer Teller: Real-time Voertuigvolging met Arduino, Processing en PHP
- Naadloze LED-dimmer met Bolt IoT en Arduino UNO
- Bouw een Slimme Arduino-gestuurde Robotarm: Eenvoudige DIY Handleiding
- Een PHP-webinterface bouwen om je Arduino Uno te besturen