Skip to content
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

Motor.run_to_position does not reach position #179

Open
Gadgeteering opened this issue Dec 3, 2022 · 19 comments
Open

Motor.run_to_position does not reach position #179

Gadgeteering opened this issue Dec 3, 2022 · 19 comments

Comments

@Gadgeteering
Copy link

Motor.run_to_position does not reach position requested

Motor C requested position -66 current position -76
Motor C requested position -66 current position -76
Motor C requested position -66 current position -76
Motor C requested position -66 current position -76
Motor C requested position -66 current position -76

I have tried to correct this behaviour but when I have used run_for_degrees(10) the motor does not move

@chrisruk
Copy link
Contributor

chrisruk commented Dec 5, 2022

Thanks for the report, I can replicate this too.

I'll pass this on to the firmware dev, to see if this can be improved in either firmware/software.

@chrisruk
Copy link
Contributor

chrisruk commented Dec 16, 2022

I just created a little script that compares the difference between the angle you're targeting vs the angle that is obtained after calling run_to_position.

With the default motor bias I got an average of around 2.8 degrees out from the desired value.

By increasing the motor bias to 0.4, the average was around 1.4 degrees out from the desired value.

(Note that the motor in my case didn't have any load attached)

#!/usr/bin/python3.9

import time
from buildhat import Motor

m = Motor('A')
m.bias(0.4)

sum_of_angle_differences = 0
angs = [-90, 90, 45, -45, 180, 0] * 5
for i in angs:
    m.run_to_position(i)
    time.sleep(1)
    pos1 = m.get_aposition()
    diff = abs((i - pos1 + 180) % 360 - 180)
    sum_of_angle_differences += diff
    print(diff)

print("avg ", sum_of_angle_differences / len(angs))

@Gadgeteering
Copy link
Author

Gadgeteering commented Dec 18, 2022

I have tried your suggestion with three motors, it works when there is no load on the motor. But when you put a mechanical load on the motor the results are similar to before.

from buildhat import Motor
import time

m1 = Motor('A')
m2 = Motor('B')
m3 = Motor('C')
m1.bias(0.4)
m2.bias(0.4)
m3.bias(0.4)
sum_of_angle_differences1 = 0
sum_of_angle_differences2 = 0
sum_of_angle_differences3 = 0
angs = [-90, 90, 45, -45, 180, 0] * 5
for i in angs:
   m1.run_to_position(i,speed=None, blocking=False)
   m3.run_to_position(i,speed=None, blocking=False)
   m2.run_to_position(i)
   time.sleep(1)
   pos1 = m1.get_aposition()
   pos2 = m2.get_aposition()
   pos3 = m3.get_aposition()
   diff1 = abs((i - pos1 + 180) % 360 - 180)
   sum_of_angle_differences1 += diff1
   diff2 = abs((i - pos2 + 180) % 360 - 180)
   sum_of_angle_differences2 += diff2
   diff3 = abs((i - pos3 + 180) % 360 - 180)
   sum_of_angle_differences3 += diff3
   print("Angle {} Motor delta A:{} B:{} C:{}".format(i,diff1,diff2,diff3))

print("avg  A:{} B:{} C:{}".format(sum_of_angle_differences1 / len(angs),sum_of_angle_differences2 / len(angs),sum_of_angle_differences3 / len(angs)))

Angle -90 Motor delta A:1 B:2 C:2
Angle 90 Motor delta A:0 B:3 C:1
Angle 45 Motor delta A:3 B:2 C:2
Angle -45 Motor delta A:1 B:1 C:1
Angle 180 Motor delta A:1 B:3 C:3
Angle 0 Motor delta A:2 B:0 C:0
Angle -90 Motor delta A:2 B:3 C:1
Angle 90 Motor delta A:2 B:3 C:1
Angle 45 Motor delta A:3 B:1 C:2
Angle -45 Motor delta A:1 B:2 C:1
Angle 180 Motor delta A:2 B:2 C:4
Angle 0 Motor delta A:1 B:3 C:2
Angle -90 Motor delta A:3 B:2 C:0
Angle 90 Motor delta A:0 B:2 C:2
Angle 45 Motor delta A:2 B:2 C:1
Angle -45 Motor delta A:1 B:1 C:1
Angle 180 Motor delta A:2 B:2 C:3
Angle 0 Motor delta A:1 B:4 C:1
Angle -90 Motor delta A:3 B:1 C:1
Angle 90 Motor delta A:1 B:2 C:0
Angle 45 Motor delta A:2 B:1 C:2
Angle -45 Motor delta A:1 B:2 C:1
Angle 180 Motor delta A:2 B:2 C:3
Angle 0 Motor delta A:1 B:1 C:1
Angle -90 Motor delta A:1 B:3 C:2
Angle 90 Motor delta A:1 B:1 C:0
Angle 45 Motor delta A:2 B:2 C:1
Angle -45 Motor delta A:1 B:1 C:2
Angle 180 Motor delta A:1 B:2 C:5
Angle 0 Motor delta A:2 B:2 C:0
avg A:1.5333333333333334 B:1.9333333333333333 C:1.5333333333333334

There is some improvement compared with no bias

Angle -90 Motor delta A:2 B:2 C:139
Angle 90 Motor delta A:1 B:6 C:3
Angle 45 Motor delta A:3 B:2 C:4
Angle -45 Motor delta A:7 B:3 C:1
Angle 180 Motor delta A:2 B:3 C:2
Angle 0 Motor delta A:2 B:3 C:1
Angle -90 Motor delta A:3 B:3 C:3
Angle 90 Motor delta A:5 B:1 C:3
Angle 45 Motor delta A:1 B:3 C:2
Angle -45 Motor delta A:1 B:3 C:3
Angle 180 Motor delta A:7 B:5 C:4
Angle 0 Motor delta A:0 B:3 C:3
Angle -90 Motor delta A:1 B:3 C:2
Angle 90 Motor delta A:2 B:2 C:3
Angle 45 Motor delta A:3 B:3 C:2
Angle -45 Motor delta A:2 B:3 C:3
Angle 180 Motor delta A:2 B:3 C:6
Angle 0 Motor delta A:1 B:6 C:3
Angle -90 Motor delta A:1 B:3 C:1
Angle 90 Motor delta A:2 B:3 C:3
Angle 45 Motor delta A:3 B:5 C:1
Angle -45 Motor delta A:1 B:3 C:2
Angle 180 Motor delta A:7 B:2 C:4
Angle 0 Motor delta A:2 B:2 C:2
Angle -90 Motor delta A:1 B:3 C:5
Angle 90 Motor delta A:5 B:3 C:6
Angle 45 Motor delta A:7 B:5 C:4
Angle -45 Motor delta A:2 B:1 C:2
Angle 180 Motor delta A:2 B:2 C:6
Angle 0 Motor delta A:7 B:5 C:1
avg A:2.8333333333333335 B:3.1333333333333333 C:7.466666666666667

@chrisruk
Copy link
Contributor

Thanks for the response, I'll try testing with a load soon, I'm wondering
if a different bias value might help, when the motor is loaded.

@dhruvsheth-ai
Copy link

@chrisruk I'm facing the same issue. With load it moves only from -29 to 51 while instructing it to move from -180 to 180. However, it does the complete round when I use the command:

motor2.run_for_seconds(1, speed=80)

Also, I believe in my case (robotic arm) there might be a power issue. The base motor isn't getting enough power to operate at it's full capacity

@chrisruk
Copy link
Contributor

@dhruvsheth-ai Thanks for reporting also, just wondering have you tried also passing a speed parameter to the run_to_position function (default is 20, so it might be worth trying say 30, 40..)?

@dhruvsheth-ai
Copy link

Hello @chrisruk thanks for reverting back this quick!
Yes I did try at alternating speed levels too. The following video file attached is your code running on the robotic arm.

demo-lego1.mp4

This is the output from the code (I added some extra print statements):

Expected -180
Actual 109
71
Expected 180
Actual 143
37
Expected 90
Actual 92
2
Expected -90
Actual 165
105
Expected 45
Actual 47
2
Expected -45
Actual -41
4
Expected 0
Actual -5
5
Expected -180
Actual -162
18
Expected 180
Actual -170
10
Expected 90
Actual 105
14
Expected -90
Actual -92
3
Expected 45
Actual 36
9
Expected -45
Actual -43
2
Expected 0
Actual -10
10
avg  20.857142857142858

Additionally, the next one is a demo video of a code asking it to run 4 loops from -180 to 180 at 70 speed

for i in range(0,4):
    motor2.run_to_position(180, speed=70, direction='anticlockwise')
    motor2.run_to_position(-180, speed=70, direction='clockwise')

And this is the attached video which shows that it manages to run just a bit more than 45 degrees rather than 360.

demo-lego2.mp4

Thanks for the help!

@chrisruk
Copy link
Contributor

Thanks for the videos, it looks a very cool project! I'll see if the firmware dev might have any suggestions

@dhruvsheth-ai
Copy link

@chrisruk Thanks for your help!

@dhruvsheth-ai
Copy link

I finally sorted this issue. The catch is that -180 to 180 i.e 360 degrees for buildhat is actually representative of just 45 degrees in actual rotation. A better way to sort this is using the command,

x = 1.5
motor.run_for_rotations(x, speed=30, blocking=True)

Here x can be any integer. 1 represents 45 degrees turned in actual conversion and 2 represents 90 degrees.
x = (degrees you want to turn)/90

before doing this, you can use the standard command to assign a particular location in the 45 degree interval

motor.run_to_position(20, speed=30, direction='clockwise')

After this running run_for_rotations will rotate it by the required amount more than 45 degrees.

I thought of posting this for anyone else stuck on the same issue.

@chrisruk
Copy link
Contributor

Cool, glad you sorted it. In case you haven't seen, there's also a run_for_degrees method, which may be useful too.

@dhruvsheth-ai
Copy link

Thanks @chrisruk it works well too!

@Gadgeteering
Copy link
Author

Is there a firmware update in the pipeline for this issue ?

@chrisruk
Copy link
Contributor

There is a new firmware I'm testing that introduces a new 'slower PWM' mode. I'm just wondering what kind of load you have attached to the motor that means run_to_position currently doesn't reach the position? I can see if I can replicate a similar load and drive with the new firmware.

@Gadgeteering
Copy link
Author

Below is a picture of the Delta Arm to show the load the motor is under
Arm2

Arm

@chrisruk
Copy link
Contributor

Wow! Very cool! Thanks for the images. I'll experiment with some different loads with the new firmware.

@chrisruk
Copy link
Contributor

@Gadgeteering If you'd like to try the new firmware there's a pull request I've created with it -
https://github.com/RaspberryPiFoundation/python-build-hat/tree/newfirmware2

You can install doing -

pip uninstall buildhat
pip install git+https://github.com/RaspberryPiFoundation/python-build-hat.git@newfirmware2

For me motors now seem to work better at lower speeds, I'm wondering if this may help with your use case.

The bias function is no longer available. So I used something like -

#!/usr/bin/python3.9

import time
from buildhat import Motor

m = Motor('A')
m.set_default_speed(5)

sum_of_angle_differences = 0
angs = [-90, 90, 45, -45, 180, 0] * 5
for i in angs:
    m.run_to_position(i)
    time.sleep(1)
    pos1 = m.get_aposition()
    diff = abs((i - pos1 + 180) % 360 - 180)
    sum_of_angle_differences += diff
    print(diff)

print("avg ", sum_of_angle_differences / len(angs))

You might want to different speeds too.

@Gadgeteering
Copy link
Author

I have tried the new firmware with a load on the motor and get an average difference of 'avg 5.033333333333333', which is not good enough for my project

I have resolved the issue by writing a feedback loop into the software to get a precision position of the motors
`#!/usr/bin/python3.9

import time
from buildhat import Motor

m = Motor('A')
m.set_default_speed(5)

sum_of_angle_differences = 0
angs = [-20, 20, -40, 40, 80, 0] * 5
for i in angs:
m.run_to_position(i)
time.sleep(1)
pos1 = m.get_aposition()
diff = abs((i - pos1 + 180) % 360 - 180)
if diff > 0:
delta = ((i - pos1 + 180) % 360 - 180)
diff = abs((i - pos1 + 180) % 360 - 180)
run = 0
run_delta =0
while(abs(delta)> 0):
if delta > 0:
m.run_for_seconds(0.001, speed = 50)
else:
m.run_for_seconds(0.001, speed = -50)
pos1 = m.get_aposition()
delta = ((i - pos1 + 180) % 360 - 180)
run += 1
pos1 = m.get_aposition()
delta = ((i - pos1 + 180) % 360 - 180)
print("Angle {} Run {} Pos {} Delta {}".format(i,run,pos1,delta))
`

@chrisruk
Copy link
Contributor

chrisruk commented Apr 5, 2023

Thanks for the response, I've just been testing altering one of the PID parameters (dead zone) to see if that helped improve accuracy for me, with a small load, along with trying rpm mode.

I altered the last value on this line - https://github.com/RaspberryPiFoundation/python-build-hat/blob/main/buildhat/motors.py#L216 from 0.01 to 0.001 to alter the dead zone.

My test script that I ran with the dead zone set to 0.01 and 0.001.

import time
from buildhat import Motor

m = Motor('A')

for rpm in [True, False]:
    m.set_speed_unit_rpm(rpm)

    for speed in [5, 10, 15, 20, 25, 30]:
        m.set_default_speed(speed)
        sum_of_angle_differences = 0
        angs = [-90, 90, 45, -45, 180, 0] * 20
        total = len(angs)

        for counter, i in enumerate(angs):
            m.run_to_position(i)
            time.sleep(1)
            pos1 = m.get_aposition()
            diff = abs((i - pos1 + 180) % 360 - 180)
            sum_of_angle_differences += diff
        avg =  sum_of_angle_differences / len(angs)
        print(f"{rpm}, {speed}, {avg}")

Results:

0.001 pid deadzone

RPM mode Speed Avg difference from desired angle
True 5 1.28
True 10 1.54
True 15 1.13
True 20 1.33
True 25 1.58
True 30 2.27
False 5 1.23
False 10 1.86
False 15 4.31
False 20 4.25
False 25 4.34
False 30 5.53

0.01 pid deadzone

RPM mode Speed Avg difference from desired angle
True 5 1.78
True 10 1.58
True 15 1.5
True 20 1.62
True 25 1.78
True 30 2.35
False 5 1.65
False 10 2.24
False 15 3.25
False 20 4.43
False 25 3.84
False 30 5.08

From looking at my results, it's hard to see how much of a difference altering the PID dead zone makes, however
you might want to try using RPM mode to see if you get similar results, however I assume
the load I'm using will be different to yours.

m = Motor('A')
m.set_speed_unit_rpm(True) # To set rpm mode
m.set_default_speed(5)

(It's hard for me to tell though, if the RPM mode appears to work better just because the speeds might be lower than non-RPM mode, but might be worth a try)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants