Pulsin and servopos in a loop? I'm getting severe servo jitter.

Brian Z

New Member
I have built a three wheeled robot with omni wheels, and all of the wheels are 60° from each other, and I'm using continuous rotation servos for my motors. I'm trying to control it with a radio controlled system, but I can't mix my channels like I need in my radio, so I wrote some code to read the channel 1 signal (up and down on the right joystick, forward and reverse), channel 2 signal (right and left on the right joystick, rotate clockwise and counterclockwise), and channel 4 signal (left and right on the left joystick, slide sideways left and right) via the pulsin command. I then mixed those signals together and outputted the data via a servopos command. Using a sertxd command I can read my variable values and they are exactly what I expect them to be, but when I run the code (with sertxd commented out) the servos are very, very jittery, and when I read their signals with an oscilloscope it is a very erratic signal.

I use my input signals from the receiver as my trigger on channel 1 on my oscilloscope, and my servopos signal (on channel 2) is maybe 4 times as long as it should be, then an appropriate space (about 20ms) and a pulse that looks correct, then it takes 3 or more transmitter input pulses before it does the long-signal correct-signal thing again, so it's not accurately pulsing a servo signal every 20ms, nor are all of the pulse lengths correct. I would guess I'm not going to get all of those calculations done in between the 20ms pulses, but that's probably OK for me, but I need my servopos signal to be operating correctly while it's doing the math for the next change in pulse length, but it's not.

Is there a trick to using pulsin and servopos at the same time? This is on a 14M2.

Code:
symbol forwards = b0    ;to be used in pulsin
symbol rotate = b1    ;to be used in pulsin
symbol slide = b2        ;to be used in pulsin

symbol flforward = b3    ;front left forward from forwards variable
symbol frforward = b4    ;front right forward from forwards variable
symbol bforward = 150     ;will always be 150

symbol flrotate = b5    ;front left rotate from rotate variable
symbol frrotate = b6    ;front right rotate from rotate variable
symbol brotate = b7    ;back rotate from rotate variable

symbol flslide = b8    ;front left slide from slide variable
symbol frslide = b9    ;front right slide from slide variable
symbol bslide = b10    ;back slide from slide variable

symbol fl = w6        ;front left
symbol fr = w7        ;front right
symbol ba = w8        ;back

symbol fldif = b18    ;difference between fl and 150
symbol frdif = b19    ;difference between fr and 150
symbol bdif = b20        ;difference between ba and 150

symbol tempone = w11    ;for holding math sub-calculations
symbol temptwo = w12    ;for holding math sub-calculations

Servosetup:
;disconnect
;low b.2
servo b.3, 150    ;front left servo
servo b.4, 150    ;front right servo
servo b.5, 150    ;back servo
pause 5000


Main:
 ;get readings
 pulsin c.2, 1, forwards    ;measure servo pulse lengtth (do I need a word variable?)
 pulsin c.1, 1, rotate        ;measure servo pulse length for rotate
 pulsin c.0, 1, slide        ;measure servo pulse length for slide
 
 ;convert forwards signal into flforward and frforward
 let flforward = forwards
 let frforward = 300-forwards
 
 ;convert rotate signal into flrotate, frrotate, and brotate
 let flrotate = rotate
 let frrotate = rotate
 let brotate = rotate
 
 ;convert slide signal into flslide, frslide, and bslide
 let flslide = slide+150/2
 let frslide = slide+150/2
 let bslide = 300-slide
 
 ;add front lefts, front rights, and backs
 let fl = flforward + flrotate + flslide - 300
 let fr = frforward + frrotate + frslide - 300
 let ba = bforward + brotate + bslide - 300
 
 ;find each signal's difference from 150, possible input range is 0-300
if fl > 150 then
    let fldif = fl-150
else
    let fldif = 150-fl
endif

if fr > 150 then
    let frdif = fr-150
else
    let frdif = 150-fr
endif

if ba > 150 then
    let bdif = ba-150
else
    let bdif = 150-ba
endif

;find the greatest distance signal from 150, its slope, and y intercept, then scale signals
if fldif >= frdif and fldif >= bdif and fldif > 50 then    ;when fl is biggest and greater than 50
                    
    ;calculate new fr
    let tempone = 50 * fr
    let temptwo = 7500 / fldif
    let fr = tempone / fldif + 150 - temptwo
    
    ;calculate new ba
    let tempone = 50 * ba
    let temptwo = 7500 / fldif
    let ba = tempone / fldif + 150 - temptwo
        
    ;calculate new fl
    if fl > 200 then
        let fl = 200
    endif
    if fl < 100 then
        let fl = 100
    endif
