Mähzonen

Hi Boilevin,

i defined in my garden in each moving area ((5 areas) each 2 gps points.

If the mower is working.. i check continously if the mower comes to one of the points closer than 2 meters ( so radius 2 m)
If he hits one of this points i know in whih area he is..

I have one area were the mower normally is not long enough to finish.. (area 3) so if he hits are 4 when sheduler tells him to move area 3 than he does "PERITRACK" until he hits area 3 again and starts mowing.

Hope this helps..

I will make on weekend a map of my garden and document the code

Best Regards

Andreas
 
So jetzt fang ich mal an mit dem Code


In Robot.h


Code:
struct Point
{
    float X;
    float Y;
};

typedef struct Point Point;


unsigned long Peri_Track_P1;
    unsigned long Peri_Track_P2;
    unsigned long Peri_Track_P3;
    unsigned long Peri_Track_P4;
    unsigned long Peri_Track_P5;
    unsigned long Peri_Track_P6;
    unsigned long Peri_Track_P7;

    int Peri_find_Point;
    int Peri_found_Point;
    unsigned long find_Point_EndTime;

    float lon_mid;
    float lat_mid;

    int Area_Soll;
    int Area_Ist;

    int Out_of_Area_Timer;




In Robot.cpp vor void Robot::processGPSData()


Code:
//----------------------------------------------------------------------------------------------------------------------------
// Abstand von Robby zu einem definierten Punkt
//----------------------------------------------------------------------------------------------------------------------------

bool Abstand(Point p, Point r) {     //  int x1, int y1, int x2, int y2) {
  float distx;
  float disty;
  float l;
  if (p.X>=r.X)  distx= p.X-r.X;
  else  distx= r.X-p.X;
  if (p.Y>=r.Y)  disty= p.Y-r.Y;
  else  disty= r.Y-p.Y;
  l =(sqrt((float)(distx*distx)+(float)(disty*disty)));
  Console.println("Abstand: ");
  printDouble(l,6);
  return l <= 0.000030;
}



void Robot::processGPSData()


Code:
void Robot::processGPSData()
{
  if (millis() < nextTimeGPS) return;
  nextTimeGPS = millis() + 1000;
  float nlat, nlon;
  unsigned long age;

  Point P1,P2,P3,P4, P_ROBI;
  
  gps.f_get_position(&nlat, &nlon, &age);
  if (nlat == GPS::GPS_INVALID_F_ANGLE ) return;
  if (gpsLon == 0){
    gpsLon = nlon;  // this is xy (0,0)
    gpsLat = nlat;
    return;
  }
  gpsX = (float)gps.distance_between(nlat,  gpsLon,  gpsLat, gpsLon);
  gpsY = (float)gps.distance_between(gpsLat, nlon,   gpsLat, gpsLon);

  P_ROBI.X = nlon;
  P_ROBI.Y = nlat;




 // Console.println("ROBBY: ");
 // printDouble(P_ROBI.X,6);
 //  printDouble(P_ROBI.Y,6);


  //----------------   Area 1  --------------------------------------------------------

  P1.X = xx.671150; P1.Y= xx.102400;
  if (Abstand(P1, P_ROBI)) Area_Ist = 1;

  P1.X = xx.671170; P1.Y= xx.102370;
  if (Abstand(P1, P_ROBI)) Area_Ist = 1;
  
  //----------------   Area 2  --------------------------------------------------------

  P1.X = xx.671040; P1.Y= xx.102440;
  if (Abstand(P1, P_ROBI)) Area_Ist = 2;

  P1.X = xx.671030; P1.Y= xx.102370;
  if (Abstand(P1, P_ROBI)) Area_Ist = 2;

  //----------------   Area 3  --------------------------------------------------------

  P1.X = xx.671280; P1.Y= xx.102480;
  if (Abstand(P1, P_ROBI)) Area_Ist = 3;

  P1.X = xx.671260; P1.Y= xx.102470;
  if (Abstand(P1, P_ROBI)) Area_Ist = 3;

  //----------------   Area 4  --------------------------------------------------------
  
  P1.X = xx.671270; P1.Y= xx.102630;
  if (Abstand(P1, P_ROBI)) Area_Ist = 4;

  //----------------   Area 5  --------------------------------------------------------
  
    P1.X = xx.671170; P1.Y= xx.102620;
  if (Abstand(P1, P_ROBI)) Area_Ist = 5;

      P1.X = xx.671090; P1.Y= xx.102590;
  if (Abstand(P1, P_ROBI)) Area_Ist = 5;

 
  if (Area_Ist == Area_Soll) Out_of_Area_Timer = millis();

  if ((Area_Soll == 0)& (Area_Ist > 0))  Area_Soll = Area_Ist;    // Wenn Schaf in einer Area gestartet wird dann mäht  er auch in dieser Area
     
}


In der state machine


Code:
case STATE_FORWARD:
      // driving forward            
      if (mowPatternCurr == MOW_BIDIR){
        double ratio = motorBiDirSpeedRatio1;
        if (stateTime > 4000) ratio = motorBiDirSpeedRatio2;
        if (rollDir == RIGHT) motorRightSpeedRpmSet = ((double)motorLeftSpeedRpmSet) * ratio;
          else motorLeftSpeedRpmSet = ((double)motorRightSpeedRpmSet) * ratio;                            
      }             
      checkErrorCounter();    
      checkTimer();
      checkRain();
      checkCurrent();            
      checkBumpers();
      checkDrop();                                                                                                                            // Dropsensor - Absturzsensor
      checkSonar();             
      checkPerimeterBoundary(); 
      checkLawn();      
      checkTimeout();     

// ---------------------------------------------------------------------------------------------------------------------------------------------------------
//     Wenn Schaf eine Zeit lang in einem anderen Bereich mäht.. dann wieder zurück fahren in richtigen Bereich  
//----------------------------------------------------------------------------------------------------------------------------------------------------------

      if (((Area_Soll == 1) && (Area_Ist ==2)) or ((Area_Soll == 3) && (Area_Ist ==5)))
         if ((millis() - Out_of_Area_Timer) > 180000)
            {
              setNextState(STATE_STATION_FORW,0);   
              Peri_found_Point = 0;   
              Peri_find_Point = 1;   
              find_Point_EndTime = millis() + Peri_Track_P1;     
              Out_of_Area_Timer = millis();
            }  

       
      break;



in Pfod.cpp


Code:
void RemoteControl::sendGPSMenu(boolean update){
  
  float lat, lon;
  unsigned long age;
  robot->gps.f_get_position(&lat, &lon, &age); 
  
  if (update) Bluetooth.print("{:"); else Bluetooth.print(F("{.GPS`1000"));         
  Bluetooth.print(F("|q00~Use "));
  sendYesNo(robot->gpsUse);
  sendSlider("q01", F("Stucked if GPS speed is below"), robot->stuckIfGpsSpeedBelow, "", 0.1, 3); 
  sendSlider("q02", F("GPS speed ignore time"), robot->gpsSpeedIgnoreTime, "", 1, 10000, robot->motorReverseTime);       
    



    serialPort->print(F("|zz~LAT : "));
    printDouble(lat,6);
    serialPort->print(F("|zz~LON : "));
    printDouble(lon,6);
    serialPort->print(F("|zz~AREA IST : "));
    serialPort->print(robot->Area_Ist);
    Bluetooth.println("}"); 
}




Code:
void RemoteControl::sendTimerDetailMenu(int timerIdx, boolean update){
  if (update) serialPort->print("{:"); else serialPort->print(F("{.Details"));
  serialPort->print("|p0");
  serialPort->print(timerIdx);
  serialPort->print("~Use ");
  sendYesNo(robot->timer[timerIdx].active);        
  int startm = time2minutes(robot->timer[timerIdx].startTime);
  int stopm = time2minutes(robot->timer[timerIdx].stopTime);
  String sidx = String(timerIdx);
  sendSlider("p1"+sidx, F("Start hour "), robot->timer[timerIdx].startTime.hour, "", 1, 23, 0);       
  sendSlider("p2"+sidx, F("Start minute "), robot->timer[timerIdx].startTime.minute, "", 1, 59, 0);         
  sendSlider("p3"+sidx, F("Stop hour "), robot->timer[timerIdx].stopTime.hour, "", 1, 23, 0);       
  sendSlider("p4"+sidx, F("Stop minute "), robot->timer[timerIdx].stopTime.minute, "", 1, 59, 0);             
    for (int i=0; i < 7; i++){
    serialPort->print("|p5");
    serialPort->print(timerIdx);
    serialPort->print(i);
    serialPort->print("~");
    if ((robot->timer[timerIdx].daysOfWeek >> i) & 1) serialPort->print("(X)  ");
      else serialPort->print("(  )  ");
    serialPort->print(dayOfWeek[i]);
  }
  serialPort->print("|p9");
  serialPort->print(timerIdx);
  serialPort->print(F("~Set to current time"));
  sendSlider("p8"+sidx, F("Area "), robot->timer[timerIdx].Area_Timer, "", 1, 5, 0);             
  
  serialPort->println("}");
}

void RemoteControl::processTimerDetailMenu(String pfodCmd){      
  timehm_t time;
  boolean checkStop = false;
  boolean checkStart = false;
  int startmin, stopmin;
  int timerIdx = pfodCmd[2]-'0';
  if (pfodCmd.startsWith("p0")) robot->timer[timerIdx].active = !robot->timer[timerIdx].active;
    else if (pfodCmd.startsWith("p1")) { processSlider(pfodCmd, robot->timer[timerIdx].startTime.hour, 1); checkStop = true; }
    else if (pfodCmd.startsWith("p2")) { processSlider(pfodCmd, robot->timer[timerIdx].startTime.minute, 1); checkStop = true; }
    else if (pfodCmd.startsWith("p3")) { processSlider(pfodCmd, robot->timer[timerIdx].stopTime.hour, 1); checkStart = true; }      
    else if (pfodCmd.startsWith("p4")) { processSlider(pfodCmd, robot->timer[timerIdx].stopTime.minute, 1); checkStart = true; }        
    else if (pfodCmd.startsWith("p8")) { processSlider(pfodCmd, robot->timer[timerIdx].Area_Timer, 1);checkStart = false; }        
    else if (pfodCmd.startsWith("p9")) {       
      robot->timer[timerIdx].startTime = robot->datetime.time; checkStop = true;      
      robot->timer[timerIdx].daysOfWeek = (1 << robot->datetime.date.dayOfWeek);      
    }
    else if (pfodCmd.startsWith("p5")) {
      int day = pfodCmd[3]-'0';
      robot->timer[timerIdx].daysOfWeek = robot->timer[timerIdx].daysOfWeek ^ (1 << day);
    }
    if (checkStop){
      // adjust start time
      startmin = min(1434, time2minutes(robot->timer[timerIdx].startTime));
      minutes2time(startmin, time);
      robot->timer[timerIdx].startTime = time;
      // check stop time
      stopmin  = time2minutes(robot->timer[timerIdx].stopTime);
      stopmin = max(stopmin, startmin + 5);       
      minutes2time(stopmin, time);
      robot->timer[timerIdx].stopTime = time;
    } else if (checkStart){
      // adjust stop time
      stopmin = max(5, time2minutes(robot->timer[timerIdx].stopTime));
      minutes2time(stopmin, time);
      robot->timer[timerIdx].stopTime = time;      
      // check start time
      startmin = time2minutes(robot->timer[timerIdx].startTime);      
      startmin = min(startmin, stopmin - 5);       
      minutes2time(startmin, time);
      robot->timer[timerIdx].startTime = time;
    }
  sendTimerDetailMenu(timerIdx, true);  
}
 
Und hier noch der Grundriss


Unbenannt.jpg

Attachment: https://forum.ardumower.de/data/media/kunena/attachments/2990/Unbenannt.jpg/
 
Zuletzt bearbeitet von einem Moderator:
Hello Andreas
Good work.
To help understand, can you easily add the perimeter wire in your plan, I guess the wire does not do the trick of each area ?

Google translate:
Hallo Andreas
Gute Arbeit.
Um zu verstehen, können Sie leicht hinzufügen, die Perimeter Draht in Ihrem Plan, denke ich, der Draht nicht den Trick in jedem Bereich?

Thanks a lot.
 
Unbenannt2.png


Here it comes with perimeter wire :)


