-
-
Notifications
You must be signed in to change notification settings - Fork 7
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] Basic IMU heading support. Feedback wanted! #989
Comments
This was requested/discussed by:
Please give this a go if you have time and tell us what you think. 😄 Thank you! If this kind of heading support is "good enough", we can start from here and add full 3D support later. |
This is exactly what the team needs to use their turn-by-gyro function. Here is what it looks like in their LEGO code:
We are more than happy to test |
Taking things a step further, we can optionally let users use the gyro with the existing drivebase methods. (This is not included in the build above yet, but it works quite well). For example, when driving straight or turning, you'd get automatic corrections like this: VID_20230316_141516.mp4We could add a keyword argument It would probably default to We could also accept a callable like |
YES!!!!! We were using a drive straight function as well. Between the two it would give the kids the functionality they had before, plus all the additional stuff Pybricks lets them work with. |
The download links above have now been updated to include the drivebase-with-gyro feature. You can use it as shown below. from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase
# Initialize both motors. In this example, the motor on the
# left must turn counterclockwise to make the robot go forward.
left_motor = Motor(Port.A, Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B)
# Initialize the drive base. In this example, the wheel diameter is 56 mm.
# The distance between the two wheel-ground contact points is 112 mm.
drive_base = DriveBase(
left_motor,
right_motor,
wheel_diameter=56,
axle_track=112,
positive_direction=Direction.COUNTERCLOCKWISE,
use_gyro=True)
# Drive in a straight line and back again a few times.
for i in range(4):
# Drive forward for 500 mm.
drive_base.straight(500)
# Turn around counterclockwise.
drive_base.turn(180)
# Drive forward for 500 mm.
drive_base.straight(500)
# Turn around clockwise.
drive_base.turn(-180) |
OK, this is fun. 😄 This is the almost the same program as above but with higher speed/acceleration: drive_base.settings(
straight_speed=500,
straight_acceleration=1000,
turn_rate=500,
turn_acceleration=2000
) VID_20230316_205400.mp4 |
@laurensvalk I LOVE how accurate and fast that is moving!!!! I updated the firmware to the latest one (the gyro worked grate in the previous one BTW), and added the arguments to DriveBase:
However, I get Here is the full block:
|
You have to use the DriveBase with We'll enable both directions both ways at a later stage. For now this restriction made it easier to implement while we work out what to do with the gyro (not just multiple directions, but also multiple axes, etc.) |
Makes sense. When I made the functions earlier that used
to reverse it for me in the functions. |
I am no longer at school (retired a decade) but tried anyway. To test I placed the hubs on the armrest of my chair. This is fun-stuff. Bert |
Jumping in excitement also counts as simulating the ambient acceleration/vibration of an FLL final 😄 Thanks for testing Bert! As the converse challenge, how high you can make them such that it still always shows red when you try to move it? |
Users can now change positive turn direction to counterclockwise, which is more common in engineering applications. See pybricks/support#989
The team did some testing today. Our meeting room is on the 3rd floor and it is very creeky. In order to walk around the robot table and have the light stay green our settings were As for the gyro feature built into the hub, we ran into a little snag. The kids launched the robot from home. It removed the energy cell from the hydro dam, goes back home, and then the kids reposition it to start the next mission. The robot wants to stick with the last heading and will turn as such. This is adding
It has the same downside of wanting to go back to the last 0° heading. gyro_test_small.mp4 |
Did you try the |
No, but after rereading the documentation that would make sense. It uses the degree being read at the time of the |
While resetting the heading after you've moved the robot manually is indeed very important, there's something else going on here: By default, any drive base command will use This is usually great in between consecutive moves because it preserves accuracy, avoiding error buildup. (@TheWendyPower, this is one reason why you don't need that 0.5 second pause between moves you talked about). But if you turn 45 degrees like in this video and lift it from the table, it is still going to try to get back to 45 when you put it down. To prevent that, just call So generally, do: hub.imu.reset_heading(0)
do_mission_one()
drive_base.stop() # <--- This will stop the robot from trying to keep up with the last commanded gyro angle
# prep robot for next mission
# put it back down / position it / wait for button
hub.imu.reset_heading(0)
do_mission_two()
drive_base.stop()
# prep robot for next mission
# put it back down / position it / wait for button
# and so on
|
That said, amazing to see the new gyro feature on a real FLL robot already! Go team! 😄 |
Assume that the variable used for these noise settings are 2 byte int. Running with Spike Prime hub with It is a matter of how fast you move the hub. Automated (more or less)Sorry for the long post. Run the turntable with program turn_test.py: from pybricks.hubs import InventorHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait
hub = InventorHub()
motor = Motor(Port.F)
print(motor.control.limits())
motor.control.limits(2000, 20000, 20000)
print("using control.limits(speed, acceleration, torque)", motor.control.limits())
while True:
motor.run_angle(700, 90)
motor.stop
wait(1000)
motor.run_angle(250, -90)
motor.stop
wait(1000) Test the IMU with the Spike Prime hub with: from pybricks.hubs import PrimeHub
from pybricks.parameters import Color
from pybricks.tools import wait, StopWatch
hub = PrimeHub()
clock = StopWatch()
gyro_noise_top = 28000
accelerometer_noise_top = 18000
# print header:
print("gyro_ \taccel")
print("noise \tnoise")
# start with a few seconds NOT measuring
clock.reset()
while clock.time() < 2000:
wait(100)
for accelerometer_noise in range(accelerometer_noise_top, 11000 - 1, -500):
# range (start, stop, step)
for gyro_noise in range(gyro_noise_top, 24000 - 1, -2000):
hub.imu.debug(gyro_noise, accelerometer_noise)
wait(10)
red = 0
seen_green = False
clock.reset()
# do at least for 4 seconds, so the turntable did two "shakes"
while clock.time() < 4000:
if hub.imu.stationary():
hub.light.on(Color.GREEN)
seen_green = True
else:
hub.light.on(Color.RED)
red += 1
print(gyro_noise, "\t", accelerometer_noise, "\t", "red seen", red, "times, green seen:", seen_green)
wait(250)
print("program ended")
hub.speaker.beep(70, 50) Resulting in:
I wonder why some observations do not get "red".
Maybe the measurements are not very well synchronized with the movements of the turntable? Hope it helps a bit. And if not I had fun with it. 😉 |
@laurensvalk One thing I was wondering about is that as I understand it the hub has three axis gyros so would it be possible for users to pick a different axis to use. Some robots may be using a different orientation. Currently I am using my hub in a "Blast" robot. Thanks for the hard work on this. Really appreciate it. |
Try reducing the gyro sensitivity while keeping the acceleration sensitivity higher.
You can do this by passing the orientation to the hub constructor, e.g |
@dlech |
As Laurens said earlier:
|
Thanks @johnscary-ev3!
That's exactly the trade off I was hoping to get some input on, but I probably didn't explain it very well 😄 It sounds like the current defaults are working for everybody. We'll probably increase them slightly and hard code those in the firmware so nobody else has to worry about picking values. Selecting an arbitrary axis and positive direction should be possible fairly soon. Stay tuned :) |
Thanks @laurensvalk |
@laurensvalk |
Based on user feedback, the current defaults are good enough. pybricks/support#989
The Rockin' Robots tried Question 1. These are their results: Based on our experience, we think it would be good for teams to be able to manually set their gyro_noise and accelerometer_noise if they need to. Our regional and semi-finals competitions are in high school gymnasiums that likely have a lot of vibration. Our state championships are in a convention center that has cement floors. It likely has less vibration. Thank you for doing this work! We only need single directional gyro information so we know which direction the robot is pointing on the FLL Challenge table. We have tried PyBricks on our Spike Prime but found that it doesn't recover when it goes off track. With the LEGO firmware we are able to get it to recover by checking the gyro and rerouting ourselves. |
Thanks for the feedback, this is really helpful! With the heading() method available as in question 2, is your robot able to recover using Pybricks as well, or did you find some issues with this method? Thanks! |
We will have to do some testing and coding during our meeting on Sunday to answer that question. Here is our "drive straight" function from this season. We check the yaw value constantly while we are driving so if the robot hits something it will straighten itself out. It also works really well when there is a hair on the wheel that causes the wheel to slip a lot. We ignore how much the wheel has turned and focus on the direction the robot is facing. We need to figure out how to incorporate the PyBricks drive_base.straight() with the gyro checks. We love how much faster and accurate the PyBricks robot is compared to the LEGO robot. We had a series of competitions between two identical robots with the two different firmwares and it made us VERY excited to use PyBricks next year! |
Thanks for the update.
While you could do the same as you did above (using If you use Here's a video to demonstrate that it can be quite effective. This is just a single VID_20230316_141516.mp4 |
Based on user feedback, the current defaults are good enough. pybricks/support#989
Based on user feedback, the current defaults are good enough. pybricks/support#989
A similar debug function was previously dropped. However, users have requested to keep this control available in [1]. This makes a setter available in proper units, and sets defaults. [1] pybricks/support#989
I tried to do that with a |
Can you share a code sample and a video that reproduces that behavior? You can just attach the video here. |
We added the use_gyro=True and now our code runs the same as our LEGO-based code. Only significantly more quickly and accurately. Note: we haven't spent time fine-tuning the Pybricks code while we spent months fine-tuning the LEGO code. So there is a ton of room for improvement in the Pybricks run. Compressing the videos to less than 10MB was challenging so we put them on a google drive. https://drive.google.com/drive/folders/1jyi3Dk_I8OmWCnQdgSpUm3Tnm6N6C4Nd?usp=share_link We tested this route because it has a large attachment that extends on the left so it is more challenging for the robot to drive straight. This route starts by going forward 10cm then turning 14 degrees to the right. Our logging shows that it never turns the full 14 degrees. It only turns 10-12 degrees and if varies up to 1.8 degrees. Here is our logging from 8 repetitions: Move: 100 |
That's awesome, thanks for sharing! 😄
The drive base (with or without gyro) has a tolerance that defines when a move is "done". This normally ensures that:
These tolerances are all configurable if you want to dig deeper. Since this is independent of the gyro, it might be better to start a separate discussion about this if you're interested. You can also make your own functions as you did previously, where you essentially set your own tolerances. |
@laurensvalk
|
I can confirm.
So +179 not -179. Running (older firmware) on the inventor hub:
[EDIT] updated inventor hub output |
Thanks @johnscary-ev3 and @BertLindeman! Proper gyro+accelerometer support is currently in progress. This issue was opened mainly to get some feedback on the heading concept. Since the response was so positive, we're now in the process of getting this merged in properly (without breaking the accelerometer 😉) If you can't wait, you can follow our progress in pybricks/pybricks-micropython#156. We'll keep this issue open for now, and ask everyone to move over to the public beta once we've released it :) |
Based on user feedback, the current defaults are good enough. pybricks/support#989
A similar debug function was previously dropped. However, users have requested to keep this control available in [1]. This makes a setter available in proper units, and sets defaults. [1] pybricks/support#989
@laurensvalk By the way, I have a comment about the new calls being called "heading". from pybricks.hubs import PrimeHub
from pybricks.parameters import Color
from pybricks.tools import wait
from pybricks.geometry import Axis
# Get Heading Angle from Yaw Angle
def get_heading_angle(yaw_angle):
if yaw_angle <= 0:
heading_angle = (-yaw_angle % 360)
else:
heading_angle = 360-(yaw_angle % 360)
return(heading_angle)
#hub = PrimeHub()
# For example, this is how the hub is mounted in BLAST in the 51515 set.
hub = PrimeHub(top_side=Axis.X, front_side=-Axis.Y)
#hub.imu.debug(gyro_noise=20, accelerometer_noise=100)
while True:
if hub.imu.stationary():
hub.light.on(Color.GREEN)
else:
hub.light.on(Color.RED)
# Get heading.
#
# FEEDBACK WANTED, Question 2: Is this good enough? :-)
#
yaw_angle = hub.imu.heading()
heading_angle = get_heading_angle(yaw_angle)
# Display heading on the LED matrix.
hub.display.number(round(yaw_angle))
# Print heading and wait a moment to we don't print so much.
print('yaw_angle= ',"{:.1f}".format(yaw_angle), 'heading_angle= ',"{:.1f}".format(heading_angle))
wait(25)
# You can easily reset the heading to arbitrary values.
# No special wait operations are required here. Just reset and go.
if hub.buttons.pressed():
hub.imu.reset_heading(0) |
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor,
from pybricks.parameters import Button, Color, Direction, Port, Side, Stop, Icon
from pybricks.robotics import DriveBase
from pybricks.tools import wait, StopWatch
hub = PrimeHub()
motorl = Motor(Port.A, Direction.COUNTERCLOCKWISE)
motorr = Motor(Port.B)
motorl.control.limits(acceleration=[2000, 400])
motorr.control.limits(acceleration=[2000, 400])
drive_base = DriveBase(
left_motor=motorl,
right_motor=motorr,
wheel_diameter=55,
axle_track=143,
positive_direction=Direction.COUNTERCLOCKWISE,
use_gyro=True)
drive_base.settings(
straight_speed=500,
straight_acceleration=1000,
turn_acceleration=2000,
turn_rate=500
)
while True:
drive_base.straight(1000, then=Stop.NONE) This code concludes in this : 20230405_133825.mp4However, if you delete the straight speed in settings : from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor,
from pybricks.parameters import Button, Color, Direction, Port, Side, Stop, Icon
from pybricks.robotics import DriveBase
from pybricks.tools import wait, StopWatch
hub = PrimeHub()
motorl = Motor(Port.A, Direction.COUNTERCLOCKWISE)
motorr = Motor(Port.B)
motorl.control.limits(acceleration=[2000, 400])
motorr.control.limits(acceleration=[2000, 400])
drive_base = DriveBase(
left_motor=motorl,
right_motor=motorr,
wheel_diameter=55,
axle_track=143,
positive_direction=Direction.COUNTERCLOCKWISE,
use_gyro=True)
drive_base.settings(
straight_acceleration=1000,
turn_acceleration=2000,
turn_rate=500
)
while True:
drive_base.straight(1000, then=Stop.NONE)` It concludes in the normal behaviour : 20230405_133902.mp4 |
Any news about my issue ? |
Thank you, I was able to reproduce it. I will open a separate ticket for this. |
OK, I have tried most of the code samples about, and for the life of could not get the robot to turn square (90, 180, 270, 360) Removed the Spike Prime hub and run the Yaw/heading output code against a square edge, rotating through all angles. Still out, and the same on 2 other hubs. I'm getting an accumulated error in my heading values of by an average -0.975 per degree out, on all our Spike Prime Hubs (3). One sample below |
Thanks @BrBarry! We have since made considerable improvements that should get it a lot closer to 360 degrees. We will be publishing a proper beta release next week (i.e. not hidden here in a GitHub discussion). We didn't publish anything this week because the FLL finals are happening right now, and we didn't want any teams to run into accidental changes during their finals. Even after these improvements, there will still be some variations between hubs. If needed, we might address that with a calibration routine that people can run on their hubs. |
All functionality discussed here is now available via https://beta.pybricks.com/. Just install the firmware with the usual steps; no custom files required. Some implementation details have changed based on your feedback. Please consult the documentation in app via the link above for using the gyro and drive bases. If you've been using the experimental versions in this thread, here are the most important updates:
Thanks everyone for your feedback! We will now archive this issue. Please feel free to open new discussions and issues if anything comes up. Also feel free to open a discussion if it "just works". That helps us too 😄 |
Update: all functionality is now available for use via https://beta.pybricks.com/
The links and functionality described below is outdated and should not be used anymore.
Is your feature request related to a problem? Please describe.
We get a lot of requests for gyro support. While full 3D attitude estimation or heading in arbitrary reference frames is still a long way off, there is perhaps something we can do to start with that works for certain use cases.
In this case, I am talking about the use case of drive bases (FLL/WRO), where the hub is flat (parallel to a horizontal table) most of the time.
We want your help to test it! (Preparation step)
Download this experimental firmware:
Installing it works just like installing Pybricks normally. But on the hub selection page, you now click Advanced to select the zip file you just downloaded:
We want your help to test it! (Question 1)
For the exciting stuff, skip to part 2. But we'd still like to get your feedback on part 1 :)
We'd love to get your help to pick certain sensible defaults so the gyro works for everyone. Please run the program below and read through the explanations in the comments. Increase the two noise values until you think it works well enough under all expected conditions.
Once we've settled on good values, others should not need to worry about it (and we'll remove this
debug
method).We want your help to test it! (Question 2)
Now for the exciting part!
NB: Positive is counterclockwise
A positive angle is a counterclockwise turn. This is consistent with most engineering definitions.
The DriveBase defaults to positive for clockwise to be similar to the LEGO apps. We will add an option to flip this. See the drive base examples further down in this thread.
That way, your drive base direction and gyro values will match, saving you some headaches.
NB: For now it only works in horizontal orientation
We have not yet implemented 3D attitude estimation. So if you lift the robot from the table and put it back, it won't give the same value.
It is only implemented for the default orientation, where the hub is parallel to the table.
This might not be so bad; after all, the EV3 also had just one gyro axis. But we'd love to get your thoughts.
The text was updated successfully, but these errors were encountered: