Sensing wheel position with the accelerometer

My initial test of using the accelerometer to determine the rotational position of a bicycle wheel and keep the pattern stationary. Had to dust off my trigonometry skills for this one!

5 Likes

So cool! You’re going to look like you’re floating out there!

Wow, that is super cool!

Thanks guys! I still need to figure out the right formula for negating the added common centrifugal force on the X & Y axes when the wheel is turning. It becomes noticeable at higher RPMs.

Share the code and perhaps we’ll have some ideas.

@Scruffynerf I would love some help!

Here’s the code. Note that due to a mixup on my part, the y-axis is oriented backwards on my prototype, and you will see a line of code that compensates for that.

/*
 * wheel test - stationary rainbow
 */

export var accelerometer;

// exported for debugging
export var angle;
export var angle1;
export var x, y;

// the weight used in the accelerometer averaging/smoothing calculation
var weight = .95;

function rotation () {

  // read x and y from the accelerometer,
  // and use a weighted average to smooth out the noise
  x = (weight * x) + ((1 - weight) * accelerometer[0]);
//y = (weight * y) + ((1 - weight) * accelerometer[1]);           // this would be the way if y-axis wasn't reversed
  y = (weight * y) + ((1 - weight) * (accelerometer[1] * -1));    // fix for y-axis being reversed on my prototype

  // calculate the rotational angle of the wheel based on the x and y values
  // result of atan2 will be between (+PI) and (-PI)
  angle = atan2 (x, y);
  
  // normalize the angle to a value between 0 and 1
  angle1 = clamp (((angle + PI) / (PI * 2)), 0, 1);
}

// the beforeRender function is called once before each animation frame
export function beforeRender(delta) {
}

// the render function is called for every pixel
export function render(index) {

  // get the current rotational position of the wheel
  // this can be done in beforeRender, but there will be a bit of lag
  rotation ();

  // calculate the static rainbow hue for this pixel
  h = index / pixelCount;

  // add the hue offset based on the current position of the wheel
  h += angle1;
  
  hsv (h, 1, 1)
}

Thanks in advance!

So let’s clarify what’s happening when the speed increases, it’s getting some form of lag, because the acceleration values aren’t accurate enough anymore? Example?

Both the x and y axes are oriented to point from the hub to the outer rim of the wheel. But since the accelerometer is positioned a small distance outside the hub, when the wheel spins, an additional force is added in equal amounts to both the x any y axes. This is added to the normal force of gravity, and skews the atan calculation of the rotational angle. The visible result is that the angle calculation goes +/- from where it would be if the wheel was static, with one +/- cycle per revolution. The +/- deviation of course gets worse as the RPM is increased.

I think there must be a clever way to mathematically detect when this force is being added, and subtract it out of the x and y values.

Ah, I get it. The wheel rotation causes a force you want to measure and remove.

You consider X and Y to be centered at the hub, but your measurement device is not at the hub, it’s rotating as part of the device (PB sensor), and that’s throwing off the calculation with the extra data/input involved.

Measuring it (for example) at top and bottom of wheel rotation, in a static position, works but the extra forces while moving need to be controlled for. You might have to add a sensor for speed (of rotation) measuring.

One other option would be change where the sensor board is. It can be moved, doesn’t have to be next to the PB. Mount it next to the axis?

Exactly! The sensor board is mounted inside an enclosure which is mounted as close to the hub as physically possible, which puts the accelerometer chip only about 2" from the rotational axis of the wheel, but it’s still enough to create this problem.

Your idea for using rotational speed to determine how much force to remove is great. I might be able to accomplish that without additional sensors, using a calculation that involves the angle, and time, to determine instantaneous rotational speed. There would also need to be a fudge-factor based on wheel position to correct the instantaneous angle, but I think its mathematically do-able.

Thanks for your help!

Ok, I finally found the formula for negating the extra force on the x & y axes when the wheel is spinning. First, I measure the change in wheel angle (which I already calculate) vs time, to determine the angular velocity, and convert that to RPM.

This formula calculates the force, where R = distance of accelerometer from the center of the wheel in centimeters:

gForce = RPM^2 × 1.118 × 0.00001 × R

Then I normalize gForce to the 1g value of the accelerometer (0.02), and subtract it from both the x & y values read from the accelerometer. I found that R needs to be very accurate for it to work. Here’s the web page I found with the explanation:
https://www.sigmaaldrich.com/technical-documents/articles/biology/g-force-calculator.html

3 Likes

I know this is a bit of an older topic, but would you mind posting the rest of your code that compensates for the spin of the sensor board? I successfully got your code running on my build, but am having a lot of trouble figuring out how to implement the math from your last post. Hopefully you see this lol

Here is my attempt so far. It sort of works, but I think i’m doing something wrong. On my bike, I have the new firmware sending different code to the wheels and the body of the bike using the nodeID.

/*
 * wheel test - stationary rainbow
 */

export var accelerometer;

scale = pixelCount / 2


// the weight used in the accelerometer averaging/smoothing calculation
var weight = .95;

function rotation () {

  // read x and y from the accelerometer,
  // and use a weighted average to smooth out the noise
  x = (weight * x) + ((1 - weight) * accelerometer[0] );
  y = (weight * y) + ((1 - weight) * accelerometer[1]);           // this would be the way if y-axis wasn't reversed
   //y = (weight * y) + ((1 - weight) * (accelerometer[1] * -1));    // fix for y-axis being reversed on my prototype

  // calculate the rotational angle of the wheel based on the x and y values
  // result of atan2 will be between (+PI) and (-PI)
  angle = atan2 (x - normalGForce, y - normalGForce);
  
  // normalize the angle to a value between 0 and 1
  angle1 = clamp (((angle + PI) / (PI * 2)), 0, 1);
  
    angleInDegrees = angle * (180 / PI);    //convert radians to degrees
    rpm = angleInDegrees / 360;             //convert degrees to RPM
    gForce = rpm^2 * 1.118 * 0.00001 * 13;  //equation for getting gForce from rotation
    normalGForce = gForce * (0.02 / 1);     //normalize gForce to 1g = 0.02 for the sensor

}

// the beforeRender function is called once before each animation frame
export function beforeRender(delta) {
   t1 = time(.1)  // Time it takes for regions to move and melt 
}

// the render function is called for every pixel
export function render(index) {
  
  

  if(nodeId()===1){
    // get the current rotational position of the FRONT wheel
    // this can be done in beforeRender, but there will be a bit of lag
    rotation ();

    // calculate the static rainbow hue for this pixel
    h = index / pixelCount*2;
    
    // add the hue offset based on the current position of the wheel
    if(h>1) {h += angle1;}
    else{h-=angle1}
  
    hsv (h, 1, 1)
    
  }
  
  else if (nodeId()===2){
    // get the current rotational position of the REAR wheel
    // this can be done in beforeRender, but there will be a bit of lag
    rotation ();

    // calculate the static rainbow hue for this pixel
    h = index / pixelCount*2;
    
    // add the hue offset based on the current position of the wheel
    if(h>1) {h += angle1;}
    else{h+=angle1}
  
    hsv (h, 1, 1)
  }
  
  else{ 
    c1 = 1 - abs(index - scale) / scale  // 0 at strip endpoints, 1 in the middle
    c2 = wave(c1)
    c3 = wave(c2 + t1)

    v = wave(c3 + t1)  // Separate the colors with dark regions
    v = v * v

    hsv(c1 + t1, 1, v)
  }
}