Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Servo Jitter — Parallax Forums

Propeller Servo Jitter

copacetic353copacetic353 Posts: 52
edited 2012-05-13 11:53 in Propeller 1
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))

Comments

  • Mike GMike G Posts: 2,702
    edited 2012-05-11 10:42
    The community needs to see all your code. Otherwise, we're just guessing.

    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().
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-11 10:55
    Mike G wrote: »
    The community needs to see all your code. Otherwise, we're just guessing.

    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 :=0
    
    
  • Mike GreenMike Green Posts: 23,101
    edited 2012-05-11 11:00
    You can't just cut and paste a Spin program ... you lose all the indenting ... see this. Please re-post your code.
    attachment.php?attachmentid=78421&d=1297987572
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-11 11:03
    My apologies, I fixed it!
  • Mike GMike G Posts: 2,702
    edited 2012-05-11 11:29
    Move the reads and writes to separate cog processes. Make x, y, and z global variables.

    Something 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 lock
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-05-11 11:53
    What are you using as a power supply?

    The 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.
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-11 12:51
    Duane,

    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 :=0     
    
    
  • Mike GMike G Posts: 2,702
    edited 2012-05-11 12:59
    copacetic353, looks like the jitter is due to sampling one pin at a time. Rethink the sampling method. Sample all 3 pins at once.. Might need to code this in PASM?
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-11 13:14
    Mike G wrote: »
    copacetic353, looks like the jitter is due to sampling one pin at a time. Rethink the sampling method. Sample all 3 pins at once.. Might need to code this in PASM?

    Mike,

    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!!
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-05-11 13:31
    You don't really need to know how to program with PASM to use an object with PASM (as you already know).

    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.
  • Mike GMike G Posts: 2,702
    edited 2012-05-11 13:43
    I have too many projects as it is but I'll help where I can.

    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.
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-11 18:30
    Duane Degn wrote: »
    You don't really need to know how to program with PASM to use an object with PASM (as you already know).

    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.

    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?
    Mike G wrote: »
    I have too many projects as it is but I'll help where I can.

    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 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 :)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-05-11 20:02
    I don't see where you used an object that captures a signal from the R/C RX?

    The object "RC_Receiver_6" can receive up to six channels from the RC receiver.
    OBJ
      Servo : "Servo32v7.spin"
     [B] Rc : "RC_Receiver_6"
    [/B]Pst : "Parallax Serial Terminal"
      Sd: "SD-MMC_FATEngine.spin"
     
    

    You need to let it know which of the first six pins to use before you call its "Start" method as I did here.
    'Initialize the RC input object
      Rc.setpins(_RxMask)
      Rc.start  
    

    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] := 0
    

    I 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.
  • Mike4421Mike4421 Posts: 131
    edited 2012-05-11 21:08
    @ copacetic353, I'm new to Propeller but I'll like to help out and I'm sure you have done this...... here it goes...........

    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.
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-13 11:51
    Mike4421 wrote: »
    @ copacetic353, I'm new to Propeller but I'll like to help out and I'm sure you have done this...... here it goes...........

    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.
  • copacetic353copacetic353 Posts: 52
    edited 2012-05-13 11:53
    Duane Degn wrote: »
    The object "RC_Receiver_6" can receive up to six channels from the RC receiver.
    OBJ
      Servo : "Servo32v7.spin"
     [B] Rc : "RC_Receiver_6"
    [/B]Pst : "Parallax Serial Terminal"
      Sd: "SD-MMC_FATEngine.spin"
     
    

    You need to let it know which of the first six pins to use before you call its "Start" method as I did here.
    'Initialize the RC input object
      Rc.setpins(_RxMask)
      Rc.start  
    

    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] := 0
    

    I 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.

    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!
Sign In or Register to comment.