If mower hits one of the GPS Points while he is doing "PERITRACK "in right area were he shall move than he turns right and starts mowing

In Timer (PFODAPP) you can define the mowing zone with the time

If Mower should move area 4 and hits area 3 or 5 longer than a specific time he makes PARITRACK until he is back in the area 3
Attachment: https://forum.ardumower.de/data/media/kunena/attachments/2990/Unbenannt2.png/
 
Zuletzt bearbeitet von einem Moderator:
Hallo Andreas,

vielen Dank noch mal für deine Arbeit zu den Mähzonen.
Ich kam erst heute dazu, deinen Code einzufügen und bekomme folgende Compiler Fehlermeldungen:

Code:
pfod.cpp: In member function 'void RemoteControl::sendTimerDetailMenu(int, boolean)':

pfod.cpp:866: error: 'ttimer_t' has no member named 'Area_Timer'

   sendSlider("p8"+sidx, F("Area "), robot->timer[timerIdx].Area_Timer, "", 1, 5, 0);


Ihm fehlt zusätzlich die Funktion: printDouble

Kannst du mir helfen?

Gruß Frederic
 
Hi Frederic

packe die Funktion noch oben in die pfod.cpp


Code:
void RemoteControl::printDouble( double val, byte precision){
 // prints val with number of decimal places determine by precision
 // precision is a number from 0 to 6 indicating the desired decimial places
 // example: printDouble( 3.1415, 2); // prints 3.14 (two decimal places)
 serialPort->print(int(val));  //prints the int part
 if( precision > 0) {
   serialPort->print("."); // print the decimal point
   unsigned long frac;
   unsigned long mult = 1;
   byte padding = precision -1;
   while(precision--)
      mult *=10;
     
   if(val >= 0)
     frac = (val - int(val)) * mult;
   else
     frac = (int(val)- val ) * mult;
   unsigned long frac1 = frac;
   while( frac1 /= 10 )
     padding--;
   while(  padding--)
     serialPort->print("0");
   serialPort->print(frac,DEC) ;
 }
}
 
