support icon indicating copy to clipboard operation
support copied to clipboard

[Doc] Absolute vs relative angle when using Motor.run_target

Open dmusican opened this issue 1 year ago • 11 comments

Describe the issue Some students and spent some time recently confused about how Motor.run_target() is supposed to work, where we couldn't make sense of why the motor was moving as it did. I found #895 which was somewhat helpful. After reading that and playing around, I had a sudden revelation that run_target() moves towards relative angle, not absolute angle. This isn't explained anywhere explicitly in the documentation, except implicitly via an example at the bottom. A mention is made of a target angle, but both my students and I had assumed it would move towards absolute angle, and take the shortest path there. I now understand how it actually works and how our assumption was unwarranted, but issue #895 suggests that at least one other person also jumped to the same incorrect conclusion.

Suggested improvement It would help a lot, I think, if a precise explanation of relative and absolute angles would be provided in the documentation, and it could made clear that run_target() uses relative angle.

The documentation also makes reference to "target" angle, and "accumulated" angle, neither of which are defined either.

Throughout the documentation, careful attention could then be paid as to not use the word "angle" on its own without prefacing it with one of the above (when relevant).

This would involve a number of small changes throughout; if there's interest in seeing a global sort of change like this happen, I can try to update the entire page via a pull request. Alternatively, I can try to just recommend some local text for run_target if that is preferred.

How can I best help?

dmusican avatar Jul 29 '24 17:07 dmusican

Thank you for opening this issue and offering to help!

I had assumed it would move towards absolute angle

It does. The challenge with the terminology is that absolute and relative are both not the most helpful names.

For me personally, a relative angles implies going from angle A to B, by specifying the relative angle (B - A).

Likewise, to me, the word absolute implies going to some particular angle C, which is what run_target does.

So there may be different intuitive assumption on what absolute means. In that sense, adding this word in places isn't necessarily going to help. I would perhaps go as far as to say that there is no "relative angle" or "absolute angle". There are only angles, and "relative motion". There is also just one sensor in the device, so having two different concepts as LEGO has makes it more confusing than it needs to be.

There will be an entire chapter devoted to this topic in the new learn guide though, and some of that could also be included in the documentation. I've been thinking to add a few diagrams and examples to illustrate. Basically, the angle values are a line, not a circle, which is perhaps where some of the confusion comes from.

Mathematically, we have α ∈ ℝ, not α ∈ [0, 360]. Or visually (just plain text for now...):

- ∞, ...., -720, ..., -360, ..., 0, .... , 360, ...., 720, ... ∞

And the marker on the hub corresponds to 0. There is no such thing as "shortest distance" by going one way or the other. There is always just one way of going from A to B.

But indeed, for some applications where there are no gears on the motor, it can be better to go from -170 to -190 than from -170 to +170. That is to say, it matters where you pick B when you are at A. These are different motions, even though they end up at the same point on a circle in this subset of applications. Good illustrations should help here. I have a hunch that your students have run into one of such applications. :smile:

Edit: just to complete this note: For lack of a better alternative, the reported angle is initialized at α ∈ [-180, 179] when the program starts. This can create some confusion in programs where the main application is to operate precisely opposite to the starting point. You can reset the angle to a different value when this is impractical.

laurensvalk avatar Jul 29 '24 17:07 laurensvalk

Thanks for the response, and I take your point regarding the terms being counterintuitive and not agreed upon!

Your line analogy / description helps a lot. That's where my mental model was way off. Terminology aside, my students and I were thinking about the motor as a clock/circle, not as a line. After I understood what was going on, I also suggested to my students that they might think about it as a spool of string.

Regardless of terminology, my fundamental mental model was initially wrong, admittedly because of incorrect assumptions that I brought with me. I still suspect some choice words in the documentation would have helped to dispel that. Even just copying and pasting some version of your response above into the documentation would be helpful. Regardless, I'm glad to hear that this will information land in the new learn guide, that's fabulous.

As always, your work is very much appreciated!

dmusican avatar Jul 29 '24 22:07 dmusican

