From d05eec16a50eb1507198d88c8bf6b048faab4661 Mon Sep 17 00:00:00 2001 From: Subodh Malgonde Date: Mon, 23 Jul 2018 13:34:10 +0530 Subject: [PATCH] Added sketch for testing Razor AHRS --- .gitignore | 2 +- .../Magnetometer_calibration.pde | 2 +- .../Razor_AHRS_test/Razor_AHRS_test.pde | 225 ++++++++++++++++++ .../Razor_AHRS_test/data/Univers-66.vlw | Bin 0 -> 116537 bytes 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 magnetometer_calibration/Processing/Razor_AHRS_test/Razor_AHRS_test.pde create mode 100644 magnetometer_calibration/Processing/Razor_AHRS_test/data/Univers-66.vlw diff --git a/.gitignore b/.gitignore index 2f470ea..3c45ccb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *~ config/my_razor.yaml .idea/ - +.DS_Store \ No newline at end of file diff --git a/magnetometer_calibration/Processing/Magnetometer_calibration/Magnetometer_calibration.pde b/magnetometer_calibration/Processing/Magnetometer_calibration/Magnetometer_calibration.pde index 3a60d96..e3c1457 100644 --- a/magnetometer_calibration/Processing/Magnetometer_calibration/Magnetometer_calibration.pde +++ b/magnetometer_calibration/Processing/Magnetometer_calibration/Magnetometer_calibration.pde @@ -40,7 +40,7 @@ import org.ejml.ops.*; // 1. Have a look at the Processing console output of this sketch. // 2. Look for the serial port list and find the port you need (it's the same as in Arduino). // 3. Set your port number here: -final static int SERIAL_PORT_NUM = 0; +final static int SERIAL_PORT_NUM = 1; // Command to find serial number on Mac - `ioreg -p IOUSB -l -b | grep -E "@|PortNum|USB Serial Number"` // 4. Try again. diff --git a/magnetometer_calibration/Processing/Razor_AHRS_test/Razor_AHRS_test.pde b/magnetometer_calibration/Processing/Razor_AHRS_test/Razor_AHRS_test.pde new file mode 100644 index 0000000..50f9957 --- /dev/null +++ b/magnetometer_calibration/Processing/Razor_AHRS_test/Razor_AHRS_test.pde @@ -0,0 +1,225 @@ +/****************************************************************************************** +* Test Sketch for Razor AHRS v1.4.2 +* 9 Degree of Measurement Attitude and Heading Reference System +* for Sparkfun "9DOF Razor IMU" and "9DOF Sensor Stick" +* +* Released under GNU GPL (General Public License) v3.0 +* Copyright (C) 2013 Peter Bartz [http://ptrbrtz.net] +* Copyright (C) 2011-2012 Quality & Usability Lab, Deutsche Telekom Laboratories, TU Berlin +* Written by Peter Bartz (peter-bartz@gmx.de) +* +* Infos, updates, bug reports, contributions and feedback: +* https://github.com/ptrbrtz/razor-9dof-ahrs +******************************************************************************************/ + +/* + NOTE: There seems to be a bug with the serial library in Processing versions 1.5 + and 1.5.1: "WARNING: RXTX Version mismatch ...". + Processing 2.0.x seems to work just fine. Later versions may too. + Alternatively, the older version 1.2.1 also works and is still available on the web. +*/ + +import processing.opengl.*; +import processing.serial.*; + +// IF THE SKETCH CRASHES OR HANGS ON STARTUP, MAKE SURE YOU ARE USING THE RIGHT SERIAL PORT: +// 1. Have a look at the Processing console output of this sketch. +// 2. Look for the serial port list and find the port you need (it's the same as in Arduino). +// 3. Set your port number here: +final static int SERIAL_PORT_NUM = 1; +// 4. Try again. + + +final static int SERIAL_PORT_BAUD_RATE = 57600; + +float yaw = 0.0f; +float pitch = 0.0f; +float roll = 0.0f; +float yawOffset = 0.0f; + +PFont font; +Serial serial; + +boolean synched = false; + +void drawArrow(float headWidthFactor, float headLengthFactor) { + float headWidth = headWidthFactor * 200.0f; + float headLength = headLengthFactor * 200.0f; + + pushMatrix(); + + // Draw base + translate(0, 0, -100); + box(100, 100, 200); + + // Draw pointer + translate(-headWidth/2, -50, -100); + beginShape(QUAD_STRIP); + vertex(0, 0 ,0); + vertex(0, 100, 0); + vertex(headWidth, 0 ,0); + vertex(headWidth, 100, 0); + vertex(headWidth/2, 0, -headLength); + vertex(headWidth/2, 100, -headLength); + vertex(0, 0 ,0); + vertex(0, 100, 0); + endShape(); + beginShape(TRIANGLES); + vertex(0, 0, 0); + vertex(headWidth, 0, 0); + vertex(headWidth/2, 0, -headLength); + vertex(0, 100, 0); + vertex(headWidth, 100, 0); + vertex(headWidth/2, 100, -headLength); + endShape(); + + popMatrix(); +} + +void drawBoard() { + pushMatrix(); + + rotateY(-radians(yaw - yawOffset)); + rotateX(-radians(pitch)); + rotateZ(radians(roll)); + + // Board body + fill(255, 0, 0); + box(250, 20, 400); + + // Forward-arrow + pushMatrix(); + translate(0, 0, -200); + scale(0.5f, 0.2f, 0.25f); + fill(0, 255, 0); + drawArrow(1.0f, 2.0f); + popMatrix(); + + popMatrix(); +} + +// Skip incoming serial stream data until token is found +boolean readToken(Serial serial, String token) { + // Wait until enough bytes are available + if (serial.available() < token.length()) + return false; + + // Check if incoming bytes match token + for (int i = 0; i < token.length(); i++) { + if (serial.read() != token.charAt(i)) + return false; + } + + return true; +} + +// Global setup +void setup() { + // Setup graphics + size(640, 480, OPENGL); + smooth(); + noStroke(); + frameRate(50); + + // Load font + font = loadFont("Univers-66.vlw"); + textFont(font); + + // Setup serial port I/O + println("AVAILABLE SERIAL PORTS:"); + println(Serial.list()); + String portName = Serial.list()[SERIAL_PORT_NUM]; + println(); + println("HAVE A LOOK AT THE LIST ABOVE AND SET THE RIGHT SERIAL PORT NUMBER IN THE CODE!"); + println(" -> Using port " + SERIAL_PORT_NUM + ": " + portName); + serial = new Serial(this, portName, SERIAL_PORT_BAUD_RATE); +} + +void setupRazor() { + println("Trying to setup and synch Razor..."); + + // On Mac OSX and Linux (Windows too?) the board will do a reset when we connect, which is really bad. + // See "Automatic (Software) Reset" on http://www.arduino.cc/en/Main/ArduinoBoardProMini + // So we have to wait until the bootloader is finished and the Razor firmware can receive commands. + // To prevent this, disconnect/cut/unplug the DTR line going to the board. This also has the advantage, + // that the angles you receive are stable right from the beginning. + delay(3000); // 3 seconds should be enough + + // Set Razor output parameters + serial.write("#ob"); // Turn on binary output + serial.write("#o1"); // Turn on continuous streaming output + //serial.write("#oe0"); // Disable error message output + + // Synch with Razor + serial.clear(); // Clear input buffer up to here + serial.write("#s00"); // Request synch token +} + +float readFloat(Serial s) { + // Convert from little endian (Razor) to big endian (Java) and interpret as float + return Float.intBitsToFloat(s.read() + (s.read() << 8) + (s.read() << 16) + (s.read() << 24)); +} + +void draw() { + // Reset scene + background(0); + lights(); + + // Sync with Razor + if (!synched) { + textAlign(CENTER); + fill(255); + text("Connecting to Razor...", width/2, height/2, -200); + + if (frameCount == 2) + setupRazor(); // Set ouput params and request synch token + else if (frameCount > 2) + synched = readToken(serial, "#SYNCH00\r\n"); // Look for synch token + return; + } + + // Read angles from serial port + while (serial.available() >= 12) { + yaw = readFloat(serial); + pitch = readFloat(serial); + roll = readFloat(serial); + } + + // Draw board + pushMatrix(); + translate(width/2, height/2, -350); + drawBoard(); + popMatrix(); + + textFont(font, 20); + fill(255); + textAlign(LEFT); + + // Output info text + text("Point FTDI connector towards screen and press 'a' to align", 10, 25); + + // Output angles + pushMatrix(); + translate(10, height - 10); + textAlign(LEFT); + text("Yaw: " + ((int) yaw), 0, 0); + text("Pitch: " + ((int) pitch), 150, 0); + text("Roll: " + ((int) roll), 300, 0); + popMatrix(); +} + +void keyPressed() { + switch (key) { + case '0': // Turn Razor's continuous output stream off + serial.write("#o0"); + break; + case '1': // Turn Razor's continuous output stream on + serial.write("#o1"); + break; + case 'f': // Request one single yaw/pitch/roll frame from Razor (use when continuous streaming is off) + serial.write("#f"); + break; + case 'a': // Align screen with Razor + yawOffset = yaw; + } +} diff --git a/magnetometer_calibration/Processing/Razor_AHRS_test/data/Univers-66.vlw b/magnetometer_calibration/Processing/Razor_AHRS_test/data/Univers-66.vlw new file mode 100644 index 0000000000000000000000000000000000000000..d2d08c24749c2e3da957da256b80585a31514ddf GIT binary patch literal 116537 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