Shop OBEX P1 Docs P2 Docs Learn Events
SimGPS: ActivityBot enters the MATRIX — Parallax Forums

SimGPS: ActivityBot enters the MATRIX

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2015-02-03 20:32 in Robotics
Here's a program I've been working on the past couple months for the robotics class I'm teaching. The students need to learn how to navigate autonomously using GPS for an upcoming ASV (autonomous surface vehicle) project. Thes ASVs will be used to photograph the sea bottom in shallow water, recording the sea star population in light of the recent wasting disease that's spreading amongst the population.

The problem with teaching GPS navigation is that there's scant room indoors to run their bots with any meaningful lat/long range. But running outdoors comes with all kinds of bureaucratic and weather issues -- as if an ActivityBot could get far enough fast enough anyway. So I decided to write a simulation, wherein the ActivityBot stays connected to the PC. The PC runs a program that communicates with the bot and acts as a web server for the Firefox browser. It serves up the Google Earth API, and the webpage's Javascript program queries the server as to the bot's current position, heading, etc. The result is that the student's Spin program sends the SimGPS object things like individual wheel speeds. SimGPS.spin does some wheel (or thruster) odometry and responds to the user's Spin program with lat/long/course/speed/time info, as if it were getting this data from a real GPS. At the same time, the bot's track is overlain on Google Earth in real-time, so the student can see how well his/her program is performing.

Here's a sample of the Google Earth display:

attachment.php?attachmentid=113060&d=1422992640

The red line is the desired track between waypoints (in this case, three, plus the starting point). The yellow line is the student's actual track. The green circle with the arrow is the student's simulated bot, showing its position and heading. The waypoints and viewpoint are specified in the main program's DAT section, along with maximum wheel speed, wheel spacing, drift (wind, current) speed and direction, and inertia. Here's the program that runs this simulation:
CON

  _clkmode      = xtal1 + pll16x
  _xinfreq      = 5_000_000

  NSEGMENTS     = 1

OBJ

  gps   :       "SimGPS"
  math  :       "GPS Math"      

VAR

  long latitude, longitude, speed, course, heading, distance, bearing
  byte year, month, day, hours, minutes, seconds
  byte nmea_buffer[200]
  
PUB  start | dy, dx, angle_dif, i, j, wpt_lat, wpt_long

  gps.start(@parameter_list, true)
  gps.wheel_speed(1000, 1000)
  gps.start_sim
  repeat j from 0 to nwaypoints - 1
    repeat i from 0 to NSEGMENTS - 1
      wpt_lat := waypoints[j * 2] + (waypoints[j * 2 + 2] - waypoints[j * 2]) * i / NSEGMENTS
      wpt_long := waypoints[j * 2 + 1] + (waypoints[j * 2 + 3] - waypoints[j * 2 + 1]) * i / NSEGMENTS
      repeat
        gps.get_position(@latitude)
        bearing := math.bearing(latitude, longitude, wpt_lat, wpt_long)
        gps.set_heading(bearing)
        distance := math.distance_m(latitude, longitude, wpt_lat, wpt_long)
        show_stats
      until (distance =< 1 - (i < 10))
  gps.stop_sim
  repeat

PUB show_stats

  gps.str(string("Heading is "))
  gps.decf(heading, 1)
  gps.str(string("° Bearing is "))
  gps.decf(bearing, 1)
  gps.str(string("° Course is "))
  gps.decf(course, 1)
  gps.str(string("° Target distance is "))
  gps.dec(distance)
  gps.str(string(" meters"))
  gps.show_text
   
     
DAT

parameter_list
wheel_spacing_mm        long    400 'mm
max_mm_per_sec          long    4000 'mm/sec
drift_speed             long    1000 'mm/sec
drift_dir               long    1350 'tenths of a degree from true north
inertia                 long    0   '0 (no inertia) to 100 (inputs have no effect)
viewpoint               long    48_108022, -122_769162, 500 'millionths of a degree
nwaypoints              long    4 'number of waypoints > 1
waypoints               long    48_107269, -122_771633, 48_108775, -122_766691  'millionths of a degree
                        long    48_108805, -122_767783, 48_108038, -122_770513

The lesson parameters are given in the DAT section. In this lesson, there's a strong wind blowing from the NW. The program demonstrates the simple technique of always aiming towards the next waypoint -- with the obvious consequence that it doesn't stay glued to the desired track. I gave the students the gps.set_heading method to get them started. But they will have to abandon it eventually and use the gps.wheel_speed method exclusively, just as they will for their ASVs come May.

After I started this project, I heard that Google has deprecated the Google Earth API and it will go away come December. They haven't said what they're replacing it with, so I had to go ahead and finish the project anyway. The installer for SimGPS is attached below. It installs SimGPS.exe and (to the Propeller Tool Library folder) the required external Spin objects. There's not much in the way of documentation yet, but I think most of the functionality can be inferred from the example program.