Some initial drafts on this topic!

I am not fully decided yet on where this should go. It is somewhat too detailed for a complete beginner. On the other hand, if someone has decided to switch to Pybricks, maybe they'll appreciate a bit of detail to build a strong foundation for everything else regarding motors.

image

image

laurensvalk avatar Sep 13 '24 09:09 laurensvalk

It would be even nicer to turn these into interactive animations. But one thing at a time, I guess.

laurensvalk avatar Sep 13 '24 09:09 laurensvalk

Honestly, this discussion just confuses things and i really think misses the point.

As a user of the API, if do reset_angle on a motor to 0 then all movements from that point onwards should be based on the "new zero" of the motor. what the true angle of the motor is should largely irrelevant at point. i think this how most users of the API would it expect it to behave and that is certainly what the kids on the my FLL team think

unfortunately this is not how the API is working based on our testing. we get inconsistent behavior with no clear reason as to why. Sometimes the different is 5-6 degrees or more.

we are using a geared motors (see attached pix) and consistently get inconsistencies in using run_angle to move the top attachment a specific number of degrees.

IMG_2257 IMG_2255

the start position of the attachment is next to the gray beam on top of the hub. makes it easier for the kids to always start in a known position. the "true" positionof the motor (the 0-0 alignment) is not known run to run.

any ideas?

bcfletcher avatar Dec 25 '24 03:12 bcfletcher

reset_angle(0) works exactly as you describe it should. Maybe you're running into a different question? Feel free to open a new discussion.

If your goal is to move a mechanism to specific target angles, run_target works well for this.

laurensvalk avatar Dec 25 '24 03:12 laurensvalk

documentation suggestion for the run_* series of function. you might want to add a note that if you set wait to false then the function can not have an "await" statement in front of it otherwise you get a "TypeError: "NoneType" object isn't iterable" error.

bcfletcher avatar Dec 25 '24 04:12 bcfletcher

Btw, I got the following from Lego today in response to a bug and feature request for Lego Spike Python

“Sue commented:

Hi bcfletcher

Thank you very much for your feedback.

Unfortunately, we are not planning any new SPIKE features or significant changes, but we have sent the feedback to the developer team for consideration in the future.”

Well if you are a first lego league (FLL) team, if there was any remaining doubt about using pybricks… this should settle it. Looks like the is no real future in Lego Spike….

bcfletcher avatar Dec 26 '24 15:12 bcfletcher

tried using run_target. seems to be more reliable.

new question: is there a way to move using run_angle and run_target such that if the motor stalls it will release and not pause the program. we thought Stop.COAST might work but it doesn't release

bcfletcher avatar Dec 26 '24 19:12 bcfletcher

is there a way to move using run_angle and run_target such that if the motor stalls it will release and not pause the program.

See https://github.com/orgs/pybricks/discussions/1621

laurensvalk avatar Dec 26 '24 19:12 laurensvalk

Thanks I will try that. Have y’all thought about adding a Boolean for stall detect to run_angle and run_target?

I did the following:

async def isStalled(attachment) -> bool :

    if debugL2: print("isStalled()",attachment.stalled())
    while attachment.stalled() == False: 
        await wait(1)
    if debugL2: print("stalled")
    return attachment.stalled()
   

# basic move attachment with stallDetection
async def moveAttachment(attachmentStr,degrees,speed,resetAngle,stallDetect):

    if attachmentStr == "top": 
        attachment=topAttachment
    elif attachmentStr == "front":
        attachment=frontAttachment
    else:
        print("moveAttachment() - illegal attachment name of ",attachmentStr)
        return False

    if resetAngle == True: attachment.reset_angle(0)
    
    if stallDetect == False: 
        await attachment.run_target(speed,degrees,then=Stop.HOLD,wait=True)
    else:
        await multitask(attachment.run_target(speed,degrees,then=Stop.HOLD),isStalled(attachment),race=True)
    
    return True

bcfletcher avatar Dec 26 '24 22:12 bcfletcher