Puck.js v2 Accelerometer/Gyroscope output...

Posted on
  • Hi Everybody,

    I've just recently begun working with the Puck.js v2 board. I updated the firmware to "espruino_2v07" immediately after powering it up. I'm starting a project hoping to count revolutions on a rotating spindle with the Puck's onboard LSM6DS3TR-C. Two questions:

    1) Is counting revolutions this way even possible (versus using a more traditional optical encoder or magnetic sensor approach)?

    2) When I manually execute "Puck.accel();" in the command line repeatedly (without moving the sensor at all), I get a huge range of output. Why is "Puck.accel();" returning such a variation in values?

    Sometimes I'll just get a dead response and have to cycle the power to fix:

    >Puck.accel()
    ={
      acc: { x: -16056, y: -5125, z: -151 },
      gyro: { x: 0, y: 0, z: 0 }
     }
    >Puck.accel()
    ={
      acc: { x: -16056, y: -5125, z: -151 },
      gyro: { x: 0, y: 0, z: 0 }
     }
    >Puck.accel()
    ={
      acc: { x: -16056, y: -5125, z: -151 },
      gyro: { x: 0, y: 0, z: 0 }
     }
    

    And sometimes the gyro output seems totally random (without physically moving the Puck at all):

    >Puck.accel()
    ={
      acc: { x: -8001, y: 2134, z: 107 },
      gyro: { x: 200, y: -9329, z: -14452 }
     }
    >Puck.accel()
    ={
      acc: { x: -7988, y: 2153, z: 101 },
      gyro: { x: -949, y: -5207, z: -12287 }
     }
    >Puck.accel()
    ={
      acc: { x: -7973, y: 2136, z: 115 },
      gyro: { x: -1532, y: -5256, z: -12355 }
     }
    >Puck.accel()
    ={
      acc: { x: -8023, y: 2158, z: 123 },
      gyro: { x: -4909, y: -13612, z: -11337 }
     }
    

    Thanks!

  • Sun 2020.10.04

    'output seems totally random'

    There is a note concerning the mag, presumably the gyro also:

    http://www.espruino.com/Puck.js#magnetom­eter

    Has E.getBattery() been used to determine the amount of battery charge remaining?


    http://www.espruino.com/Puck.js#accelero­meter-gyro

    Are the same flutuations observed when using the low power module?

    source:    http://www.espruino.com/modules/puckjsv2­-accel-movement.js

  • Thanks for the tip regarding the battery charge. However, E.getBattery() returns a value of 100. I actually have the Puck wired to two new 1.5v AAA batteries.

    Unfortunately, using the puckjsv2-accel-movement.js module also returns the same unpredictable results.

  • 1) Is counting revolutions this way even possible (versus using a more traditional optical encoder or magnetic sensor approach)?

    Yes, however I'd say only if what you're attaching the Puck to is moving slowly. Since you're unlikely to be able to get the accelerometer at the centre of rotation, at higher speeds the centrifugal force could cause issues.

    2) When I manually execute "Puck.accel();" in the command line repeatedly (without moving the sensor at all), I get a huge range of output. Why is "Puck.accel();" returning such a variation in values?

    You mean for the gyro? The accelerometer itself looks pretty good.

    In the gyro case I think it's an issue with the accelerometer - basically because Espruino turns the accelerometer on, then takes the first reading, then turns it off, the gyro doesn't get time to 'settle' before a reading is made.

    I bet if you run Puck.accelOn() first (to leave the accelerometer on), and then use Puck.accel() you'll get much better readings. What I'd suggest is you just respond as soon as you get accelerometer data (rather than polling):

    Puck.on('accel', function(d) {
     // ...
    });
    Puck.accelOn(12.5);
    

    The other thing you could try for counting revolutions is the magnetometer. For instance you could stick a magnet on whatever is rotating and then have the Puck next to it. You could use the XYZ values from the magnetometer to get a pretty good idea of absolute position.

  • Thanks Gordon, I was able to get better readings from the IMU after using Puck.accelOn()

    The application I'm working on is a bit unique. I need to count revolutions of a spindle with the Puck mounted inside the spindle itself. I can't use any external reference source like a magnet or light to count the revolutions. The spindle will spin infrequently, and at varying velocities (think of pulling a length of paper from a roll).

    I'm hoping the gyroscope might be a reliable source of data for this application (spinning on the z axis). If I can get the gyro to reliably output scaled values in degrees or revolutions per second, would it just be a matter of dividing the the output by the amount of time that it spun? Or is the math not that simple?

  • Ahh, interesting. Try this:

    Puck.accelOn();
    Puck.accelWr(0x11, 0b00011100); // scale to 2000dps
    
    var d = 0;
    Puck.on("accel", a=> d += a.gyro.z/64000);
    
    setInterval(function() {
      print(d.toFixed(2));
    }, 500);
    

    If you rotate the Puck around like it's a knob then it will do a pretty convincing job of counting rotations (I can't promise it's actually calibrated in any way though). However it will wander off over time because the gyro does have noise.

    How is the spindle mounted? Because if it's horizontal then you've also got gravity, so you could detect when the Puck hasn't moved for a while and then use gravity to get an 'absolute' reading for rotation...

    This one seems to work reasonably well - it uses a slightly higher data rate of 26Hz which I think works better:

    Puck.accelOn(26); // 26Hz
    Puck.accelWr(0x11, Puck.accelRd(0x11)|0b00001100); // scale to 2000dps
    
    var d = 0, lastAccel;
    var timeStationary = 0;
    Puck.on("accel", r=> {
      lastAccel = r;
      d -= r.gyro.z/128000;
      var a = r.acc;
      a.mag = a.x*a.x + a.y*a.y + a.z*a.z;
      a.ang = Math.atan2(a.y,a.x)/(2*Math.PI);
      if (a.mag < 66000000 || a.mag > 71000000) {
        timeStationary = 0;
      } else {
        if (timeStationary<100) timeStationary++;
        else {
          // if stable for a while, re-aligh turn count
          var nearest = Math.round(d)+a.ang;
          d = d*0.8 + nearest*0.2;
        }
      }
      
    });
    
    setInterval(function() {
      print("rotation",d.toFixed(2), 
            "abs",lastAccel.acc.ang.toFixed(2), 
            "stationary", timeStationary);
    }, 500);
    

    However with that kind of thing it's drawing around 1mA, so you could expect a battery life of around 10 days. You might also want to consider combining it with the movement detection on http://www.espruino.com/Puck.js#accelero­meter-gyro to get it to enter a low power sleep mode when nothing is happening.

  • Wow! Thanks, Gordon. Both of those examples seem to be working well. A couple questions:

    1) In both examples, the gyro z-axis data is added to itself, then divided by a constant (64,000 or 128,000). Could you help me understand where you got those numbers from?

    2) The second example uses a.mag and a.ang... I don't see these referenced in the API docs. Is 'mag' using the magnetometer? If so, what for? Where do I find more info about these two properties?

    3) The math in the second example gets over my head. Any insight into that would be great (but don't feel obligated if there is not a simple way to explain it.)

    Thanks again!

  • I actually just added this to the Espruino App loader as well: https://espruino.github.io/EspruinoApps/­#puckrotate

    You can get the code by clicking the GitHub icon but the code has some power saving built in too which will really increase the battery life.

    1) In both examples, the gyro z-axis data is added to itself, then divided by a constant (64,000 or 128,000). Could you help me understand where you got those numbers from?

    Honestly, I started out calculating it based on 2000dps, 26 samples per second, and 360 degrees but then I just rotated the Puck by 360 degrees looked at the value I got, and divided by that - so it's almost certainly not exactly right.

    2) The second example uses a.mag and a.ang... I don't see these referenced in the API docs. Is 'mag' using the magnetometer? If so, what for? Where do I find more info about these two properties?

    They're created in line 10/11. Just added to the acceleration object because then I could see what they were by looking at lastAccel in the console.

    • mag is the size of the acceleration (squared). If the Puck isn't moving it should be the same as gravity (8192*8192 because of the scaling)
    • ang is the angle based on X and Y

    3) The math in the second example gets over my head. Any insight into that would be great (but don't feel obligated if there is not a simple way to explain it.)

    Yes, sorry - it was a quick hack so I didn't document it too well...

    // work out the rotation from the gyro - this line is all you need
    // if you don't care alignment with gravity
    d -= r.gyro.z/128000;
    
      var a = r.acc;
    // work out the magnitude of acceleration squared 
      a.mag = a.x*a.x + a.y*a.y + a.z*a.z;
    // work out the angle (as a value between 0 and 1)
      a.ang = Math.atan2(a.y,a.x)/(2*Math.PI);
    // if magnitude is more or less than gravity we assume we've moved
      if (a.mag < 66000000 || a.mag > 71000000) {
        timeStationary = 0;
      } else {
    // otherwise we start incrementing a counter
        if (timeStationary<100) timeStationary++;
    // when we've been stationary for 100 samples (~4 seconds)
    // start re-adjusting the value based on gravity
        else {
    // work out the nearest rotation value that matches with the rotation
    // we're getting based on gravity. 
    // a.ang is the fractional amount of rotation (-0.5 .. 0.5)
    // Math.round(d) is the nearest whole rotation
          var nearest = Math.round(d)+a.ang;
    // then we do a simple average (we don't set the value right away)
          d = d*0.8 + nearest*0.2;
        }
      }
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Puck.js v2 Accelerometer/Gyroscope output...

Posted by Avatar for BrianH @BrianH

Actions