-
-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Builtin 1D heading self-correction routine #1678
Comments
Would this help your students, @afarago , @DrTom, @ggramlich, @harikun77, @thehomelessguy, @MonongahelaCryptidCooperative, @TheWendyPower? Or would it be more confusing than helpful, even if it is optional? How did you previously correct for the gyro being precise, but not accurate (off by about +/- 1%)? Do your students notice it at all? Have they noticed that this varies from hub to hub? As always, thank you for any feedback! |
Yes, it has been noticed. It can be a problem but most times it is not an major aspect. At some point during a run some drift accumulates and I believe that causes quickly a larger problem than the systematic error. Currently one team compensates for this problem; the others I am quite sure didn't even notice. That said. It would be nice if a correction factor could be supplied when creating a hub along with the parameters for the axis. I do not think a build in, interactive process is necessary. The drivebase has to be manually calibrated with respect to wheel size and track width. It seems to me this is similar to that. Some good documentation would be helpful though. |
One of my teams noticed yet due to other uncertainties we agreed to limit the "uncontrolled" turns to max ~360 degrees. This way the offset was eliminated. Afterwards a wall align or clean start is required. I completely agree with @DrTom any built-in procedure to the firmware seems unnecessary. i think it is good to have a best practice (even a code) to measure though. Both in EV3 and SPIKE/RI it seemed that CCW and CW gyro shift was not uniform. Never had the time to really get to the bottom. |
Thanks for the feedback! The potential issue with putting the correction factor in the hub initialization, is that your code becomes suitable only for that one hub. I was thinking that storing it on the hub might solve this.
This is definitely a good approach. |
Finally I read the full proposal and I am not sure, if I get all the implications. Our team members all have their own hub and we noticed that fine tuning which was done by one team member did not work well on the other hubs. One cause probably was the inaccuracy of the gyro. Other causes were things like slightly different absolute positions of the motors for the arms. If it where possible to store a correction factor on the hub (with a reasonable default) that would be used with the drivebase useGyro() that would be awesome. Putting the correction factor as a value in the setup would be quite cumbersome for our use case, as the kids would always need to change it, when trying out the programs of their team mates. The process you proposed would be nice, but also a simpler way like giving a sample program to measure the deviation and then a function call (python only, no block coding needed for that) to store the correction factor in persistent storage would be fine. |
Based on the rest of your reply, I think you understood it exactly right. Thanks for the input!
This is indeed what I was concerned about, and why it would be nice to have the value stored. I see @DrTom's case of supplying a value directly also being useful. I think we can essentially have both. Since the "builtin routine" is likely going to be a Python script, that script will have to make a call to eventually save the result. If we expose that call in the API, anyone who wants to store their own value can do so, or even make their own automatic routine. Here is the script that we might include. I'd actually be curious for your findings! I have a few hubs that are reasonably spot on, but another that is quite far off. For that one I get:
It doesn't store the result yet. It just shows it to you. What do you get? You can run it on any Prime / Technic / Essential / Inventor Hub. Flat on the table is the easiest. But it can be in the robot if you make sure to keep all wheels on the table as you spin it around. from pybricks.hubs import ThisHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Button, Color, Direction, Port, Side, Stop, Axis
from pybricks.robotics import DriveBase
from pybricks.tools import wait, StopWatch
# Number of turns to confirm the result.
NUMBER_CONFIRM_TURNS = 5
# Maximum speed values before we consider the result invalid.
MAX_XY_SPEED = 30
MAX_Z_SPEED = 800
# Routine to wait on a button, with some extra time to avoid vibration directly after.
def wait_for_click():
while hub.buttons.pressed():
wait(1)
while not hub.buttons.pressed():
wait(1)
print("Processing...")
while hub.buttons.pressed():
wait(1)
wait(1500)
# REVISIT: Decide how to deal with non-standard orientation in case of the builtin routine.
hub = ThisHub()
hub.system.set_stop_button(None)
print("Put the hub on a flat table. Align against a fixed reference like a wall or heavy book. Press hub button when ready.")
# Wait for fixed reference and store the initial angle value.
wait_for_click()
while not hub.imu.ready() or not hub.imu.stationary():
wait(1)
start_z = hub.imu.rotation(-Axis.Z)
# Wait for a full rotation and get the result.
print("Keep the hub flat and slide it to make a full turn clockwise. Put it against the same reference. Press hub button when ready.")
wait_for_click()
one_turn = hub.imu.rotation(-Axis.Z) - start_z
# Require clockwise...
if one_turn < 0:
raise ValueError("You turned it the wrong way. Please try again.")
# Sanity check. Should be close to 360.
if not (350 < one_turn < 370):
print(one_turn)
raise ValueError("The error was more than 10 degrees, which is unexpected. Please try again.")
# Instruct to make more turns.
print("So far so good! Now make", NUMBER_CONFIRM_TURNS, "full clockwise turns.")
for i in range(NUMBER_CONFIRM_TURNS):
# The rotation target is just under a rotation so we can show the next
# message to keep going in time, avoiding doubts about what to do.
target = one_turn * (i + 2) - 10
# Wait until the hub reaches the target.
while hub.imu.rotation(-Axis.Z) - start_z < target:
wait(1)
if hub.buttons.pressed():
raise RuntimeError("Don't press the button until all turns are complete!")
if abs(hub.imu.angular_velocity(Axis.Z)) > MAX_Z_SPEED:
raise RuntimeError("Not so fast! Try again.")
if abs(hub.imu.angular_velocity(Axis.X)) + abs(hub.imu.angular_velocity(Axis.Y)) > MAX_XY_SPEED:
raise RuntimeError("Please keep the hub flat! Try again.")
# Inform user of status.
print("Completed", i + 1, "out of", NUMBER_CONFIRM_TURNS, "turns. ", end="")
if i < NUMBER_CONFIRM_TURNS - 1:
print("Keep going!")
else:
print("Put it against the same reference. Press hub button when ready.")
# Wait for final confirmation.
wait_for_click()
# Verify the result.
expected = one_turn * (NUMBER_CONFIRM_TURNS + 1)
result = hub.imu.rotation(-Axis.Z) - start_z
if abs(expected - result) > NUMBER_CONFIRM_TURNS / 2:
print("The", NUMBER_CONFIRM_TURNS, "extra turns where different from the first turn. Try again.")
print("Expected", expected, "but got", result)
# Get the final result to save.
average_turn = result / (NUMBER_CONFIRM_TURNS + 1)
multiplier = 360 / average_turn
print("For every 360-degree turn your gyro reports:", average_turn)
print("Correction factor:", multiplier)
# TODO: Save this value on the hub. |
The following proposal might have the best of both worlds. Green are new additions. This is the more technical, to-the-point method where you can set/save and get the value directly. With an extra setting in the existing Additionally, here is the simpler routine that most users can use. Possibly with a dedicated block. |
I have tested it an CW and CCW gives consistently different results even after checking with multiple hubs. Not a huge amount, still something that worth checking here imo. CW CCW |
Thank you for adding some data to this! Agreed it should be looked into as well. Still, cutting down an error of 3.6 degrees (1%) to about 0.36 degrees (0.1%) with the proposed method is already a pretty big step. 🙂 |
Yes, agreed, even the measurement itself surprised me, I never checked and being 6-7 degrees off was quite surprising! So this will be an awesome improvement for us, the pybricks users! |
Here are my values from one Spike Prime and two Robot Inventors (CCW values displayed positive) Spike
RI1
RI2
|
Excellent, nice to have some extra data! Because if we're finding a trend here, we could not only add the calibration routine but also guesstimate a baseline to improve the default for everyone. For bonus points, would anyone want to retry this with their team's ready-built robot? If your hub is flat, the result should be approximately the same. But it is a bit trickier to complete the routine successfully, so I wonder if this is too hard or acceptable. |
This is the data for our FLL competition robot this year. RI CW CCW |
Although one way to correct for this is by adjusting the top axis, running the calibration routine will also correct for this 🙂 |
You can try it out now! Using this firmware file for Prime Hub or Technic Hub or Essential Hub For @DrTom, there is a setting that lets you do it right below the hub initialization. For @ggramlich, there is an interactive routine to determine the value. In both cases, the value is saved on the hub in the same way. from pybricks.hubs import InventorHub
from pybricks.tools import vector, wait
# Initialize the hub. Optional: Choose a custom orientation
hub = InventorHub(top_side=vector(3, 0, 4), front_side=vector(-4, 0, 3))
# Optional: Set the value yourself.
hub.imu.settings(heading_correction=363.2)
# Optional: check the result.
print(hub.imu.settings())
# Run the update routine. This will give you instructions to update it interactively.
hub.imu.update_heading_correction() After that, and even after reboot, the following program should result in near-perfect 360-degree values if you make a full turn: from pybricks.hubs import InventorHub
from pybricks.tools import vector, wait
# Set up all devices. (Optional: Use the same custom orientation as before)
hub = InventorHub(top_side=vector(3, 0, 4), front_side=vector(-4, 0, 3))
# The main program starts here.
while True:
print(hub.imu.heading())
wait(100) Eventually, there might be blocks for this too. If your hub has the default orientation (flat), then you can also simply click the |
Is your feature request related to a problem? Please describe.
Gyro calibration for a typical navigation task has (at least) these two aspects:
357
or361
, which varies by hub. In other words, the gyro is quite precise, but not accurate (off by about +/- 1%)?Pybricks already implements 1 but not 2. Now that we have enabled persistent storage for settings, it is reasonable to implement 2 also.
Describe the solution you'd like
The goal is to obtain and store a correction factor so that one turn will result in 360 degrees. We could have a routine that interactively guides the user through these steps to find and save it.
This could be in a fancy window with animations and all, but we can start much simpler. For example, we could have the user run a script/block that will produce the following as you follow the instructions in the output window:
After doing this
hub.imu.heading(use_stored_correction=True)
will give you the adjusted value. Or maybe it should be the default.The value would persist until you update to a new firmware.
The value would only be useful for the hub orientation that was used to do the calibration.
The text was updated successfully, but these errors were encountered: