How do I "dynamically" name my servo?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

How do I "dynamically" name my servo?

Post by twodoctors » Mon Jul 11, 2022 12:53 am

Sorry guys. Another question:

Thanks to OlivierLenoir who answered my question earlier about randomisation of a string...

I'm trying to control my servos (10 of them) using the result of the randomisation. I'm sure it's something simple, but I don't know the correct term for this to find the answer.

So I kind of name my servos A to J:

Code: Select all

servoA = PWM(Pin(0))
servoA.freq(50)
servoB = PWM(Pin(1))
Then I move my servo by using duty_u16

Code: Select all

servoA.duty_u16(4500)
Then I assigned the order number A to J (1 to 10):

Code: Select all

targets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
Then I run the code as per Olivier:

Code: Select all

    count_target = range(len(targets))
    for t in count_target:
        target = choice(targets)
	servo(target).duty_u16(1350)
        targets.remove(target)
        utime.sleep(1)
        print(f'{target}')
And of course it didn't like it. I was trying to substitute the letter into the object "servo" but it's of course invalid. Also tried "" and {} but doesn't work. The closest I've got is:

Code: Select all

        target.duty_16(1350)
but it says AttributeError: 'str' object has no attribute 'duty_16'

So how do I rename the "Object" for PWM with the above? Couldn't find the answer with Google... I've probably misunderstood something.

Thanks in advance!

Adrian

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: How do I "dynamically" name my servo?

Post by jimmo » Mon Jul 11, 2022 2:22 am

twodoctors wrote:
Mon Jul 11, 2022 12:53 am
I'm trying to control my servos (10 of them) using the result of the randomisation. I'm sure it's something simple, but I don't know the correct term for this to find the answer.
I'm not sure the specific name for this, but in general this isn't a good idea (it's slow, and some parts of this aren't well supported in MicroPython).

Instead, you need to make some sort of data structure that maps a name (or an index) to a given servo. Is there any reason why they have to have names, rather can they just be numbered zero through nine?

If that's ok, then by far the easiest way is to just make a list, and then you can index that list to control a given servo.

Code: Select all

