Is there a way to get the actual rpm from the pasm so it could be printed or transmitted as data to a control program (using an XBee for example).
Tom M.
Here is a version that returns the measured velocity ONLY WHEN IN CONSTANT VELOCITY MODE.
GetVelocity(serv0)
Since spin is best at returning a single value, CCW velocity is returned as negative. I am still thinkin' how best to make it more general.
I do not know for sure that my conversion from RPM to clicks/tic is correct and so I don't if the velocity is actually what is programmed. I can just sort of
eyeball it and say "one-mississippi two-mississippi" and it looks right, but...
I don't have any way to easily install mount an encoder to actually measure the rpm. If you have encoders on your activitybot and wanted to temporarily spend
a cog to independently measure velocity, that would be way cool.
Found a way to mount an encoder and will write code to compare claimed RPM to actual RPM.
Here is the latest (and I hope final) PASM driver for the Feedback Servo. It includes methods for ConstantPulseWidth, SetPosition, SetVelocity, GetPosition, and GetVelocity. A demonstration program is included. There is a little bit of engineering code in the driver but it is commented out.
Tom, I believe the driver is strictly compatible with the previous version posted in the previous comment. I made it possible to read the velocity in ConstantPulseWidth and generally cleaned things up in the PASM (comments, VariableNames).
There is also an object that displays the current velocity according to a ActivityBot encoder on servo 2.
Finally, I append a couple of pictures of my test rig as well as a picture of what is left of my cannabalized Bot.
I would like to suggest the Blockly guys have a look at this. Four servos in one cog is more better than one servo in two cogs
Tom,
Thanks. This looks interesting. I tried the previous version last night and had a problem running the SPIN program - I would get text printed out but the servos did not turn. I finally realized that I had not set the pins for servo3 to zero (I only have 3 servos). Then it worked. But since it was about 2AM when I started to port the code I took that as an omen that I wasn't thinking clearly enough to continue. Glad I waited. I hope to work on it this evening.
How did your rpm measurements compare with the encoder values?
Yup. If the PASM part tries to measure feedback from a servo that doesn't exist, it dies a horrible death.
I think it actually runs about 5% fast. That is, the encoder returns a value about 5% faster than what is commanded or sensed. I do not know why. I'm trying to decide if I think that is close enough.
Perhaps you can have a look at VelocityTest.spin and see if you see anything obvious.
The good news is that the velocity is just about completely independent of load. Plenty of gear noise, though.
This is P2asm... but it is easy to read.
The biggest difference from Pasm is that waitpeq and waitpneq have been replace by edge instructions...
So, we are waiting for positive and negative edges.
If you compare this with the logic that Phil posted, it is nearly identical.
get_position
waitse1 'wait for positive encoder edge
getct t1 'store counter in t1
.looper
mov t3,t1 'save t1 to t3
waitse2 'wait for negative encoder edge
getct t2 'store counter in t2
mov hperiod,t2 'move counter at negative edge to hperiod
sub hperiod,t1 'subtract counter value to give pulse width
waitse1 'wait for next positive encoder edge
getct t1 'store counter in t1
mov lperiod,t1 'move latest (positive) edge counter value to lperiod
sub lperiod,t2 'subtract counter value from lperiod to give low period
mov period,t1 'move latest positive encoder edge time to period
subs period,t3 'subtract first positive edge encoder vale to give complete period
jmp #.looper
This works as expected. I am getting a total period (depending upon how hard I drive the servo) right around 87,990 clocks)
I get a pulse width (just before the zero crossing) of 85,400 clocks... which is 97.0% of the period (perfect).
The problem is that when the zero crossing occurs, I get an immediate next pulse width value of 18,191 clocks... way too big.
AND this value stays until the servo turns about 30-40 degrees...
After that everything goes back to normal until the next zero crossing.
I am currently driving the servo at 5.80V with a pulse width of 1470us, but I have varied both with similar results.
I have tried two different servos.
I don't think it is a coding issue, and I don't think it is a hardware issue. Everything is wired up right.
What else is there?
1. Can you look at the feedback signal with a scope? That would tell you for sure whether you are measuring correctly or not.
2. I found that it was easier initially, at least, to not drive the servo but rather turn it by hand to experiment with the feedback signal.
I have turned the servo by hand and have the same problem. That is actually where I started.
In my experience it is always a software problem. What flabbergasts me is that it can be so perfect and on spec 2/3 of the time... and then just fail so miserably. I have 3 other units. I'm going to test them all today and then hook up a Propeller Activity Board.
What flabbergasts me is that it can be so perfect and on spec 2/3 of the time... and then just fail so miserably. I have 3 other units. I'm going to test them all today and then hook up a Propeller Activity Board.
Yeah, you always wonder about overflow, but with 32-bit registers and about 17-bit numbers, no...
I put my quad P1 driver up on OBEX. It all works on an activity board with 4 servos. I'd love to have more people try it.....
Problem solved... I had serial and servo running out of the same cog. It took too many clocks in the serial loop.
By putting another wait edge state at the beginning of the loop all is good.
Fresh eyes never hurt.
I'll take a look over in OBEX... haven't been over there for a while.
I've updated my port of Tom Crawford's servo360 spin driver into C and attached a zip folder with the project "libservo360b full".
"libservo360b full" contains demos that Tom had in his folder and a couple I added. One C function I added to the driver is fdsdrive(L-RPM, R-RPM) for driving an ActivityBot (or similar robot). The only specific requirement for that function is that servo0 is the left servo (CCW= robot fwd) and servo1 is the right servo (CW = robot fwd).
*** Note *** The demo as written is not set up for running an activitybot. The early parts of the demo exercise the servos (including randomly) which will result in weird movement.
One thing I noted is that when using the fdsdrive command (which uses the SetVelocity driver function) if a servo gets a command in the same direction it had been rotating, it will start sooner than the servo that gets a command in the opposite direction to which it had been going.
For example in the code below the robot is commanded to pivot to the left "fbsdrive(-20, 20);" for 3 seconds, then stop. It is then commanded to travel straight " fbsdrive(20, 20);". Since the right servo is +20 in the pivot command and also in the travel straight command, it starts to move before the left servo (even though the command to "SetVelocity" for the left servo is immediately before that for the right servo.) Changing the code so that the left servo gets the commands with the same direction causes the left servo to start moving before the right.
Any ideas?
// now pivot left
print("Pivot left\n");
fbsdrive(-20, 20);
pause(3000); // do for 3 seconds
fbsdrive(0, 0);
pause(500);
// straighten out
print("fwd 20 RPM\n");
fbsdrive(20, 20);
pause(3000); // do for 3 seconds
// and stop
fbsdrive(0, 0);
I've used that code to build a servo360 C library that I'm testing - so far it works. I'm also adding functions to the library (e.g. adding a drive function to move at a certain distance (inches & cm) per second which requires initialization with the wheel diameter) . Once I happy with it, I'll post the library.
Tom,
Here is a new version of the DAT section only. Should be able to put it right on top the old DAT. Basically, it makes servos start up from zero whenever a servelocity is issued.
Tom,
I tried the new DAT section. It solved the "one servo starting before the other" issue, but one of its effects is that it is not possible to change speed while traveling in the same direction without stopping. For example, starting to move fwd at 20 rpm, increasing speed to 40 and then 80 rpm, the servos come to a stop before each speed change. With the previous version, the servos appeared to keep turning during the speed change.
Tom,
Here is a new version of the DAT section only. Should be able to put it right on top the old DAT. Basically, it makes servos start up from zero whenever a servelocity is issued.
Tom,
Here is a new version of the DAT section only. Should be able to put it right on top the old DAT. Basically, it makes servos start up from zero whenever a servelocity is issued.
Tom C
Wouldn't this start from zero account for that?
It accounts for it, all right. Tom is just sayin' that is not the way it should work. He is correct, of course. I hope to find some time over the weekend.
Tom M,
This ought to be mo' better. I ramp up (or down) from the current delta or from DeltaMin, whichever is greater. It seems to change speeds pretty smoothly.
I added another demo segment that exercises the ramping capability. I am also posting just the PASM portion.
I think I will replace my original on OBEX next week.
tom c
Thanks. I haven't had a chance to look at the program in detail yet.
Were the changes made to only the DAT section or were there also changes to the Spin functions (in FeedBackServo5)?
Tom,
Thanks -- it works. (and gives me a few examples of PASM to study.)
I've attached a zip of the full C program that contains the driver and my demo (didn't add a smooth ramp function to my demo yet). I will turn this into a SimpleIDE library when I get a chance, but for now it runs as one program (with FeedBackServo4.spin updated to include the new DAT section in the same folder).
I did change the pin numbers to those used by the Parallax SimpleIDE servo360 library instead of the ones we've previously used. Now they are:
#define CP0 12 // also for ActivityBot left wheel (CCW = fwd)
#define CP1 13 // also for ActivityBot right wheel (CW = fwd)
#define CP2 16 // Servo2 control
#define CP3 0
#define FP0 14 // Servo0 feedback
#define FP1 15 // Servo1 feedback
#define FP2 17 // Servo2 feedback
#define FP3 0
I'm not happy about using the ActivityBoard servo header for the feedback pins since those headers can supply Vin for servos that need higher than 5v. In the future when I've explored the Parallax library and figured out how to change pin numbers I will change the feedback pins to something less than P12.
Tom M.
Edit: I deleted the zip and reposted version "b" in the post below. I had to correct my method for stopping the servo rotation in the demo. I added a new driver function "fbsdrivestop()" that stops the rotation by using the Width function to set 1500 usec in each wheel servo.
Here is a logic capture of accelerating from 20 CCW to 40 CCW. The yellow trace is pulsed when the pulse width is increased by one microsecond; the green trace when the pulse width is decreased. Wider pulses mean faster CCW. The pulse on the red trace is the demo program just prior to issuing the faster FBS.SetVelocity command.
Prior to the new command, you can see increase/decrease cases that pretty much balance each other. Immediately following the new command, a lot of increases take place until the servo has ramped up to the new velocity; then the increases/decreases again balance each other. It takes about 1/2 second.
Here is the latest final version.
Tom M, the DAT section is exactly the same as above except the debugging instructions are removed altogether rather than commented out.
Comments
Here is a version that returns the measured velocity ONLY WHEN IN CONSTANT VELOCITY MODE. Since spin is best at returning a single value, CCW velocity is returned as negative. I am still thinkin' how best to make it more general.
I do not know for sure that my conversion from RPM to clicks/tic is correct and so I don't if the velocity is actually what is programmed. I can just sort of
eyeball it and say "one-mississippi two-mississippi" and it looks right, but...
I don't have any way to easily install mount an encoder to actually measure the rpm. If you have encoders on your activitybot and wanted to temporarily spend
a cog to independently measure velocity, that would be way cool.
Found a way to mount an encoder and will write code to compare claimed RPM to actual RPM.
Tom, I believe the driver is strictly compatible with the previous version posted in the previous comment. I made it possible to read the velocity in ConstantPulseWidth and generally cleaned things up in the PASM (comments, VariableNames).
There is also an object that displays the current velocity according to a ActivityBot encoder on servo 2.
Finally, I append a couple of pictures of my test rig as well as a picture of what is left of my cannabalized Bot.
I would like to suggest the Blockly guys have a look at this. Four servos in one cog is more better than one servo in two cogs
Thanks. This looks interesting. I tried the previous version last night and had a problem running the SPIN program - I would get text printed out but the servos did not turn. I finally realized that I had not set the pins for servo3 to zero (I only have 3 servos). Then it worked. But since it was about 2AM when I started to port the code I took that as an omen that I wasn't thinking clearly enough to continue. Glad I waited. I hope to work on it this evening.
How did your rpm measurements compare with the encoder values?
Tom M.
I think it actually runs about 5% fast. That is, the encoder returns a value about 5% faster than what is commanded or sensed. I do not know why. I'm trying to decide if I think that is close enough.
Perhaps you can have a look at VelocityTest.spin and see if you see anything obvious.
The good news is that the velocity is just about completely independent of load. Plenty of gear noise, though.
Thanks,
Tom C
It turns out to be straightforward.
This is P2asm... but it is easy to read.
The biggest difference from Pasm is that waitpeq and waitpneq have been replace by edge instructions...
So, we are waiting for positive and negative edges.
If you compare this with the logic that Phil posted, it is nearly identical.
This works as expected. I am getting a total period (depending upon how hard I drive the servo) right around 87,990 clocks)
I get a pulse width (just before the zero crossing) of 85,400 clocks... which is 97.0% of the period (perfect).
The problem is that when the zero crossing occurs, I get an immediate next pulse width value of 18,191 clocks... way too big.
AND this value stays until the servo turns about 30-40 degrees...
After that everything goes back to normal until the next zero crossing.
I am currently driving the servo at 5.80V with a pulse width of 1470us, but I have varied both with similar results.
I have tried two different servos.
I don't think it is a coding issue, and I don't think it is a hardware issue. Everything is wired up right.
What else is there?
2. I found that it was easier initially, at least, to not drive the servo but rather turn it by hand to experiment with the feedback signal.
In my experience it is always a software problem. What flabbergasts me is that it can be so perfect and on spec 2/3 of the time... and then just fail so miserably. I have 3 other units. I'm going to test them all today and then hook up a Propeller Activity Board.
Yeah, you always wonder about overflow, but with 32-bit registers and about 17-bit numbers, no...
I put my quad P1 driver up on OBEX. It all works on an activity board with 4 servos. I'd love to have more people try it.....
Problem solved... I had serial and servo running out of the same cog. It took too many clocks in the serial loop.
By putting another wait edge state at the beginning of the loop all is good.
Fresh eyes never hurt.
I'll take a look over in OBEX... haven't been over there for a while.
"libservo360b full" contains demos that Tom had in his folder and a couple I added. One C function I added to the driver is fdsdrive(L-RPM, R-RPM) for driving an ActivityBot (or similar robot). The only specific requirement for that function is that servo0 is the left servo (CCW= robot fwd) and servo1 is the right servo (CW = robot fwd).
*** Note *** The demo as written is not set up for running an activitybot. The early parts of the demo exercise the servos (including randomly) which will result in weird movement.
One thing I noted is that when using the fdsdrive command (which uses the SetVelocity driver function) if a servo gets a command in the same direction it had been rotating, it will start sooner than the servo that gets a command in the opposite direction to which it had been going.
For example in the code below the robot is commanded to pivot to the left "fbsdrive(-20, 20);" for 3 seconds, then stop. It is then commanded to travel straight " fbsdrive(20, 20);". Since the right servo is +20 in the pivot command and also in the travel straight command, it starts to move before the left servo (even though the command to "SetVelocity" for the left servo is immediately before that for the right servo.) Changing the code so that the left servo gets the commands with the same direction causes the left servo to start moving before the right.
Any ideas?
I've used that code to build a servo360 C library that I'm testing - so far it works. I'm also adding functions to the library (e.g. adding a drive function to move at a certain distance (inches & cm) per second which requires initialization with the wheel diameter) . Once I happy with it, I'll post the library.
Tom
But just now I am helping with Tree decoration and am starting beef broth for braising chuck roast on Monday.
It sounds like you are doing the important things. Enjoy.
I'm just starting to shop for my nieces and nephews.
Tom M.
Here is a new version of the DAT section only. Should be able to put it right on top the old DAT. Basically, it makes servos start up from zero whenever a servelocity is issued.
Tom C
I tried the new DAT section. It solved the "one servo starting before the other" issue, but one of its effects is that it is not possible to change speed while traveling in the same direction without stopping. For example, starting to move fwd at 20 rpm, increasing speed to 40 and then 80 rpm, the servos come to a stop before each speed change. With the previous version, the servos appeared to keep turning during the speed change.
Tom M.
Wouldn't this start from zero account for that?
It accounts for it, all right. Tom is just sayin' that is not the way it should work. He is correct, of course. I hope to find some time over the weekend.
This ought to be mo' better. I ramp up (or down) from the current delta or from DeltaMin, whichever is greater. It seems to change speeds pretty smoothly.
I added another demo segment that exercises the ramping capability. I am also posting just the PASM portion.
I think I will replace my original on OBEX next week.
tom c
Thanks. I haven't had a chance to look at the program in detail yet.
Were the changes made to only the DAT section or were there also changes to the Spin functions (in FeedBackServo5)?
Thanks again
Tom M.
Edit: I posted the DAT section as a separate file. Endedit
tom C
Thanks -- it works. (and gives me a few examples of PASM to study.)
I've attached a zip of the full C program that contains the driver and my demo (didn't add a smooth ramp function to my demo yet). I will turn this into a SimpleIDE library when I get a chance, but for now it runs as one program (with FeedBackServo4.spin updated to include the new DAT section in the same folder).
I did change the pin numbers to those used by the Parallax SimpleIDE servo360 library instead of the ones we've previously used. Now they are:
I'm not happy about using the ActivityBoard servo header for the feedback pins since those headers can supply Vin for servos that need higher than 5v. In the future when I've explored the Parallax library and figured out how to change pin numbers I will change the feedback pins to something less than P12.
Tom M.
Edit: I deleted the zip and reposted version "b" in the post below. I had to correct my method for stopping the servo rotation in the demo. I added a new driver function "fbsdrivestop()" that stops the rotation by using the Width function to set 1500 usec in each wheel servo.
Tom
Prior to the new command, you can see increase/decrease cases that pretty much balance each other. Immediately following the new command, a lot of increases take place until the servo has ramped up to the new velocity; then the increases/decreases again balance each other. It takes about 1/2 second.
Tom M.
Tom M, the DAT section is exactly the same as above except the debugging instructions are removed altogether rather than commented out.
Jim
In this case, B = 0.53", and C = 1.33". IOW, the shaft is offset from the center by 0.4".
-Phil