Asked — Edited
Resolved Resolved by DJ Sures!

How To Stop A Moving Servo

I'm learning how to use EZB scripts. I'm doing a very simple exercise. I make a servo move , and want it to stop it as soon as a sensor makes a digital input high. I'd want to see how long it takes to actually stop. I use "release" to stop it (it's a digital servo, it will keep its position even with no signal). I see no other script statement to stop a servo movement. I wrote this script:

servospeed(d3,5) servo(d3,180) (the servo travel, from 130 to 180, lasts about 3 sec.) sleep(200) if (getdigital(d11)=1) release(d3) endif

The sensor is activated during servo travel, (3 sec.) but the servo reaches its target, ignoring the "if" statement . What's going on ? stress stress


ARC Pro

Upgrade to ARC Pro

Subscribe to ARC Pro, and your robot will become a canvas for your imagination, limited only by your creativity.

PRO
USA
#1  

@Leonardo,

Release you already know does not stop the servo.

When you setup a speed and you send a position to the EZB controller, the firmware keeps moving the servo until it reaches the position.

ASFAIK there is no stop command implemented in the EZB controller.

The only way is to send the servo to a specific position without a delay (Speed 0).

PRO
Synthiam
#2  

Please post code using the [ code ] [ /code ] tags for readability

It's a real good idea to first learn how a servo works to understand limitations, here's an important tutorial: https://synthiam.com/Tutorials/Lesson/48?courseId=6

That tutorial will explain that a servo simply moves into position based on a PWM signal.

Secondly, the code you posted does not have a loop or logic that resembles the goal.

Try this, which will move the servo and test the position with each step...


repeat($pos, 130, 180, 5)

servo(d3, $pos)

sleep(100)

if (getdigital(d11)=1)

  halt()

endif

endrepeat

PRO
USA
#3  

@DJ, my bad, slower even better, you can stop. When doing slower moves i prefer your approach, that way i can know the position.

@Leonardo, what kind of sensor you have on d11 ? Are you sure is not floating ?

#4  

I saw two options, by DJ, to try, one using "release", the other step by step , capable to inform about the position where the servo was stopped. I don't see any more the first one. Dj has deleted it ? I tried the second . It works, but the servo stops with a large delay (1-1.5 seconds !) Is it a limit of the ezb system (that's not a real-time system ) ? Should I use an embedded MCU for that ? @PTP. I plan to use a digital I.R. sensor. Now I'm trying with a simple micro-switch. I checked it working correctly , non floating. there's a resistor pull-down at the pin. It works well.

PRO
Synthiam
#5  

It stops with a delay because you mostly likely have the servo speed still set to 5. You will need to reset the servo speed to use default values and follow my code, otherwise it will not work.

If your project requires a slower servo speed, you will need to increase the sleep() delay to compensate for it.

PRO
Synthiam
#6  

Also, adding the sleep() after the servo() before the condition will work better by accommodating the physical real world movements.


repeat($pos, 130, 180, 5)

servo(d3, $pos)

sleep(100)

if (getdigital(d11)=1)

  halt()

endif

endrepeat

#7  

Ok,DJ, now it works faster. Can you explain the logic of the repeat statement? it's not very clear in the scrips description. It seems like the "for-next" statement in basic. repeat ($pos,130,180,5) means execute 5 times the code , but which value is assigned to the variable $pos each time ? 140,150,160,170,180 ?

PRO
Synthiam
#8  

5 is the increment. It's like a for statement.

130, 135, 140, 145, 150, 155 ... 180

#9  

ok,DJ. you solved the question. By the way, I always have problems in understanding how to use scripts. I hope that in some new version of ezb there will be a better explanation about each statement, and a better alphabetic or logical order in the list of statements to make it easier to find the one that could help. I have to go up/down each time.

@PTP. Thanks to you, for remembering that EZB firmware, while moving a servo, can't do anything else until it's finished. I'd expect that a rather fast and powerful MCU like that could do something more than merely outputting a PWM ! http://www.ez-obot.com/emicons/emo_biggrin.gif

PRO
USA
#10  

@Leonardo,

I didn't say that, let me explain.

1) Your EZ-Script code runs on ARC and ARC communicates with EZB controller, the communication is one way, e.g. Get Something, Do Something. The EZB can handle multiple tasks, for example while playing music, you can keep sending more commands, while moving a servo you can keep moving other servos etc.

2) Moving a servo without a delay i.e. ServoSpeed (0) is a simple task, ARC sends the action Servo(P1, X) to EZB controller, ARC knows the servo P1 is in position X

3) When moving a servo with a delay e.g. ServoSpeed(10), ARC sends the action Servo(P1, X) to EZB controller and assumes the servo is in position X, but the true is not, EZB controller is slow moving the Servo.