-Phil
861 x 668 - 1M

Comments

  • ercoerco Posts: 20,255
    edited 2015-02-03 12:26
    Awesome project, PhiPi. I would love to be a student in your class, Master.

    Plywood Grasshopper
  • NikosGNikosG Posts: 705
    edited 2015-02-03 13:31
    Amazing Project Phil!

    Thanks for sharing!

    Can we use this method in order to make an outdoor Robot to draw something (using a marker or a chalk) in a large area (like a school yard)?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-02-03 15:42
    Thanks, guys!

    Nikos, my program could simulate the drawing as practice, prior to actually doing it outdoors with a real GPS. The simulation doesn't have a "pen up/down" function, though.

    BTW, the program will work with any Propeller board; it doesn't have to be an ActivityBot or Board. However, if you do use an actual robot, it might be fun to incorporate wheel movements with it on a test stand. It would appear rather like a dog having a dream. :) One other thing I have not tried is to rewrite the odometry code using a real GPS unit and to use an XBee for comms. That way you could monitor the actual position of a detached bot out in the field. SimGPS.exe does need a persistent internet connection to function, however.

    -Phil
  • mindrobotsmindrobots Posts: 6,506
    edited 2015-02-03 16:22
    Awesome!!

    Sign me up for your class too! Can I be on erco's team? I'll buy the beer for the "discussion" sessions!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-02-03 19:10
    mindrobots wrote:
    I'll buy the beer for the "discussion" sessions!
    Thanks, but you wouldn't necessarily even have to. In the above photo, you'll notice a building near the beach with red, green, and blue patio umbrellas. That's the local Pour House. Their rule is that, if you come by boat and land on their beach, you get a free beer. (I'm not sure about virtual landings, though. A virtual beer is certain to be less satisfying than a real one. :) )

    -Phil
  • WhitWhit Posts: 4,191
    edited 2015-02-03 20:02
    I want in too! Great project Phil!
  • ercoerco Posts: 20,255
    edited 2015-02-03 20:10
    Why is it that beer and robots go together so doggone well?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-02-03 20:32
    Here's a program that takes the bot on a tour around the Parallax parking lot. The challenge for forumistas is to modify it so that it uses only gps.wheel_speed to change directions and not gps.set_heading.
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      NSEGMENTS     = 1
    
    OBJ
    
      gps   :       "SimGPS"
      math  :       "GPS Math"      
    
    VAR
    
      long latitude, longitude, speed, course, heading, distance, bearing
      byte year, month, day, hours, minutes, seconds
      byte nmea_buffer[200]
      
    PUB  start | dy, dx, angle_dif, i, j, wpt_lat, wpt_long
    
      gps.start(@parameter_list, true)
      gps.wheel_speed(1000, 1000)
      gps.start_sim
      repeat j from 0 to nwaypoints - 1
        repeat i from 0 to NSEGMENTS - 1
          wpt_lat := waypoints[j * 2] + (waypoints[j * 2 + 2] - waypoints[j * 2]) * i / NSEGMENTS
          wpt_long := waypoints[j * 2 + 1] + (waypoints[j * 2 + 3] - waypoints[j * 2 + 1]) * i / NSEGMENTS
          repeat
            gps.get_position(@latitude)
            bearing := math.bearing(latitude, longitude, wpt_lat, wpt_long)
            gps.set_heading(bearing)
            distance := math.distance_m(latitude, longitude, wpt_lat, wpt_long)
            show_stats
          while (distance)
      gps.stop_sim
      repeat
    
    PUB show_stats
    
      gps.str(string("Heading is "))
      gps.decf(heading, 1)
      gps.str(string("° Bearing is "))
      gps.decf(bearing, 1)
      gps.str(string("° Course is "))
      gps.decf(course, 1)
      gps.str(string("° Target distance is "))
      gps.dec(distance)
      gps.str(string(" meters"))
      gps.show_text
       
         
    DAT
    
    parameter_list
    wheel_spacing_mm        long    400 'mm
    max_mm_per_sec          long    1000 'mm/sec
    drift_speed             long    0 'mm/sec
    drift_dir               long    0 'tenths of a degree from true north
    inertia                 long    0   '0 (no inertia) to 100 (inputs have no effect)
    viewpoint               long    38_812752, -121_296161, 232 'millionths of a degree
    nwaypoints              long    6 'number of waypoints > 1
    waypoints               long    38_812977, -121_296461, 38_812677, -121_296813  'millionths of a degree
                            long    38_812433, -121_296180, 38_812822, -121_295664
                            long    38_813216, -121_296133, 38_812977, -121_296461
    

    -Phil
Sign In or Register to comment.