Visualization in Web Browser and Unity
For this week, Mimi and I worked together to visualize IMU orientation in both web browser and also in Unity. For the web serial vsiualizer, after experimenting with processing and trying to make it work, we decided to use the webserial API for Chrome browser. It was much easier to do (thanks to chrome. ) For this one, we learned to use Adafruit Unified Sensor and BNO055.Arduino Wiring
Final Wiring
Wire Details
Final webserial visualizer
From computer
Computer + in-person
Arduino BNO055 code web visualizer
#include
#include
#include
#include
Adafruit_BNO055 bno = Adafruit_BNO055(55);
void setup(void)
{
Serial.begin(9600);
Serial.println("Orientation Sensor Test"); Serial.println("");
/* Initialise the sensor */
if(!bno.begin())
{
/* There was a problem detecting the BNO055 ... check your connections */
Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
while(1);
}
delay(1000);
bno.setExtCrystalUse(true);
}
void loop(void)
{
/* Get a new sensor event */
sensors_event_t event;
bno.getEvent(&event);
/* Display the floating point data */
Serial.print("X: ");
Serial.print(event.orientation.x, 4);
Serial.print("\tY: ");
Serial.print(event.orientation.y, 4);
Serial.print("\tZ: ");
Serial.print(event.orientation.z, 4);
Serial.println("");
delay(100);
}
Unity IMU
Then we tried to visualize the same thing in Unity. Unity took a while, as I don't know how to use C-sharp. The compiling took a while, syntax etc took a long time to make a very simple thing work. The connection between Arduino and Unity also was not easy. I need to upload the file first, and then the port will be occupied by Unity. If I want to change anything, I have to close the port and then reconnect after reuploading.
Final look
Unity Interface
IMU C sharp code
using UnityEngine;
using Uduino;
public class ReceiveIMUValues : MonoBehaviour {
Vector3 position;
Vector3 rotation;
public Vector3 rotationOffset ;
public float speedFactor = 15.0f;
public string imuName = "r"; // You should ignore this if there is one IMU.
void Start () {
// UduinoManager.Instance.OnDataReceived += ReadIMU;
// Note that here, we don't use the delegate but the Events, assigned in the Inpsector Panel
}
void Update() { }
public void ReadIMU (string data, UduinoDevice device) {
//Debug.Log(data);
string[] values = data.Split('/');
if (values.Length == 5 && values[0] == imuName) // Rotation of the first one
{
float w = float.Parse(values[1]);
float x = float.Parse(values[2]);
float y = float.Parse(values[3]);
float z = float.Parse(values[4]);
this.transform.localRotation = Quaternion.Lerp(this.transform.localRotation, new Quaternion(w, y, x, z), Time.deltaTime * speedFactor);
} else if (values.Length != 5)
{
Debug.LogWarning(data);
}
this.transform.parent.transform.eulerAngles = rotationOffset;
// Log.Debug("The new rotation is : " + transform.Find("IMU_Object").eulerAngles);
}
}
IMU Arduino code
#include "Uduino.h" // Include Uduino library at the top of the sketch
Uduino uduino("IMU");
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"
MPU6050 mpu;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
void setup() {
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
Serial.begin(38400);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
mpu.initialize();
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(54); //++
mpu.setYGyroOffset(-21); //--
mpu.setZGyroOffset(5);
if (devStatus == 0) {
mpu.setDMPEnabled(true);
// set our DMP Ready flag so the main loop() function knows it's okay to use it
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// Error
Serial.println("Error!");
}
}
void loop() {
uduino.update();
if (uduino.isInit()) {
if (!dmpReady) {
Serial.println("IMU not connected.");
delay(10);
return;
}
int mpuIntStatus = mpu.getIntStatus();
fifoCount = mpu.getFIFOCount();
if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // check if overflow
mpu.resetFIFO();
} else if (mpuIntStatus & 0x02) {
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
mpu.getFIFOBytes(fifoBuffer, packetSize);
fifoCount -= packetSize;
SendQuaternion();
//SendEuler();
//SendYawPitchRoll();
//SendRealAccel();
//SendWorldAccel();
}
}
}
void SendQuaternion() {
mpu.dmpGetQuaternion(&q, fifoBuffer);
Serial.print("r/");
Serial.print(q.w, 4); Serial.print("/");
Serial.print(q.x, 4); Serial.print("/");
Serial.print(q.y, 4); Serial.print("/");
Serial.println(q.z, 4);
}
void SendEuler() {
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetEuler(euler, &q);
Serial.print(euler[0] * 180 / M_PI); Serial.print("/");
Serial.print(euler[1] * 180 / M_PI); Serial.print("/");
Serial.println(euler[2] * 180 / M_PI);
}
void SendYawPitchRoll() {
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print(ypr[0] * 180 / M_PI); Serial.print("/");
Serial.print(ypr[1] * 180 / M_PI); Serial.print("/");
Serial.println(ypr[2] * 180 / M_PI);
}
void SendRealAccel() {
// display real acceleration, adjusted to remove gravity
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
Serial.print("a/");
Serial.print(aaReal.x); Serial.print("/");
Serial.print(aaReal.y); Serial.print("/");
Serial.println(aaReal.z);
}
void SendWorldAccel() {
// display initial world-frame acceleration, adjusted to remove gravity
// and rotated based on known orientation from quaternion
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
Serial.print("a/");
Serial.print(aaWorld.x); Serial.print("/");
Serial.print(aaWorld.y); Serial.print("/");
Serial.println(aaWorld.z);
}
LED blink and fade in Unity
LED blink
Control LED high and low
LED strip Arduino code
#include
#include
#define NUM_LEDS 60
#define DATA_PIN 3
CRGB leds[NUM_LEDS];
Uduino uduino("PixelBoard");
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
FastLED.addLeds(leds, 60);
FastLED.setBrightness(20);
uduino.addCommand("turnOn", turnOnPixel);
uduino.addCommand("turnOff", turnOffPixel);
}
void turnOnPixel() {
int led = atoi(uduino.getParameter(0));
int r = atoi(uduino.getParameter(1));
int g = atoi(uduino.getParameter(2));
int b = atoi(uduino.getParameter(3));
leds[led].setRGB(r,g,b);
FastLED.show();
}
void turnOffPixel() {
int led = atoi(uduino.getParameter(0));
leds[led].setRGB(0,0,0);
FastLED.show();
}
void loop() {
// put your main code here, to run repeatedly:
uduino.update();
delay(10);
}