[Bug] DriveBase.distance() changes after resetting wheel motors' angles
Describe the bug
What is the problem?
Hi, I am coaching an FLL team and was testing the robot today using relative distances (passing target_distance - robot.distance() to DriveBase.straight()). I noticed that the robot sometimes moved a lot and other times it moved very little. Later today I was able to isolate the issue and found out that resetting the wheel motors' angles changed the measured distance value. I don't know if it's relevant but we use the SPIKE Prime.
To reproduce I made this program that shows that DriveBase distance changes without moving the motors:
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Button, Color, Direction, Port, Side, Stop, Axis, Icon
from pybricks.robotics import DriveBase
from pybricks.tools import wait, StopWatch, Matrix
hub = PrimeHub()
left_wheel = Motor(Port.B, Direction.COUNTERCLOCKWISE)
right_wheel = Motor(Port.A, Direction.CLOCKWISE)
robot = DriveBase(left_wheel, right_wheel, 62.4, 110)
print("Before reset:")
print("robot distance:", robot.distance())
print("left_wheel angle:", left_wheel.angle())
print("right_wheel angle:", right_wheel.angle())
print()
left_wheel.reset_angle(0)
print("After left_wheel reset:")
print("robot distance:", robot.distance())
print("left_wheel angle:", left_wheel.angle())
print("right_wheel angle:", right_wheel.angle())
print()
right_wheel.reset_angle(0)
print("After right_wheel reset:")
print("robot distance:", robot.distance())
print("left_wheel angle:", left_wheel.angle())
print("right_wheel angle:", right_wheel.angle())
print()
Which outputs this:
Before reset:
robot distance: 0
left_wheel angle: 97
right_wheel angle: 158
After left_wheel reset:
robot distance: -27
left_wheel angle: 0
right_wheel angle: 158
After right_wheel reset:
robot distance: -70
left_wheel angle: 0
right_wheel angle: 0
As you can see, the robot's measured distance is changed without even moving the motors.
Expected behavior I think the measured distance should stay the same after resetting the drivebase's motors.
Resetting distance after resetting the motors solves this issue but I spent some time figuring this out.
Thank you! I can see that this could be confusing.
Another way to resolve this would be to reset the motors before creating the drive base:
left_wheel = Motor(Port.B, Direction.COUNTERCLOCKWISE)
left_wheel.reset_angle(0)
right_wheel = Motor(Port.A, Direction.CLOCKWISE)
right_wheel.reset_angle(0)
robot = DriveBase(left_wheel, right_wheel, 62.4, 110)
Resetting distance after resetting the motors solves this issue but I spent some time figuring this out.
Maybe we should do this automatically, and add a note about it in the docs.
Related to this, maybe we should also reset the angle when switching to/from using the gyro.
Thanks ☺️
Another way to resolve this would be to reset the motors before creating the drive base:
left_wheel = Motor(Port.B, Direction.COUNTERCLOCKWISE) left_wheel.reset_angle(0) right_wheel = Motor(Port.A, Direction.CLOCKWISE) right_wheel.reset_angle(0) robot = DriveBase(left_wheel, right_wheel, 62.4, 110)
Even then, if the robot references itself with a wall (for example), resets distance, then resets motors, the measured distance will change too, right? This year, in my team's robot we sometimes want to turn with a single wheel, so we reset motors in the wall too (although I don't think it makes much sense to use relative angle here instead of gyro or absolute angle).
Thank you. I think we haven't given the individual motor resets combined with drive base resets enough thought.
I agree that some changes would be helpful, and we can certainly do so. We just have to figure out what's "right", and then find a way to implement it as well.
Possible approach: (feel free to suggest others!)
- when
reset_angleis used on a single motor, change the firmware so thatdrive_base.distance()andangle()are not affected. - drivebase.reset() does not affect the individual motor, since it isn't clear what they should be reset to.
Would this solve your use case?
That approach is exactly what I had in mind!! 😄
For solving our use case we have made a function similar to this to make sure we always reset distance after motors:
def reset_motors():
left_wheel.reset_angle(0)
right_wheel.reset_angle(0)
drivebase.reset()