Propeller Servo Jitter
Hello all,
I am using the Simple Radio Control Capture and Repeat, and Servo32v7 objects off of the OBEX to capture signals and control three servos that will eventually (hopefully) be used to control the cyclic on a model helicopter.
However, I am running into an issue with servo jitter as noted in the thread title. When I control just one servo, the motion seems to be very fluid even when I move the sticks on my radio quickly but when I try to do the same with all three cyclic servos, instead of a nice fluid servo movement, it becomes jerky, very much so when I move the sticks fast.
I have tried playing with certain values within both objects but to no avail. I have also tried controlling one servo with its own cog by declaring each servo as its own object and using the Start method within Servo32v7, but I get the same issue.
Please help!
The relevant code is here:
Servo.Start
repeat
x := PULSIN(PIN_SERVO_IN_1,1)
y := PULSIN(PIN_SERVO_IN_2,1)
z := PULSIN(PIN_SERVO_IN_3,1)
Servo.Set(PIN_SERVO_OUT_1,(x * 2))
Servo.Set(PIN_SERVO_OUT_2,(y * 2))
Servo.Set(PIN_SERVO_OUT_3,(z * 2))
I am using the Simple Radio Control Capture and Repeat, and Servo32v7 objects off of the OBEX to capture signals and control three servos that will eventually (hopefully) be used to control the cyclic on a model helicopter.
However, I am running into an issue with servo jitter as noted in the thread title. When I control just one servo, the motion seems to be very fluid even when I move the sticks on my radio quickly but when I try to do the same with all three cyclic servos, instead of a nice fluid servo movement, it becomes jerky, very much so when I move the sticks fast.
I have tried playing with certain values within both objects but to no avail. I have also tried controlling one servo with its own cog by declaring each servo as its own object and using the Start method within Servo32v7, but I get the same issue.
Please help!
The relevant code is here:
Servo.Start
repeat
x := PULSIN(PIN_SERVO_IN_1,1)
y := PULSIN(PIN_SERVO_IN_2,1)
z := PULSIN(PIN_SERVO_IN_3,1)
Servo.Set(PIN_SERVO_OUT_1,(x * 2))
Servo.Set(PIN_SERVO_OUT_2,(y * 2))
Servo.Set(PIN_SERVO_OUT_3,(z * 2))

