# Sensing wheel position with the accelerometer

**URL:** <https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076>\
**Category:** Show and Tell\
**Created:** [March 16, 2021, 12:50am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076 "2021-03-16T00:50:39Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 16, 2021, 12:50am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/1 "2021-03-16T00:50:39Z")

</div>

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!

[/uploads/default/original/1X/510a94600e7c44f6d024c1eff0588374c5f2a85b.mp4](https://forum.electromage.com/uploads/default/original/1X/510a94600e7c44f6d024c1eff0588374c5f2a85b.mp4)

---

<div class="post-metadata">

**Author:** ![jeff](https://forum.electromage.com/user_avatar/forum.electromage.com/jeff/32/3473_2.png) [@jeff](https://forum.electromage.com/u/jeff)\
**Post date:** [March 16, 2021, 1:33am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/2 "2021-03-16T01:33:12Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![wizard](https://forum.electromage.com/user_avatar/forum.electromage.com/wizard/32/4_2.png) [@wizard](https://forum.electromage.com/u/wizard)\
**Post date:** [March 16, 2021, 1:52am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/3 "2021-03-16T01:52:26Z")

</div>

Wow, that is super cool!

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 16, 2021, 2:28am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/4 "2021-03-16T02:28:47Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [March 16, 2021, 1:58pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/5 "2021-03-16T13:58:30Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 18, 2021, 11:36am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/6 "2021-03-18T11:36:43Z")

</div>

@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!

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [March 18, 2021, 12:01pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/7 "2021-03-18T12:01:30Z")

</div>

> **[Centripetal vs Centrifugal Force: What's the Difference & Why it Matters](https://sciencing.com/centripetal-vs-centrifugal-force-whats-the-difference-why-it-matters-13721035.html)**
>
> A typical misconception is that centripetal force is directed toward the center of an object's circular path, while centrifugal force is directed outward, as though the two are opposites. However, only one of these is actually a real force!...

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?

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 18, 2021, 12:45pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/8 "2021-03-18T12:45:49Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Scruffynerf](https://forum.electromage.com/letter_avatar_proxy/v4/letter/s/34f0e0/32.png) [@Scruffynerf](https://forum.electromage.com/u/Scruffynerf)\
**Post date:** [March 18, 2021, 2:37pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/9 "2021-03-18T14:37:36Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 18, 2021, 2:57pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/10 "2021-03-18T14:57:52Z")

</div>

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!

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [March 31, 2021, 10:18pm UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/11 "2021-03-31T22:18:11Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![daderaide](https://forum.electromage.com/letter_avatar_proxy/v4/letter/d/e9bcb4/32.png) [@daderaide](https://forum.electromage.com/u/daderaide)\
**Post date:** [June 8, 2023, 2:16am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/12 "2023-06-08T02:16:56Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![daderaide](https://forum.electromage.com/letter_avatar_proxy/v4/letter/d/e9bcb4/32.png) [@daderaide](https://forum.electromage.com/u/daderaide)\
**Post date:** [June 8, 2023, 5:47am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/13 "2023-06-08T05:47:45Z")

</div>

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.

```auto
/*
 * 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)
  }
}

```

---

<div class="post-metadata">

**Author:** ![john1](https://forum.electromage.com/letter_avatar_proxy/v4/letter/j/e9bcb4/32.png) [@john1](https://forum.electromage.com/u/john1)\
**Post date:** [July 28, 2024, 6:09am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/14 "2024-07-28T06:09:11Z")

</div>

@Irwin @daderaide Curious if this progressed any further? My use case is similar, but also want to explore added static images on the spokes.

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [July 28, 2024, 10:16am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/15 "2024-07-28T10:16:09Z")

</div>

Funny you should mention this…

My bike with all of the Pixelblazes, LEDs, batteries, etc was stolen at Burning Man last year (i’m not sure why - it was just a cheap cruiser), and I’m currently working on re-building the lighting system using the new Pico w/6-axis controller. Hoping to finish in time for this year!

I plan to use [this tutorial](https://howtomechatronics.com/tutorials/arduino/arduino-and-mpu6050-accelerometer-and-gyroscope-tutorial/) on the 6-axis controller, and the new “pattern rotate” function of the PB.

I’ll try to update this thread with my results after the burn.

---

<div class="post-metadata">

**Author:** ![john1](https://forum.electromage.com/letter_avatar_proxy/v4/letter/j/e9bcb4/32.png) [@john1](https://forum.electromage.com/u/john1)\
**Post date:** [July 31, 2024, 6:15am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/16 "2024-07-31T06:15:15Z")

</div>

@Irwin That’s absolutely brutal, I’ve heard similar stories and am planning to lock my bike. I have the same exact use case for Burning Man, let me know if you want to share tips/code. I can update this thread if I get something functional.

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [July 31, 2024, 9:58am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/17 "2024-07-31T09:58:28Z")

</div>

Thanks, John. My bike was actually locked, parked on the Esplanade for a couple of days because I couldn’t even walk it home in the mud. Came back on Monday and it was gone.

If you haven’t already seen it, there’s recent [pattern](https://electromage.com/patterns) “Accelerometer level example” with an example of the rotation function. It has an issue with the angle wrapping through the 0/360 boundary but I think that can be solved.

---

<div class="post-metadata">

**Author:** ![john1](https://forum.electromage.com/letter_avatar_proxy/v4/letter/j/e9bcb4/32.png) [@john1](https://forum.electromage.com/u/john1)\
**Post date:** [August 2, 2024, 4:40am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/18 "2024-08-02T04:40:23Z")

</div>

Oh man, sorry that happened! And thanks for the tip, I hadn’t seen that pattern. I’ll play around with it.

---

<div class="post-metadata">

**Author:** ![Irwin](https://forum.electromage.com/user_avatar/forum.electromage.com/irwin/32/2228_2.png) [@Irwin](https://forum.electromage.com/u/Irwin)\
**Post date:** [August 11, 2024, 4:50am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/19 "2024-08-11T04:50:51Z")

</div>

I’ve come up with some preliminary code where I modified the “Accelerometer level example” pattern to use the gyro in the Pico 6-axis accelerometer, and it’s quite simple! The correction factor on line 14 is not really required for my purposes, but it gives better accuracy over long periods time. I’m using the Z axis.

Note that by using the gyro instead of the g-force values, there is no need to compensate for the g-forces generated by the spinning wheel.

```auto
// Modified from "Accelerometer level example" pattern

export var sixAxis
export var gyro
export var currentAngleDegs
export var currentAngleRads

export function beforeRender(delta) {
  
  // get the current value of the Z gyro
  gyro = sixAxis[5]
  
  // add error correction (varies by device)
  gyro += -0.9

  // calculate the current wheel angle in degrees
  currentAngleDegs = mod (currentAngleDegs + ((gyro * (delta) / 1000)), 360)
  
  // convert angle from degrees to rads
  currentAngleRads = currentAngleDegs * (PI / 180) 

  //you may need to reverse this angledepending on the 
  //sensor board orientation relative to the LEDs
  //currentAngleRads = -currentAngleRads
  
  //apply this angle as a rotation
  //first reset the transform
  resetTransform ()
  //center coordinates so we rotate around the center, not the corner
  translate (-.5, -.5) 
  //rotate by the correction angle
  rotate (currentAngleRads)
}

export function render2D(index, x, y) {
  //draw a vertical line
  var v = clamp (1 - abs (y * 5), 0, 1)
  hsv (x + .5, 1, v * v)
}

export function render(index) { hsv(0,1,1); } // all red

```

---

<div class="post-metadata">

**Author:** ![john1](https://forum.electromage.com/letter_avatar_proxy/v4/letter/j/e9bcb4/32.png) [@john1](https://forum.electromage.com/u/john1)\
**Post date:** [August 22, 2024, 6:41am UTC](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076/20 "2024-08-22T06:41:35Z")

</div>

Just gave this a go, it works great! Are you planning to port other patterns into this code? I’m going to fool around with some of the 1D patterns and see if tI can fit them into this structure. I’ll report back.

[Next page](https://forum.electromage.com/t/sensing-wheel-position-with-the-accelerometer/1076.md?page=2)