4) Release Release does two things, stops the servo and releases the servo. The reason why i don't like release (DJ pointed that too) when you send the Release you can stop a slow movement but ARC is not aware where the servo is.

5) GetServo(P1) The command does not query the EZB controller servo position, basically returns the last Servo(P1) position. Providing a EZB firmware call to return the real position could help when you Release a servo while EZB is still moving the servo.

5) StopServo(P1) Note: does not exist Stop command could be sent to EZB controller to stop the servo, and EZB controller would return the servo position (not servo feedback), that way ARC would be in sync with EZB

one example:


#full speed
ServoSpeed(23,0)
Servo(23, 1)
Print(GetServo(23))  => servo position is 1 
Sleep(2000)

#slowest speed
ServoSpeed(23,10)
Servo(23, 180)
Print(GetServo(23)) => ARC returns 180, EZB started moving the servo from position 1 to 180
Sleep(1500) 
Print(GetServo(23)) => ARC returns 180, EZB is still moving the Servo
Release(23) 
Print(GetServo(23)) => ARC returns 180, EZB is not moving the servo and the servo's position is not 180.

while EZB is moving the servo ARC is not aware of the servo's real position.

DJ's solution is the only true way to know where you are.

And yes i don't like the Release :-)

The MCU is powerful, but you need to adapt the logic when you are away from the MCU, is like working remotely.

I hope it's clear now, I'm sorry if you got the wrong idea.

#11  

Many thanks, PTP, for your detailed explanation. It'll be a reference for me when trying to write scripts. You helped me to understand how EZB +EZB controller work. It's like the captain of a ship, giving a command : " 5 degrees to right !" . He doesn't know the real position of the rudder helm and when his command will be fully executed. He assumes his command is being executed, and the ship will steer 5 degrees. If you ask him, he couldn't say anymore than this. And nobody knows the real servo position, except its internal pot , that doesn't give this information to anyone outside. Some servos , using serial communication, can give this information. Standard servos can't.

#12  

I can think of one way to use the servo release command and for ARC to know where the servo actually is. Please forgive if this has already been mentioned;

1). First you need to set the start position and speed of each servo you have in your project in the INT script when you start up ARC. (Also set your Min Max settings for your servos in the INT script while you're at it). 2). When you want the servo to start moving send it off in the direction you want it to travel and at the speed you want using whatever command you have been using. 3). When the servo reaches the point at which you want it to stop send the Release command. 4). Now the servo is stopped but ARC does not know where it actually is. Hopefully you know the setpoint (the degree you wanted it to stop at). Use that knowledge to reset the servo's position and speed by sending a new set of servo(), Servospeed() commands like you did in your INT script.

Hopefully this position command will be very close to where you wanted it to stop. You may experience a little additional servo movement while the servo moves back to the last sent command set point (position) because it may have overshot. Now after it moves back to the sent position ARC knows exactly where the servo is.

Not exactly a clean stop but at least you now have a true position for ARC to work from. This would only work if you can tolerate some movement after the release command is sent and time enough to reset the position before you send the servo off on another journey. :)

#13  

Thanks for your answer, Dave. I can't reset the servo after the release command (your point 4), because the servo has no pre-determined position to go to. I don't know where the servo has to go. It stops where a sensor, during the movement, detects and object . After that I can't know the position, using release. So iI prefer the method by DJ, by which I can know with a sufficient precision where the servo is.

#14  

Humm, I understand where you're coming from. I'm at a disadvantage as I don't know how you're using these servos or anything about your project.

However as far as starting the servo's up in a direction you may need to just either set the setpoint at either 1 or 180 and send the release command when it gets to the point you want to stop it. I've not tried that so I don't know if that will work.

As far as knowing where it stopped I guess that's the trick here. I assume it is stopped at a random place each time? The only electronic way In know would be to somehow attach a pot to the project somewhere and read it with a ADC port.

#15  

Okay, my 2 cents.

Would it be possible to put a switch in the system such that it would close at the point where the servo has reached it's maximum travel? This is so a signal can be sent back to an ADC input. Using a script generated counter and this switch you could calibrate the servo such that you would know where it stopped pretty closely.

First you would run the servo all the way to the starting point, perhaps 1 degree. Set the servo at the speed you want it to go during normal operation using the ServoSpeed instruction. Then a calibration cycle takes place. You would start the servo and a counter at the same time. When the switch closes, you stop the counter and remember the count. You now have a relationship between the counter's count and the servo position. If you want more accuracy, you could run the cycle 3 times and average the result.

You then start the counter again each time you run the servo. You stop the counter when you stop the servo. Knowing the count, you now have a fairly close estimate of the degrees moved. This allows you to get a bearing on where the servo is when stopped.