Comments
My best guess, move the PULSIN commands to a separate process. Use semaphore locks to reduce dirty x, y and z reads while invoking Servo.Set().
CON _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 'Pins on Prop Chip for Servo Input/Output PIN_SERVO_IN_1 = 0 PIN_SERVO_OUT_1 = 1 PIN_SERVO_IN_2 = 2 PIN_SERVO_OUT_2 = 3 PIN_SERVO_IN_3 = 4 PIN_SERVO_OUT_3 = 5 OBJ Debug : "FullDuplexSerial" 'standard from propeller library Servo : "Servo32v7" 'standard from propeller library AILERON_LEFT : "Servo32v7" 'standard from propeller library AILERON_RIGHT : "Servo32v7" 'standard from propeller library PUB Begin | x,y,z 'Start Servo handler Servo.Start repeat x := PULSIN(PIN_SERVO_IN_1,1) y := PULSIN(PIN_SERVO_IN_2,1) z := PULSIN(PIN_SERVO_IN_3,1) Servo.Set(PIN_SERVO_OUT_1,(x * 2)) Servo.Set(PIN_SERVO_OUT_2,(y * 2)) Servo.Set(PIN_SERVO_OUT_3,(z * 2)) PUB PULSIN (Pin, State) : Duration | us {{ From BS2 Functions Library Object Reads duration of Pulse on pin defined for state, returns duration in 2uS resolution Shortest measureable pulse is around 20uS Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example x := BS2.Pulsin(5,1) BS2.Debug_Dec(x) }} us := clkfreq / 1_000_000 ' Clock cycles for 1 us Duration := PULSIN_Clk(Pin, State) / us / 2 + 1 ' Use PulsinClk and calc for 2uS increments \ PUB PULSIN_Clk(Pin, State) : Duration {{ From BS2 Functions Library Object; modified waitpne and waitpeq functions to use |< Pin Reads duration of Pulse on pin defined for state, returns duration in 1/clkFreq increments - 12.5nS at 80MHz Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example x := BS2.Pulsin_Clk(5,1) BS2.Debug_Dec(x) }} DIRA[pin]~ ctra := 0 if state == 1 ctra := (%11010 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, A level count else ctra := (%10101 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, !A level count frqa := 1 waitpne(|< Pin, |< Pin, 0) ' Wait for opposite state ready phsa:=0 ' Clear count waitpeq(|< Pin, |< Pin, 0) ' wait for pulse waitpne(|< Pin, |< Pin, 0) ' Wait for pulse to end Duration := phsa ' Return duration as counts ctra :=0Something like
PUB ReadXYZ repeat 'Check out a memory lock x := PULSIN(PIN_SERVO_IN_1,1) y := PULSIN(PIN_SERVO_IN_2,1) z := PULSIN(PIN_SERVO_IN_3,1) 'Release the memory lock PUB WriteZXY repeat 'Check out a memory lock Servo.Set(PIN_SERVO_OUT_1,(x * 2)) Servo.Set(PIN_SERVO_OUT_2,(y * 2)) Servo.Set(PIN_SERVO_OUT_3,(z * 2)) 'Release the memory lockThe power supply you are using might not provide enough power for all the servos.
You ought to try running the servos with a known program to see if they will all move.
If you run my QuickStart servo tester, any servos attached to pins 24 - 27 should start oscillating. If you're not using a QuickStart board, try to make sure nothing is connected to pins 0 - 7.
The servos are being powered by the helicopter battery, I don't think power is an issue here.
Mike,
I tried what you suggested and, perhaps I was implementing it incorrectly, but I was still getting the same issue.
I did however do this and it seems to work, but unfortunately ties up 4-5 cogs which I want to avoid if possible:
CON _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 'Pins on Prop Chip for Servo Input/Output PIN_SERVO_IN_1 = 0 PIN_SERVO_OUT_1 = 1 PIN_SERVO_IN_2 = 2 PIN_SERVO_OUT_2 = 3 PIN_SERVO_IN_3 = 4 PIN_SERVO_OUT_3 = 5 OBJ Debug : "FullDuplexSerial" 'standard from propeller library Servo : "Servo32v7" 'standard from propeller library VAR long elevator long left_aileron long right_aileron long read1[20] long read2[20] long read3[20] long writeStack[20] PUB Begin Servo.Start cognew(ReadELEV,@read1[20]) cognew(ReadLEFTAIL,@read2[20]) cognew(ReadRIGHTAIL,@read3[20]) cognew(Write,@writeStack[20]) PUB ReadELEV repeat elevator := PULSIN(PIN_SERVO_IN_1,1) PUB ReadLEFTAIL repeat left_aileron := PULSIN(PIN_SERVO_IN_2,1) PUB ReadRIGHTAIL repeat right_aileron := PULSIN(PIN_SERVO_IN_3,1) PUB Write repeat Servo.Set(PIN_SERVO_OUT_1,(elevator * 2)) Servo.Set(PIN_SERVO_OUT_2,(left_aileron * 2)) Servo.Set(PIN_SERVO_OUT_3,(right_aileron * 2)) PUB PULSIN (Pin, State) : Duration | us {{ From BS2 Functions Library Object Reads duration of Pulse on pin defined for state, returns duration in 2uS resolution Shortest measureable pulse is around 20uS Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example x := BS2.Pulsin(5,1) BS2.Debug_Dec(x) }} us := clkfreq / 1_000_000 ' Clock cycles for 1 us Duration := PULSIN_Clk(Pin, State) / us / 2 + 1 ' Use PulsinClk and calc for 2uS increments \ PUB PULSIN_Clk(Pin, State) : Duration {{ From BS2 Functions Library Object; modified waitpne and waitpeq functions to use |< Pin Reads duration of Pulse on pin defined for state, returns duration in 1/clkFreq increments - 12.5nS at 80MHz Note: Absence of pulse can cause cog lockup if watchdog is not used - See distributed example x := BS2.Pulsin_Clk(5,1) BS2.Debug_Dec(x) }} DIRA[pin]~ ctra := 0 if state == 1 ctra := (%11010 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, A level count else ctra := (%10101 << 26 ) | (%001 << 23) | (0 << 9) | (PIN) ' set up counter, !A level count frqa := 1 waitpne(|< Pin, |< Pin, 0) ' Wait for opposite state ready phsa:=0 ' Clear count waitpeq(|< Pin, |< Pin, 0) ' wait for pulse waitpne(|< Pin, |< Pin, 0) ' Wait for pulse to end Duration := phsa ' Return duration as counts ctra :=0Mike,
I am very new to the Propeller and Spin.
I was really hoping you were not going to mention PASM lol.
Could I bother you to send me down the right path to getting this done in a more efficient way? I've never coded PASM and it all looks pretty complicated to me but I am willing to learn.
Thank you so much for your time!!
My PropBOE-Bot project read the input from a RC receiver and then sends pulses (after being mixed) to a pair of servos.
I've suggest you use the same receiver object I used. The "PulseIn" method is too slow for what you want to do.
First, is is really necessary? Do you need more than 2 free COGs for something else? I assume the control signal is not always going to be an RC transmitter.
I don't see where you used an object that captures a signal from the R/C RX? Capturing that signal definitely seems to be what is slowing my program down, what would be a faster method than PulseIn?
I still need to read input from an IMU and I also need COGs to control a neural network, and I do not know how many COGs that will take. Freeing up as many COGs as possible while still giving me the data I need to feed through the NN would be great! Efficiency is always a good thing
The object "RC_Receiver_6" can receive up to six channels from the RC receiver.
You need to let it know which of the first six pins to use before you call its "Start" method as I did here.
I use this method to update my array of channel values.
The constant "_RxMask" had the value (percent sign)11_1111 assigned to it. (The forum software doesn't like the percent sign used to indicate binary numbers.)
PUB ReadRx | localIndex repeat localIndex from 0 to _RxChannels - 1 channel[localIndex] := Rc.getrc(localIndex) if ||channel[localIndex] =< _DeadBand channel[localIndex] := 0I added a bit of deadband to keep the wheels from jittering when I wanted them to be stopped.
I used the aileron and elevator channels to control the bot's two wheel speeds.
when I use the QuickStart Board with the "PE servo Object" 2007 code for continueus servo control, one servo turns just very very slighty and slowly...... just a guess.... but maybe adding a couple of Caps on the servo and/or power supply can just fix the noise problem....if it is noise.
Mike4421,
Thank you very much for your response but I don't think noise is the issue here. I believe Mike G hit the nail on the head in the reading one pulse one pin at a time using PulseIn is slowing the program down dramatically.
Mike G,
I will toy with this a little bit today to see if it is faster than the method I was using before. I just finished constructing my 3D State Object that is very simply an abstract data type and operators that will describe the state of something in 3D space. Thank you!