# Update to whatever your 10 actual pins are
servo_pin_ids = [0, 1, 7, 3, 9, 5, 20, ...]
# Make a list of PWM instances
servos = []
for p in servo_pin_ids:
  pwm = PWM(Pin(p)
  pwm.freq(50)
  servos.append(pwm)
Now you can write, for example, this to move the fourth servo:

Code: Select all

servos[3].duty_u16(1350)
To use your random example:

Code: Select all

# Make a copy of the servos list.
targets = servos[:]
# Keep looping while the target list is non-empty:
while targets:
    # Choose one servo, update it, remove from the list.
    target_servo = choice(targets)
    target_servo.duty_u16(1350)
    targets.remove(target_servo)
    time.sleep(1)
    print(f'{target_servo}')
If you really want to work with "names" rather than indicies, then you can compute the index from the name using

Code: Select all

def name_to_index(name):
    return ord(name)-'A'
Or you could instead create a dictionary, where the keys are the names.

twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

Re: How do I "dynamically" name my servo?

Post by twodoctors » Mon Jul 11, 2022 9:16 am

Thank you so much Jimmo. I can see how much more efficient using a list is. I'm learning, but not sure how much I'll remember being an "occasional programmer"! I've made notes in the codes so I can refer back to it.

I am still getting an error:

Code: Select all

AttributeError: 'PWM' object has no attribute 'duty_16'
I'll just cut and paste the "crucial bits" that I have copied / adopted:

Code: Select all

# Name the servo
servo_pin_ids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# List of PWM instances
servos = []
for p in servo_pin_ids:
    pwm = PWM(Pin(p))
    pwm.freq(50)
    servos.append(pwm)

Code: Select all

# Duplicate servos list
targets = servos[:]

Code: Select all

count_target = range(len(targets))
    for t in count_target:
        target_servos = choice(targets)
        #target_servos.duty_16(4500)
        targets.remove(target_servos)
        utime.sleep(1)
        print(f'{target_servos}')
The line "target_servos.duty_16(4500)" still doesn't work. Taking that out, I'm generating these output:

Code: Select all

<PWM slice=0 channel=1>
<PWM slice=4 channel=1>
<PWM slice=1 channel=0>
<PWM slice=3 channel=1>
<PWM slice=3 channel=0>
<PWM slice=4 channel=0>
<PWM slice=2 channel=1>
<PWM slice=1 channel=1>
<PWM slice=2 channel=0>
<PWM slice=0 channel=0>
The thing is, I have this line of code before the randomisation to reset the target (and to show that I am learning!):

Code: Select all

for p in servo_pin_ids:
        servos[p].duty_u16(4500)
And that resets all 10 servos fine with no error code.

I really have no idea why "target_servos.duty_16" doesn't work.

twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

Re: How do I "dynamically" name my servo?

Post by twodoctors » Mon Jul 11, 2022 10:42 am

I've worked it out by trial and error. I'm sure someone will have an even more elegant solution to this.

So for some reason this doesn't work:

Code: Select all

count_target = range(len(targets))
    for t in count_target:
        target_servos = choice(targets)
        #target_servos.duty_16(4500)#
        targets.remove(target_servos)
        utime.sleep(1)
        print(f'{target_servos}')
Giving this error

Code: Select all

AttributeError: 'PWM' object has no attribute 'duty_16'
I think it has to do with naming of the servo, since this line works:

Code: Select all

for p in servo_pin_ids:
        servos[p].duty_u16(4500)
So I took a convoluted route, and using random to create a random list first:

Code: Select all

targets = servo_pin_ids[:]
count_target = range(len(targets))
random_list= []
for t in count_target:
    target_servos = choice(targets)
    random_list.append(target_servos)      
    targets.remove(target_servos)
Then run the targets from the random list:

Code: Select all

for p in random_list:
    servos[p].duty_u16(4500)
    utime.sleep(1)
And it is working. Thanks to Jimmo again for pointing me to the right directions.

Happy to learn a more elegant solution to this. Thanks all!

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: How do I "dynamically" name my servo?

Post by jimmo » Mon Jul 11, 2022 10:45 am

twodoctors wrote:
Mon Jul 11, 2022 10:42 am
Giving this error
CODE: SELECT ALL

AttributeError: 'PWM' object has no attribute 'duty_16'
duty_16 should be duty_u16

User avatar
OlivierLenoir
Posts: 126
Joined: Fri Dec 13, 2019 7:10 pm
Location: Picardie, FR

Re: How do I "dynamically" name my servo?

Post by OlivierLenoir » Mon Jul 11, 2022 8:04 pm

I've started a project MicroPython-ShootingGallery, I plan to use it for archery training. You can see it on develop branch.
It's just the beginning.

You create Target(servo_pin, hide_duty_ns, show_duty_ns), then you add target to ShootingGallery.

twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

Re: How do I "dynamically" name my servo?

Post by twodoctors » Tue Jul 12, 2022 12:56 am

jimmo wrote:
Mon Jul 11, 2022 10:45 am
twodoctors wrote:
Mon Jul 11, 2022 10:42 am
Giving this error
CODE: SELECT ALL

AttributeError: 'PWM' object has no attribute 'duty_16'
duty_16 should be duty_u16
:o

Spent an hour trying to work out why it didn't work. 🤦🏻‍♂️

twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

Re: How do I "dynamically" name my servo?

Post by twodoctors » Tue Jul 12, 2022 1:01 am

OlivierLenoir wrote:
Mon Jul 11, 2022 8:04 pm
I've started a project MicroPython-ShootingGallery, I plan to use it for archery training. You can see it on develop branch.
It's just the beginning.

You create Target(servo_pin, hide_duty_ns, show_duty_ns), then you add target to ShootingGallery.
You put me to shame Olivier! :lol: I won't show you my code then!

At least I get to see how the Pro does it!

Mine is working as it should (on the software side) so I'm going to leave mine as it is. Very interesting how it can be made so efficient when you know what you are doing.

User avatar
OlivierLenoir
Posts: 126
Joined: Fri Dec 13, 2019 7:10 pm
Location: Picardie, FR

Re: How do I "dynamically" name my servo?

Post by OlivierLenoir » Tue Jul 12, 2022 5:32 am

twodoctors wrote:
Tue Jul 12, 2022 1:01 am
You put me to shame Olivier! :lol: I won't show you my code then!
Twodoctors be pound of your code.
The secret to code is to follow PEP20 - Zen of Python and those two steps:
  • Make it work
  • Improve it
Let us know if you want help to understand OOP.

twodoctors
Posts: 19
Joined: Tue Jun 28, 2022 11:24 am

Re: How do I "dynamically" name my servo?

Post by twodoctors » Wed Jul 13, 2022 3:19 pm

OlivierLenoir wrote:
Tue Jul 12, 2022 5:32 am
twodoctors wrote:
Tue Jul 12, 2022 1:01 am
You put me to shame Olivier! :lol: I won't show you my code then!
Twodoctors be pound of your code.
The secret to code is to follow PEP20 - Zen of Python and those two steps:
  • Make it work
  • Improve it
Let us know if you want help to understand OOP.
Thanks Olivier. Yourself and Jimmo (and other helpers) are a credit to the forum.

I've put it all together now. Working as expected, but more fun than I imagined!

https://youtu.be/f3MHArfTpe4

My next challenge to myself is to try and figure out how pop 2 random targets up at a time (instead of 1). Keep an eye out for another "Help me please!" post from me soon! :lol:

Post Reply