Hello, is this or any other mowing zone solution implemented in any Azurit SW or I must do it manualy?
Or is there any other solution for mowing zones ?
Thanks
Alex

Hallo, hier ist oder jede andere Mäh- Zone-Lösung implementiert in jedem Azurit SW oder ich muss es manuell tun?
Oder gibt es eine andere Lösung für die Zonen zu mähen?
 
Hallo,

ich habe Andreas GPS Mähzonen Code bei mir eingebaut und leicht angepasst. Soweit funktioniert das auch recht gut.
Über die App kann ich den Mähzonen Punkte zuweisen, die im EEPROM gespeichert werden. Meinem Mower wird zur Zeit noch manuell per App vorgegeben in welcher Zone er mähen soll. Befindet er sich mal außerhalb der ZielZone, tracked er solange den Perimeter, bis er wieder in der richtigen Zone ist.
Mein Problem ist jedoch weiterhin, dass meine Mähzonen über einen 80 cm breiten gepflasterten Weg miteinander verbunden sind.
Da mein Tracking noch mit sehr großen Schwingungen abläuft, musste ich die die Schleifenkabel mit einem Abstand von 15cm unter dem Pflaster verlegen. Das Signal wird jetzt vom Pflaster und auch von der dicht liegenden Rückschleife immens verschlechtert, sodass der Timeout ausgeführt wird. Ich habe gestern mal die Änderungen von Bernard eingebaut und hoffe jetzt, dass das Tracking deutlich stabiler funktioniert und ich die Schleifen wieder mit mehr Abstand zueinander verlegen kann. Konnte ich aber leider noch nicht testen.
Parallel verfolge ich immer noch das Ziel einer zusätzlichen Suchschleife. Aktueller Stand meiner Entwicklung ist, dass mein Sender nun 2 Schleifen anspricht und zeitverzögert zwei sich nicht korrelierende Orthogonalcodes überträgt. Das Ende der Suchschleife wird per Mittelabgriff an die Begrenzungsschleife angeschlossen.
zonen_2017-06-18-2.jpg

