Shop OBEX P1 Docs P2 Docs Learn Events
Controlling Servo to One of Three Positions — Parallax Forums

Controlling Servo to One of Three Positions

BoatServoBoatServo Posts: 6
edited 2014-05-09 12:23 in Robotics
Hello,
I am new to this language besides the course I am in. This also is not my major so it is not my strong suit. I am trying to get the servo to stay at one of three positions depending on ultrasonic sensor readings. The servo controls the rudder of a boat and operates at one of three positions: Forward (0 Degrees), Left (-45 Degrees), Right (45 Degrees). I pass the variable "tHa" from my main file in values from 1.5 (Forward), 1.3 (Left), and 1.7 (Right). I am not sure if the way I went to approach this is wrong or where the issue may be. The code is posted below:
CON
   
  _clkmode = xtal1 + pll16x                  ' System clock → 80 MHz
  _xinfreq = 5_000_000
Var long ServoStack[30]                      'Stack Space for Servo Cog
    long tC, t                               'Variables for Controlling Time and Frequency
PUB Begin (tHa)
  ctra[30..26] := %00100                     ' Configure Counter A to NCO
  ctra[5..0] := 31
  frqa := 1
  'dira[31]~~                                  'Pin for Servo
  cognew(Position(tHa), @ServoStack[0])     'Starts New Cog to Run Servo Continuosly 
 
Pub Position (tHa)
  tC := clkfreq                              'Constant
  t := cnt/50                                'Constant 20ms Reading Times by Servo 
  repeat                                     ' Repeat PWM signal
    phsa := -tHa                             ' Set up the pulse
    t += tC                                  ' Calculate next cycle repeat
    waitcnt(t)                               ' Wait for next cycle