For example, Let's say, during the calibration cycle, the servo moves from 1 degree to 100 degrees, and the counter counts 10,000 during that time. We then have a relationship of 10,000 count /100 degrees moved =100 counts per degree. So, during normal operation, let's say the servo moved such that the counter has counted to 5000 (for example) when it is stopped by the release command. The degrees moved would be: 5000 count/100 counts per degree =50 degrees. When it's time to move the servo from where it is, the command Servo(Dnn, 50) is sent out first. It should already be at 50 degrees so little, if any, movement should come from the servo. Once the servo command for 50 degrees is sent out, the system then also knows where the servo is.

It may be possible to do the calibration cycle without a switch by watching the servo and clicking a script button manually when you see it it reach it's maximum travel (when it stops) . Again, you could do this several times and take an average of the counts for greater accuracy. A switch, however, would make the whole operation automatic. That would allow the initialization cycle to run a calibration script first thing when the robot connects. However, that may not be necessary since the count vs time relationship is not likely to change from run to run. Done once, it could be entered into a script manually.

Of course certain assumptions are made here.

  1. The servo has very little load on it, so it moves freely at any speed.
  2. The servo's rate of movement isn't going to physically change much as voltage decreases with use during a given run. If it does, the voltage can be monitored and either an adjustment factor can be introduced every X-Number of volts (or fraction of a volt) drop, or the calibration cycle can be automatically re-run at those lesser voltage points (assuming a switch is in place).

I can write a script to do all this if you like. It won't take long. Though it may not seem like it, such a script should be pretty simple to implement.

#16  

Hi, Dave. I'm experimenting a robotic arm searching for an object on the table and then grasping it in its hand. No need for extra pots. The pots are in the servos ! The arm is first positioned at 2 inches above the table, then moved around ,by a script, from 130 to 180 degrees, to detect the object in front of the hand, by IR sensors. When the object is detected, the arm is stopped , the hand is moved towards the object, stopped ad the right distance, then closed. To stop the servos, I had good results using DJ's method, i.e. commanding, by a loop, small step-by-step movements (e.g. 2 degrees) , then polling each time the IR sensor, and halting the script when the sensor detects the object. This way, the position is known with a tolerance of 2 degrees or , if wanted, even 1 degree. Simple, but effective. Hi,wbs00001 I appreciate the idea you suggested, i.e. tracking servo movements by a calibrated counter . I already had good results using DJ's very simple method (described in this post) consisting in only 4-5 lines of code. But, if you want, write the script for your method for me to see an alternative way.

#17  

That's why DJ is the Master around here. He is the keeper of the knowledge. :D

I'm glad you found a way.

PRO
Synthiam
#18  

Leonard, i did not say that you could not use Release - that was not me.

Release with a servo while it is moving slowly from ServoSpeed() will work, it's just not the way i would do it. You can do it anyway you wish that works.

Rather than speculating, a simple experiment will provide the answer. For example, try this code. Which will initialize a servo on D0 to a position, then begin moving it slowly with slow servospeed() to a new position, and 2 seconds later will release it, which stops from moving. You can simply do this test on your own. The code posted in the first message, as i had previously stated, was incomplete and did not have a loop or reflect any form of logic related to the goal - incomplete or incorrect user code does not reflect the capabilities of the EZ-B or ARC.


servospeed(d0, 0)

servo(d0, 10)

sleep(500)

servospeed(d0, 5)

servo(d0, 180)

sleep(2000)

release(d0)

If you were to use your original idea, it could be done like this...


# Open the gripper quickly
servospeed(d3, 0)
servo(d3, 150)

# Wait a bit for the servo to be open
sleep(1000)

# begin closing gripper
servospeed(d3, 5)
servo(d3, 180)

# loop 50 times or until the digital port has detected object
REPEAT ($cnt, 0, 50, 1)

  IF (getdigital(d11) = 1)

    release(d3)

    halt()

  ENDIF

  sleep(100)

ENDREPEAT

If you wish to for some reason know the position of a servo by reading it, you can purchase dynamixel servos, which are a lot more money but have more features than standard servos. However, there is no reason to know the position of a servo by reading it - ever. Because you're the one telling the servo what position to be in. Unless you expect some external force to move the servo so you can use the servo as a POT or input device, there's no reason. It's simple logic - your code tells the servo where to move, that's it. There's no mystery around it.:)

PRO
USA
#19  

Related to the release... I was the bad influence:)

#20  

Ok, DJ, I had well understood what you had said in your #3 post. I had tried some code, but not working, being a newcomer. You had said "release is worth trying, but I'd use another option, with a loop, etc, so I can know the position". I tried the loop option, and it worked, and I gave you a credit for having solved my question. You provided, in that post , the code for the release option. For some unknown reason (?), I found the post modified and the release option had disappeared. Now you are sending once again the code for the release option. I can test it for my experience, though the other option is working well. I agree with you that reading the servo position from the servo itself is not necessary. It might be useful only in very sophisticated projects.