Ich habe die Perimeter.cpp so angepasst, dass das empfangene Signal nun zweimal durch den Matchfilter läuft und mit den beiden Orthogonalcodes des Senders verglichen wird. Anhand der 2 errechneten Mag Werte kann ich nun herausfinden, welche Schleife am nächsten ist. Das funktioniert schon mal alles zufriedenstellend.
Ich kämpfe aber noch mit dem Problem der Inside / Outside Erkennung. Denn wenn der Mower die Suchschleife B abfahren soll, bekommt er in der Zeit wo Schleife B ausgeschaltet ist von der der Schleife A ein Insidesignal. Es muss also irgendwie erreicht werden, dass er sich nur auf ein Signal fokussiert oder aber das Tracking der Suchschleife B nicht mehr mithilfe der In / Out Transition gemacht wird, sodern evtl mit absoluten MAG Werten. Zusätzlich muss er ja das Suchkabel B auch in beiden Richtungen abfahren können.
Würde mich über Vorschläge freuen.
Gruß Frederic
Attachment: https://forum.ardumower.de/data/media/kunena/attachments/3897/zonen_2017-06-18-2.jpg/
 
Zuletzt bearbeitet von einem Moderator:
Hi Frederic,

ich hatte ja änliches Problem mit der Schleife unter einem Pflasterweg.

Meine Lösung:

Ich arbeite jetzt mit Zwei Perimeterspulen (Rechts / Links)
für das Tracking benutzte ich die linke .
ZUdem habe ich heute den neuen Code für das Perimetertracking eingebaut.

Das Ding fähtr jetzt ziemlich Mittig über den Pflatserweg super geradeaus ohne schwingungen :)

EInfach perfekt der Code aus dem Thema : How I reduce vibration when tracking
 
Hallo,

wäre jemand so nett mir den GPS Mähzonen Code von al_ohr3 in die Aktuelle Azurit 1.0a7 zu importieren.

Habe jetzt schon Tage lang herumprobiert ohne Erfolg :(

Danke...

Gruß
Dery
 
Oben