Thank You in advance for any help.

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-04-28 16:52
    BoatServo wrote: »
    I pass the variable "tHa" from my main file in values from 1.5 (Forward), 1.3 (Left), and 1.7 (Right).

    I don't see any provision to use floating point numbers. The Propeller uses integer math unless an object (such as F32) is used to allow floating point calculations. It appears you are passing a value "tHa" from the parent. With the present code, the child object has no way of knowing when the parent's variable has changed. Instead of passing the value of the variable you would need to pass the variable's address and then access the value with "long[address]".

    Under the Propeller Tool's "File" menu is an option to archive a project. If you archive the top object it will include all the child objects you're using. It would probably be easier for us to help you if attached the entire program to a post.
  • BoatServoBoatServo Posts: 6
    edited 2014-04-28 21:30
    Thank you Duane! I really do appreciate the timely response. I have not yet tried the suggestion that you gave, but I will tomorrow when I meet with my group. Until then, the archive that you requested is attached. The top level file, "Current file edited with cogs" calls "HC SR04" to determine the distances and give proper units. It calls ServoPos, which is the code that was attached above. The HC SR04 sensors determine the distance from shore and the main file will determine what value to send "ServoPos" to steer the boat. The code is not very concise as of now, as bits and pieces of code are brought together.

    Thank you again,
    Boat Team
  • BoatServoBoatServo Posts: 6
    edited 2014-04-29 12:48
    Thank You for the advice again Duane,
    I have started to do what you suggested by sending the address, which I found out how to do by looking at your other posts. I want to refer to the variable in the child, so therefore I send the address instead by putting "@tHa". then in the child I can just use the variable, is that correct?

    I also am now using float math instead of integer math, so to calculate tHa, I use the formula:
    tHa := fMath.FMUL (Forward, clkfreq/20), where forward holds 1.5, which can be replaced by Left (1.3) or Right (1.7)

    Obviously there are still some mistakes because I my servo still isn't responding, and I forgot to mention that it is a standard Parallax Servo. Is there any way from you to tell if the values I am passing are correct for Pulse Width Modulation of a servo? I have noticed that my way of going about it is very different than others so I am unsure if I am correct in that part. I will attach the new files below.

    You can disregard the above comment.

    Current file edited with cogs and floats - Archive [Date 2014.04.29 Time 14.43].zip
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-04-29 13:35
    Floating point math isn't trivial to use. You can't mix floats and integers in equations. A floating point number gets encoded into a long differently than an integer. It's worth learning how to use floats but I really doubt you need them for your program.

    Instead of floats, I'd suggest using scaled integers (Heater told me the correct name for this technique but I don't remember what that name is). Multiply the integers you want to use by some fixed value like 1,000. So instead of attempting to have 0.5, you'd now have 500 which is but easier to work with.

    Not only can you not mix floats and integers, but you can't really use "<" or ">" comparisons between floats and other floats or floats and integers. Keeping your numbers as integers will save you a lot of trouble (you are presently using floats incorrectly).

    To display these scaled integers you can use a method like this:
    PUB DecPoint(value, denominator)
      if value < 0
        Pst.Char("-")
        -value
          
      if value => denominator
        result := value / denominator
        Pst.Dec(result)
        value //= denominator     
      else    
        Pst.Char("0")
      Pst.Char(".")  
      repeat while denominator > 1
        denominator /= 10
        if value => denominator
          result := value / denominator
          Pst.Dec(result)
          value //= denominator
        else
          Pst.Char("0")
    

    If "x" had been scaled by 1000 you could display the value in decimal format with the following line of code.
    DecPoint(x, 1000)
    

    If x equaled 500 it would display as "0.500".

    The forum member "local roger" (possibly one word) posted a better formatting method "fdec" in an object call "strfmt". "fdec" isn't as simple to use as my DecPoint but it's much more versatile.

    I looks like you're passing the address of the variable but you're not using the address correctly in the child object.
    cognew(Position(@tHa), @ServoStack[0]) 'Starts New Cog to Run Servo Continuosly 
    
    

    should be
    cognew(Position(tHa), @ServoStack[0]) 'Starts New Cog to Run Servo Continuosly 
    
    

    and
    phsa := -tHa                             ' Set up the pulse
    

    should be
    phsa := -long[tHa]                             ' Set up the pulse
    

    And in the top object, you don't need (nor do you want) the final
    Rudder.Begin(@tHa)                                    'Updates Current Rudder Position
    

    The top object just needs to change the value of "tHa" and the child will read it new value automatically.

    I think using scaled integers and making the other changes I suggest will go a long way in improving your code. I'm pretty sure there will be other mistakes (there are lots of ways to make them) but you should be closer to a working program.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-04-29 13:41
    I'd suggest renaming the variable containing the address in the child object from "tHa" to "tHaPtr" to indicate it's a pointer to the variable.
  • TtailspinTtailspin Posts: 1,326
    edited 2014-04-29 14:44
    the variable that says the boat needs to turn left, right, or center, could just be used in a "case" statement?
    I mean if the variable adds up to be in the range of turn left, then case := A, if the boat needs to turn right, then case := B...

    Math is hard enough for me, let alone trying to put a dot into the middle of a number just to try and trip me up. :)
    But if anyone can do the math, it will be the mighty Duane Degn.


    However you wind up doing it, +1 for using a Rudder..:thumb::thumb:


    -Tommy
  • BoatServoBoatServo Posts: 6
    edited 2014-04-29 15:35
    I appreciate both of your responeses, but I have switched to a different way of coding it because of the complexity as Duane mentioned. Now I am just passing an integer of the the position, named 1, 2, or 3 for left, right, and forward. Then in the child I use if statements to determine what position the code needs to be in depending on the value of the integer. Now I am having problems with how I am launching the cog, or at least I believe that is the issue. Duane mentioned in his 2nd post that I did not need to send the value again because it would automatically update somehow in the child? I don't know if I am interpreting that wrong, but if that is the case, how does the child know when the variable changes since the variable isn't the same one technically in the child.

    Maybe it is different now because I am not referring to the address since I am passing an integer now.

    Once again I have attached the code at the bottom. The main issue I am having is that when I try to call Servo #2 a second time with a new value, the parallax serial terminal goes berserk and sends random symbols at me. If I comment the last part out, the Parallax works correctly, but it only moves the rudder to the forward position and does not change it to any change from the sensors.

    I am sorry I am being such a pain here and I wish I knew more so I didnt have to ask, I am learning new things every time though, but there is still plenty to learn.

    Thanks again.

    And @Ttailspin, I realize now that I can use "case" statements now, but I is there any benefit besides it is more straightforward, like is it faster or someting? And thanks, the rudder boat is pretty cool, we had one with a dual prop but we figured this one would allow us better control. I just hope the HC SR04's will be accurate enough because I did not feel like spending 300 dollars on laser sensors for my school project although I am sure they would be loads more accurate.

    Current file edited with cogs and floats New - Archive [Date 2014.04.29 Time 17.33].zip
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-04-29 16:06
    Add this method to your Servo #2 code.
    Pub Change(newPos)
    
      Position := newPos
    

    Instead of calling "Rudder.Begin(Position)" at the end of your loop in the parent object call the "Rudder.Change(Position)" method.

    The "Begin" method starts a cog each time it is called until all the cogs are used up.
  • BoatServoBoatServo Posts: 6
    edited 2014-04-29 16:43
    Thank You so much, you are a lifesaver! That was what I needed. I tried to launch a specified cog before using the "coginit" function (or something like that) and it didnt work so I just went with cognew. That makes sense, I just didnt know if it would run through the cogs and then start back up at cog one once it ran out but since I didnt stop them i guess it will assume they are still in use. I have tested it and the rudder is now moving around as it is supposed to based upon the readings!
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-04-29 16:46
    BoatServo wrote: »
    I have tested it and the rudder is now moving around as it is supposed to based upon the readings!

    Fantastic!

    I hope you keep us posted on your project. It would be great to see some video.
  • BoatServoBoatServo Posts: 6
    edited 2014-04-29 17:11
    I will definitely post a video to this thread and forward you a video directly. Hopefully we will be able to figure out how to use our gps unit we just got to log the lake we are driving around to create a map of it and post that too. That is a big maybe right now and outside of the requirements but will definitely help my grade.

    thanks again for all of your help
  • WBA ConsultingWBA Consulting Posts: 2,933
    edited 2014-05-09 12:23
    Interesting sounding project, looking forward to the video.

    As for the GPS mapping side of things, it's actually insanely easy with the Propeller. Check out this recent thread which discusses this a bit. The code on this post from another thread has the code from my Google Earth Datalogger spawned from Paul_H's code that might do everything you are looking for as well. It parses GPS NMEA strings into KML formatting to be imported directly into Google Earth.
Sign In or Register to comment.