endif
    
if frdif >= fldif and frdif >= bdif and frdif > 50 then    ;when fr is biggest and greater than 50
                    
    ;calculate new fl
    let tempone = 50 * fl
    let temptwo = 7500 / frdif
    let fl = tempone / frdif + 150 - temptwo
    
    ;calculate new ba
    let tempone = 50 * ba
    let temptwo = 7500 / frdif
    let ba = tempone / frdif + 150 - temptwo
        
    ;calculate new fr
    if fr > 200 then
        let fr = 200
    endif
    if fr < 100 then
        let fr = 100
    endif
endif

if bdif >= fldif and bdif >= frdif and bdif > 50 then        ;when bdif is biggest and greater than 50
                    
    ;calculate new fl
    let tempone = 50 * fl
    let temptwo = 7500 / bdif
    let fl = tempone / bdif + 150 - temptwo
    
    ;calculate new fr
    let tempone = 50 * fr
    let temptwo = 7500 / bdif
    let fr = tempone / bdif + 150 - temptwo
        
    ;calculate new ba
    if ba > 200 then
        let ba = 200
    endif
    if ba < 100 then
        let ba = 100
    endif
endif
;sertxd("fl ",#fl," fr ",#fr," ba ",#ba,13,10)

servopos b.3, fl        ;output to front left
servopos b.4, fr        ;output to front right
servopos b.5, ba        ;output to back

goto main
 

AllyCat

Senior Member
Hi,
Is there a trick to using pulsin and servopos at the same time? This is on a 14M2.
Code:
pulsin c.2, 1, forwards    ;measure servo pulse lengtth (do I need a word variable?)
pulsin c.1, 1, rotate        ;measure servo pulse length for rotate
pulsin c.0, 1, slide        ;measure servo pulse length for slide
No, probably not; IMHO you're being too ambitious/optimistic. I've beeen programming PICaxe for around 10 years (and micros for more than 40) and I don't think I could make that work reliably with a PICaxe. :(

The problem(s) is that the Servo pulses are (internal system) Interrupt-driven, but PULSIN is a "blocking" command which basically "counts" the pulse width (and the waiting/timeout period) in 10us timeslots. It cannot do anything else at the same time. It's not clear which task has priority, but if the Servo interrupt has priority, then it will corrupt the time measured by PULSIN. Any SERTXD/SEROUT (and SERRXD/IN) commands will block both!

But your problem may be even more fundamental: The program reads three pulses in sequence and has to first wait for each to start. Thus it might take three (x 20 ms) "frames" before the pulses have been measured. You might be able to read the three pulses in the correct sequence, but the PICaxe is probably not fast enough to jump from one to the next, so the best might be to measure pulses 1 and 3 in a first frame and number 2 in the second. You really need a Logic Analyser, but might be able to manage with a two-channel 'scope. Running the PICaxe at SETFREQ M16 might help, but then you will need to use Word variables for PULSIN and the programming could get quite complex.

It probably would be better to use a completely different communications method (such as IRIN), but I can see two possible methods that might be able to work with input pulses. One would be to employ (User) Interrupts to read the pulses, but it won't be very accurate and you'd probably need to PEEKSFR both bytes of Timer 1, which is getting to rather "advanced" programming. Therefore, the method I woud try is to emulate the Servo pulses using the "background" hardware PWMOUT/DUTY commands (not HPWM). However, the PWM will not run quite as low as 50 Hz (20 ms frames) even at the default SETFREQ M4, but might be adequate. Also the pulse resolution is only around 20 us (against 10us for SERVOPOS) but this might not be too relevant with Continuous Rotation Servos. I don't know if you're using "real" CR servos or "modified" normal servos (if there is any difference), nor how much fine speed control the pulse variations might produce.

Cheers, Alan.
 

inglewoodpete

Senior Member
Have a read of Appendix 4 of Manual 2. This describes potential conflicts between several PICAXE commands. It is not very detailed but, as Alan says, the area is frought with risks.

While some timing commands (count, pulsin, pulseout...) do not disable interrupts, their results can be distorted by background interrupts.
 

Brian Z

New Member
Could I offload the pulsin commands to another Picaxe chip? Is there a way to communicate between two chips that won't mess up the servo signals?
 

BillBWann

Member
I’d suggest that you use an approach that I used in #12 of this thread. In that instance, two 08M2’s are used. The first reads the radio control signal (which was simulated by a third 08M2 in that test case) and then transmits it at high speed to the second 08M2 which then generates the output servo pulses using pulsout commands.

The input signal and the output signal are kept rigidly in sync so there is no need to use the picaxe servo command to generate the 20msec output frame rate.

In my (somewhat limited) servo experience, the servos haven’t been fussy about their received frame rate of pulses but of course they must be consistent and not include eratic incorrect pulses dispersed amongst the correct ones. So if the servo doesn’t need to rapidly track changing joystick movements, then it may be possible to use just one 08M2. Simply read an input frame from the radio control and then generate an output signal in the next 20msec frame and then go back to reading the signal from the radio control in the next 20 msec frame. In that way, the output servo signal would only occur every 40 msecs and it would be exactly in sync with the radio control signal but I wouln't expect that to be a problem.
 

Brian Z

New Member
I’d suggest that you use an approach that I used in #12 of this thread. In that instance, two 08M2’s are used. The first reads the radio control signal (which was simulated by a third 08M2 in that test case) and then transmits it at high speed to the second 08M2 which then generates the output servo pulses using pulsout commands.

The input signal and the output signal are kept rigidly in sync so there is no need to use the picaxe servo command to generate the 20msec output frame rate.

In my (somewhat limited) servo experience, the servos haven’t been fussy about their received frame rate of pulses but of course they must be consistent and not include eratic incorrect pulses dispersed amongst the correct ones. So if the servo doesn’t need to rapidly track changing joystick movements, then it may be possible to use just one 08M2. Simply read an input frame from the radio control and then generate an output signal in the next 20msec frame and then go back to reading the signal from the radio control in the next 20 msec frame. In that way, the output servo signal would only occur every 40 msecs and it would be exactly in sync with the radio control signal but I wouln't expect that to be a problem.
Thank you for the reply! That is very interesting. I get the concept, but I'm going to have to look into the nuts and bolts of this. As I'm only reading three channels and outputting three channels, I may have an advantage here, with more time to get my calculations done, which are more complicated and at the moment use more variables. My concern with the increased clock speed is the requirement for word variables. I may not have enough with my current calculation methods. I'm going to look into this method with a single 14M2. Thanks again!
 

Brian Z

New Member
For posterity's sake, I'm posting an update: I commented out my servo commands and replaced the servopos commands with pulsout commands, and it's working pretty well. I'm getting an output signal once every four input signals, or about every 80ms. I'm going to clean up my code for my math (I think I can remove some variables and lines of math code) and see if that helps, and after that I might switch to 32Mhz. That may be enough to get it working well. If it's jittery I may try that interrupt thing.
 

mikeyBoo

Senior Member
For-what-it’s-worth dept.:

My very first Picaxe project (Kayak Control System) was the hardest one I’ve done to date. My buddy (Lewis) wanted to use a Picaxe-20M2 because it had 4 PWM outputs & he said the circuit-boards would be easier to lay out.
Having never used a Picaxe before, I was certain that it wasn’t up to the task. But Lew was right & I WAS WRONG…

When the project was finished, on a single 20M2 @ 4MHz we had PWM for motor speed, PWM for servo position, I2C for reading inputs & running lighting effects, serial to an OLED, analog inputs, calcing time-on-water & ampHour consumption all at the same time.
I swallowed my high-level language pride & admitted the little BASIC-like Picaxe was a very capable platform.

However… the one problem we could not get around was trying to use the Picaxe servo commands to drive the steering servo. Buzzing, inconsistent behavior, it just didn’t wanna’ do right with everything else running!
I didn’t waste time trying to figure out why (more ways than one to skin a cat). The solution was to use the pwmout command to drive the servo. (Most new servos can take the 245Hz update rate we used) All was well, fishing with it since 2014.
 

BillBWann

Member
I had a closer look at your thread today as I’d only skimmed it previously and I see that you’re actually reading the three individual servo outputs. I’d assumed that you were reading the output from the receiver before the signal was split off to the individual servos as occurred in the thread I suggested you look at.

I don’t have a proper radio control system but I’ve just looked at a remote toy car which has 3 channels even though only two are actually used. If your radio control is like this one, then I don’t think that you can do much better than you’re doing now with getting an update to the servos (using pulsout) every 80 msecs or every forth frame. This is because according to my logic probe, the start of the servo pulse for channel 2 starts only 2 usecs later than the end of the pulse from channel 1 and a picaxe at any frequency does effectively nothing in 2 usecs. So to get the 3 channels, your current program could be using three separate 20 msec frames to read the servo pulses and then outputting during the fourth.

Thinking about it further, you might be able to get it down to an update every 40 msecs if you run at 32 MHz and read channels 2 & 4 in the first 20 msec frame and channel 1 in the next and then manage to do your calcs and get your three pulsouts out and get back to reading channel 2 before the end of frame 2. And after a bit of further extra investigation, I think that may be possible.

As you say, your current program will have variable storage problems if they are all word variables but as you suggested, you can do away with a lot of those intermediate variables.

If you use the equations:-

fl=slide+150/2+forwards+rotate-300
fr=slide+150/2-forwards+rotate
ba=bforward +rotate-slide

you can free up variables b3 to b10 and it will also run faster. I don’t really understand what you’re doing after that so I haven’t tried to find a more efficient way of doing that.

Anyway, I put that slightly modified code into an 08M2 and used the logic probe to determine how long it took to do your calcs. At 4 MHz it took about 32 msecs which reduces to 4 msecs at 32 MHz. So I think you should be able to read channel 1 say within the first 3 msecs, do the calcs in another 4 and then send your 3 pulsouts (say at max 2 msecs each) in another 6 which only uses 13 msecs of your 20 msec frame. So I think you’re in with a good chance of doing it in 2 frames.

One final thing, do you realize that your fl, fr, ba equations break down if the average of the three pulsin values is below 90 or -60 in servo terms?

Looking at my cheap toy servo pulses, they don’t drop to much below 120 so if yours is like mine, you won’t have a problem but I was surprised that the servo range was so small (ie only +/-30).

All the best with it. It seems like an interesting project you're doing.
 

erco

Senior Member
I didn’t waste time trying to figure out why (more ways than one to skin a cat). The solution was to use the pwmout command to drive the servo. (Most new servos can take the 245Hz update rate we used) All was well, fishing with it since 2014.
Clever! Would be interested to hear more about that. 245 hz... did you have to do a lot of experimentation to map PWM output to servo position?
 

mikeyBoo

Senior Member
Clever! Would be interested to hear more about that. 245 hz... did you have to do a lot of experimentation to map PWM output to servo position?
hi erco,
no experiments needed
(note all this stuff is done in “Picaxe Kayak Control” app as well as PCA9685 control (very similar))
(used Picaxe pwmout to set update freq, pwmduty to set pulse width)
(note: on my servo I used 0.8ms…2.2mS, depends on servo travel allowed)

For a standard servo:
given that: a servo is controlled by sending it a pulse = 1mS…2mS
; 1.0mS = max CCW position
: 1.5mS = center position
; 2.0mS = max CW position

for example:
I want to update the servo at 245Hz (may use 50Hz…330Hz)
so that: PWM output period = 1/245 = 0.004082 seconds
pwmPeriod
= 0.004082

Find dutyCycle value needed for servo positions:
max CCW position = 1.0mS
so that y = 0.001
dutyCycle = y(1023)/pwmPeriod = 0.001(1023)/.004082 = 251
dutyCycle = 251

center position
= 1.5mS
so that y = 0.0015
dutyCycle = y(1023)/pwmPeriod = 0.0015(1023)/.004082 = 376
dutyCycle = 376

max CW position
= 2.0mS
so that y = 0.002
dutyCycle = y(1023)/pwmPeriod = 0.002(1023)/.004082 = 501
dutyCycle = 501

; we now know that center position = 376
; set pwmout to free-run at 245Hz with servo at center position
; need to select a Picaxe pin that supports pwmout
pwmout pwmdiv16, pin, 254, 376 ; 245Hz at servo neutral (center) position

; now we can move the servo by by specifying dutyCycle 251…501
; (i.e. 0…100% travel aka full CCW to full CW))
pwmduty pin,dutyCycle

Hope the above makes sense.
Of course, I don’t do manual calcs as shown above, but use a 3-way Linear Conversion app or spreadsheet. (I'm lazy...)
ConvCalc.jpg
 
Top