Git Product home page Git Product logo

sunray's Introduction

Sunray firmware

Table of contents

  1. Description
  2. Sunray for Ardumower
  3. Sunray for Alfred
  4. Sunray Simulator
  5. Further topics

Description

Sunray firmware is an alternative Firmware (experimental) for...

sunray_platform

Platform Hardware required
Ardumower Ardumower kit mowing and gear motors, PCB 1.3, Adafruit Grand Central M4 (or Arduino Due) and ArduSimple RTK kit
Alfred Alfred robot with Alfred RTK conversion kit (tiny Linux computer, IO-board, ArduSimple RTK kit, base antenna etc.)

The robot mower uses RTK to localize itself (without a perimeter wire)

How does it work?

Platform Compilation
Ardumower The complete Sunray firmware and software drivers are compiled for a specific MCU (Due/M4)
Alfred Sunray firmware is compiled for Linux. Additionally, a tiny serial robot driver is compiled for the specific Alfred MCU (STM32). The Linux Sunray firmware will communicate with this serial robot driver to control motors, read sensors etc.
Simulator The complete Sunray firmware and simulated hardware is compiled for Linux.

Sunray for Ardumower

Warning

All software and hardware, software and motor components are designed and optimized as a whole, if you try to replace or exclude some component not as designed, you risk to damage your hardware with the software

Ardumower Wiki

http://wiki.ardumower.de/index.php?title=Ardumower_Sunray

Adumower Demo video

https://www.youtube.com/watch?v=yDcmS5zdj90

Ardumower Download

WARNING: Do not use the master version (via download button), that is 'code we work on' and it may be unstable - use one release version instead (click on 'releases' link below)!

https://github.com/Ardumower/Sunray/releases

Sunray for Alfred / Sunray for Raspberry PI

NOTE: Below steps are only required if you want to compile a custom version of the 'Sunray for Alfred' firmware. The code for all steps will require a Linux system (either the Alfred, a Raspberry PI or some PC).

How to install code and compile 'Sunray for Alfred' (required only once)

Run this on your Alfred Linux terminal (in your Alfred home folder):

## clone repository ##
cd ~
git clone https://github.com/Ardumower/Sunray.git

## compile sunray (NOTE: 'make' will always copy config.h in current makefile folder into Sunray source folder) ##
cd ~/Sunray/alfred/build
rm -Rf *
cmake ..
make

## install new sunray executable ##
sudo systemctl stop sunray
cp sunray ~/sunray_install/
sudo systemctl start sunray

How to update installed code and re-compile 'Sunray for Alfred'

## update repository ##
cd ~/Sunray
git pull

## compile sunray (NOTE: 'make' will always copy config.h in current makefile folder into Sunray source folder) ##
cd ~/Sunray/alfred/build
rm -Rf *
cmake ..
make

## install new sunray executable ##
sudo systemctl stop sunray
cp sunray ~/sunray_install/
sudo systemctl start sunray

How to compile and flash 'Alfred MCU firmware'

NOTE: This step is only required if you want to compile a custom version of the Alfred MCU firmware. The Alfred MCU firmware can be compiled using Arduino compiler on the Alfred. The compiled Alfred MCU firmware will then be flashed to the Alfred MCU using OpenOCD.

cd sunray_install
sudo ./flash.sh
  1. Choose 'Install Arduino IDE' (this step may not be required for Alfred SD card images)
  2. Choose 'Build+Flash NGP firmware (Sunray-compatible)'

How to compile 'Sunray for Alfred' on a Raspberry PI (OS Lite 64 bit, Debian Bullseye)

Before running above commands, install required libs:

sudo apt-get -y install cmake
sudo apt-get -y install libbluetooth-dev

For Raspberry PI, you may have to adjust the serial path for the Alfred MCU UART connection in 'alfred/config.h':

#define SERIAL_ROBOT_PATH "/dev/ttyS0" 

You can find out the correct UART serial path using: 'dmesg | grep uart'. NOTE: you may have to stop running services accessing the UART serial path:

# find out processes accessing the UART serial path:
sudo lsof /dev/ttyS0
# list all running services:
sudo systemctl list-units --type=service --state=running
# stopping/disabling service
sudo systemctl stop [email protected]
sudo systemctl disable [email protected]

How to use more robust Bit-bangling-based instead ARM-based I2C driver on a Raspberry PI (OS Lite 64 bit, Debian Bullseye)

The Raspberry CPU-based I2C driver has certain issues (e.g. missing clock stretching for BNO055, missing SCL recovery in noisy environment etc.) You can switch from the Raspberry ARM-I2C-driver to a more robust software-based driver (aka 'bit-bangling') like this:

  1. Run 'sudo raspi-config' and disable the ARM I2C driver
  2. Run 'sudo nano /boot/config.txt' and add this line to activate the software-based I2C driver: dtoverlay=i2c-gpio,bus=1,i2c_gpio_sda=2,i2c_gpio_scl=3
  3. Reboot ('sudo reboot')
  4. Verify the I2C bus is working (e.g. a MPU 6050 IMU should be detected at address 69): sudo i2cdetect -y 1

How to compile 'OpenOCD' on a Raspberry PI (OS Lite 64 bit, Debian Bullseye)

OpenOCD is used to flash the Alfred MCU firmware via GPIO interface (SWD emulation). Run this in your 'pi' home folder. The compiled binary ('openocd') can be found in folder 'src'. The binary will be called by the flash script ('~/sunray_install/flash.sh') to flash the Alfred MCU firmware.

sudo apt-get -y install libusb-1.0-0
sudo apt-get -y install libusb-1.0-0-dev
sudo apt-get -y install pkg-config
sudo apt-get -y install libtool
git clone --recursive https://github.com/lupyuen/openocd-spi
cd openocd-spi
git remote add zorvalt https://github.com/Zorvalt/openocd-spi/
git pull zorvalt fix-multiple-gcc-10-errors
./boostrap
./configure --disable-internal-libjaylink --enable-sysfsgpio
make

Please note that the OpenOCD config ('~/sunray_install/config_files/openocd/swd-pi.ocd') has to be adjusted as the GPIO pins have different numbers:

# SWD banana-pi wiring:
# CON1-P18 	SWDIO   GPIO-10  (raspi GPIO24)
# CON1-P22 	SWCLK 	GPIO-47  (raspi GPIO25)
# CON1-P16 	SRST2 	GPIO-11  (raspi GPIO23)  -- main MCU
# CON1-P24 	SRST1 	GPIO-20  (raspi GPIO8)   -- perim MCU

sysfsgpio_swdio_num 24
sysfsgpio_swclk_num 25
sysfsgpio_srst_num 23

How to configure Bluetooth BLE module on a Linux computer

NOTE: This step is only required if you don't use the Alfred Linux SD card image.

echo "----bluetooth devices----"
hcitool dev
# configure bluetooth BLE module
echo "----BLE config----"
echo 12 > /sys/kernel/debug/bluetooth/hci0/conn_min_interval  # 24   6
echo 20 > /sys/kernel/debug/bluetooth/hci0/conn_max_interval  # 40   6
echo 1 > /sys/kernel/debug/bluetooth/hci0/conn_latency       # 0    1
btmgmt -i hci0 power off
btmgmt -i hci0 le on
btmgmt -i hci0 bredr off
btmgmt -i hci0 connectable on
btmgmt -i hci0 name "alfred"
btmgmt -i hci0 advertising on
btmgmt -i hci0 power on

How to compile 'Sunray Simulator'

The simulator will simulate an Ardumower/Alfred on a Linux computer. It uses the same firmware with simulator drivers for all robot hardware (motors, bumper, GPS, etc.). It will also simulate all network-based interfaces (HTTP, MQTT etc.) and support phone connections via Bluetooth BLE (see section above how to configure BLE module on Linux).

Run this on your Linux terminal:

## clone repository ##
cd ~
git clone https://github.com/Ardumower/Sunray.git

Now edit the file alfred/config.h and uncomment only the simulation driver:

//#define DRV_SERIAL_ROBOT  1   // for Alfred
//#define DRV_ARDUMOWER     1   // keep this for Ardumower
#define DRV_SIM_ROBOT     1   // simulation

Finally, compile and run the simulator:

cd ~/Sunray/alfred/build
rm -Rf *
cmake ..
make
sudo ./sunray

When uploading a map, simulator will set the robot position to the uploaded docking point. Using the Sunray App, you can simulate sensors like this:

Keyboard key Sensor simulation
o Trigger robot obstacle sensor (bumper etc.)
r Trigger robot rain sensor
l Trigger robot battery low

Fixing issue: 'available(): not listening'

If you restart the Linux sunray process, it might be possible that the HTTP port is still not cleared from a previous session. A quick check before running the process will solve this:

echo "----waiting for TCP connections to be closed from previous sessions----"
echo "Waiting TCP port 80 to be closed..."
for _ in `seq 1 30`; do
  RES=$(netstat -ant | grep -w 80)
  if [ -z "$RES" ]; then
    break
  fi
  echo $RES
  sleep 2.0    
done;

Further topics

Generating robot heatmaps (WiFi/GPS signal quality etc.): https://forum.ardumower.de/threads/advanced-topic-generate-wifi-gps-heatmaps-with-sunray-on-alfred-or-ardumower-with-connected-raspberry-pi.25078/

sunray's People

Contributors

eineinfach avatar felixganzer avatar greymfm avatar hjw2015 avatar jopl avatar mw46d avatar nefiertsreblis avatar rstonkus avatar shadedself avatar sirvival71 avatar svol0 avatar timotto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sunray's Issues

Problem in "comm.cpp"

I noticed is the following problem in "comm.cpp":

Commands received from BLE and CONSOLE share the same buffer "cmd".

If for example CONSOLE sends "ABC" PAUSE "DE\n"
(PAUSE means there is a delay between "ABC" and "DE\n")
and in parallel BLE sends "FGH\n"

then received BLE string in "cmd" is "ABCFGH\n" and the result is a checksum error.

The problem can be reproduced when using the linux terminal program "screen" on a Raspberry Pi. This program does not buffer keyboard input until ENTER is pressed (like "putty" does), instead it sends keyboard input immediately.

SMOOTH_CURVES turning with full speed

Hi there,
I turned on the SMOOTH_CURVES option within the config.h.
After that change the robot slows down near the target point and goes then into full speed for turning. Every turn is now in full speed.
Could you check this please

App: Make audible sound if float or invalid during perimeter training

Ein weiter Punkt betrifft die Sunray APP. Mir persönlich ist es schon zweimal passiert das beim Abfahren des virtuellen Perimeters, das GPS Signal schlecht war und somit der virtuelle Perimeter in der Steinrabatte eingezeichnet wurde. Leider erst bemerkt als der Ardumower die Steine schneiden wollte (hat einen Satz Messer gekostet und ein hoffentlich nix kaputt). Vielleicht wäre es möglich in der APP entweder das Feld mit Fix/Float/Invalid anders zu positionieren oder eine optische eventuell auch Tonsignal wenn das GPS Signal von Fix auf Float oder invalid umspringt.

Support arbitrary mower GPS antenna position

Currently, the GPS antenna is assumed to be in the rotation center of the mower. The map heading (alpha in degree) is calculated as shown in the image below. We should support an arbitrary GPS antenne position (e.g. cross point) by:

  1. By using IMU, back-rotating the current mower rotation (top mower in the image) to the reference mower rotation (bottom mower in the image)
  2. Computing the map heading (alpha in degree) as usual

German: Es geht um die Berechnung von delta über GPS-Koordinaten für den Fall wenn die GPS-Antenne woanders sitzt - dazu wird der roboter so gedreht dass er dieselbe Orientierung wie bei der "vorigen" GPS-Position hat ... und dann wird ganz normal über arcutans über die Geraden der Winkel bestimmt...
Wohlgemerkt: Das GPS wird das delta nicht schlagartig ändern - es filtert es nur langfristig (Tiefpass), kurzfristigig (Hochpass) hat immer die IMU das sagen - nur für die Berechnung des GPS-Winkels muss die Drehung für 2 aufeinander folgende GPS-Messpunkte identisch sein wenn die GPS-Antenne woanders sitzen soll.

#59

gps_course

raspberry compile support

Hi, trying to compile on raspbery(raspiarduino) and getting compilation errors:

sunray/src/raspi/raspi.h:6:25: fatal error: LinuxSerial.h: No such file or directory

did I wrongly supposed sunray code can run on raspberry with low level dive running on AGM4 via serial driver ?
Thanks

PWM frequency

Hi,

In der pinman.cpp wurde während des refactorings die TCC_TOP von 0X80 auf 0XFF für den SAMD51 mit brushed Motoren geändert. Damit ändert sich die PWM Frequenz von 3.6 kHz auf 1.8 kHz. War das Absicht oder ein versehen?

Missing driver call in battery.cpp

Line 130 in battery.cpp
if ((switchOffAllowedIdle) || (switchOffByOperator)) digitalWrite(pinBatterySwitch, LOW);
should read
if ((switchOffAllowedIdle) || (switchOffByOperator)) batteryDriver.keepPowerOn(false);

robot in-place rotating for a second in the wrong direction, then correcting

With activated IMU (not with odometry-only), the robot sporadically in-place rotates for a second in the wrong direction, then corrects and rotates into the right direction.
Idea: log a session with rotation data (IMU angle, set-rotation angle, current-rotation angle etc.) to find out if it's a calculation or maybe hardware issue

experiment with low-pass filtering GPS float solution

Currently, robot wobbles around tracked path if using GPS float solution. Try out if low-pass filtering GPS float solution would help.

Idea:
In function 'void computeRobotState()' (robot.cpp, line 931), try out a low-pass filter for GPS float solution (similar to what we do to low-pass filter the GPS yaw angle) :

small low-pass filtering:

if (maps.useGPSfloatForPosEstimation){ // allows planner to use float solution?
  if  (stateGroundSpeed > 0.03) {
    stateX = 0.99 * stateX + 0.01 * posE;  // this will only use GPS float positions as 'long-term' values
    stateY = 0.99 * stateY + 0.01 * posN;
  } else {
    stateX = posE;
    stateY = posN; 
  }
}

add DOCK_AUTO_START delay

Fehlerbeschreibung: Der Mähroboter fährt beim automatischen Docking
(Akku leer) in die Ladekontakte, startet aber sofort wieder den
Mähvorgang bei aktiviertem „DOCK_AUTO_START“ (insbesondere bei
Verwendung von Ladegeräten, die den Ladestrom erst langsam ansteigen
lassen).
Ursache: Die Variable „chargingCompleted“ kann, wenn zum Zeitpunkt
der Abfrage gerade die Bedingung „chargerConnectedState“ erfüllt
ist, aber der Ladestrom noch nicht angestiegen ist, schon „TRUE“
sein, wodurch dann in der „robot.cpp“ die Bedingung
„battery.chargingHasCompleted()“ erfüllt ist und der Mähvorgang
automatisch fortgesetzt wird.
Abhilfe: Wenn der Mäher nicht an den Ladekontakten ist
(„chargerConnectedState“ = FALSE), wird die Variable
„chargingCompleted“ ebenfalls auf FALSE gesetzt. Wechselt
„chargerConnectedState“ auf TRUE, so wird erst eine gewisse Zeit
gewartet, bis auf den Zustand des Ladestroms und der Ladespannung
geschaut wird.

Mäher fährt sofort wieder aus Ladestation heraus_20210906.pdf

Die gelb hinterlegten Codeteile sind in der „battery.h“ einzufügen:  
class Battery { 
  public: 
    bool batMonitor; 
    float batGoHomeIfBelow; 
    float batFullVoltage; 
    float batSwitchOffIfBelow;  // switch off battery if below voltage (Volt) 
    int batSwitchOffIfIdle;      // switch off battery if idle (minutes)   
    int enableChargingTimeout; 
    float batFullCurrent; 
    float batteryVoltage;   // volts 
    float chargingVoltage;  // volts 
    float chargingCurrent;  // amps 
    bool chargingEnabled; 
    int chargingCompletedDelay; // ensure that loadingcurrent or loadingvoltage triggers 
“chargingCompleted” condition for a longer periode  
    bool chargingCompleted; 
    void begin();             
    void run();    
    bool chargerConnected(); 
    void enableCharging(bool flag);           
    bool shouldGoHome();     
    bool chargingHasCompleted(); 
    bool underVoltage(); 
    void resetIdle(); 
    void switchOff(); 
  protected:        
    unsigned long nextBatteryTime ; 
    bool switchOffByOperator;     
    unsigned long timeMinutes; 
    bool chargerConnectedState; 
    bool switchOffAllowedUndervoltage; 
    bool switchOffAllowedIdle; 
    unsigned long switchOffTime; 
    unsigned long chargingStartTime; 
    unsigned long nextCheckTime;    
    unsigned long nextEnableTime;    
    unsigned long nextPrintTime;        
}; 
 
 
 
Die gelb hinterlegten Codeteile sind in der „battery.cpp“ 
einzufügen:  
void Battery::run(){   
  if (millis() < nextBatteryTime) return; 
  nextBatteryTime = millis() + 50; 
   
  float voltage = batteryDriver.getChargeVoltage(); 
 
  if (abs(chargingVoltage-voltage) > 10) chargingVoltage = voltage;   
  chargingVoltage = 0.9 * chargingVoltage + 0.1* voltage;   
 
  voltage = batteryDriver.getBatteryVoltage(); 
  if (abs(batteryVoltage-voltage) > 10) batteryVoltage = voltage;   
  float w = 0.995; 
  if (chargerConnectedState) w = 0.9; 
  batteryVoltage = w * batteryVoltage + (1-w) * voltage;   
 
  chargingCurrent = 0.9 * chargingCurrent + 0.1 * batteryDriver.getChargeCurrent();     
   
  if (!chargerConnectedState){ 
    if (chargingVoltage > 5){ 
      chargerConnectedState = true;        
      DEBUGLN(F("CHARGER CONNECTED"));                      
      buzzer.sound(SND_OVERCURRENT, true);         
    } 
  } 
   
  if (millis() >= nextCheckTime){     
    nextCheckTime = millis() + 5000;            
    if (chargerConnectedState){         
      if (chargingVoltage <= 5){ 
        chargerConnectedState = false; 
        nextEnableTime = millis() + 5000;    // reset charging enable time          
        DEBUGLN(F("CHARGER DISCONNECTED"));                               
      } 
    }           
    timeMinutes = (millis()-chargingStartTime) / 1000 /60; 
    if (underVoltage()) { 
      DEBUGLN(F("SWITCHING OFF (undervoltage)"));               
      buzzer.sound(SND_OVERCURRENT, true); 
      if (switchOffAllowedUndervoltage)  batteryDriver.keepPowerOn(false);      
    } else if ((millis() >= switchOffTime) || (switchOffByOperator)) { 
      DEBUGLN(F("SWITCHING OFF (idle timeout)"));               
      buzzer.sound(SND_OVERCURRENT, true); 
      if ((switchOffAllowedIdle) || (switchOffByOperator)) batteryDriver.keepPowerOn(false); 
    } else batteryDriver.keepPowerOn(true);               
             
    if (millis() >= nextPrintTime){ 
      nextPrintTime = millis() + 60000;            
      //print();      
      /*DEBUG(F("charger conn=")); 
      DEBUG(chargerConnected()); 
      DEBUG(F(" chgEnabled=")); 
      DEBUG(chargingEnabled); 
      DEBUG(F(" chgTime="));       
      DEBUG(timeMinutes); 
      DEBUG(F(" charger: "));       
      DEBUG(chargingVoltage); 
      DEBUG(F(" V  "));     
      DEBUG(chargingCurrent);    
      DEBUG(F(" A "));          
      DEBUG(F(" bat: ")); 
      DEBUG(batteryVoltage); 
      DEBUG(F(" V  "));     
      DEBUG(F("switchOffAllowed="));    
      DEBUG(switchOffAllowed);       
      DEBUGLN();      */           
    }  
  } 
   
  if (millis() > nextEnableTime){ 
    nextEnableTime = millis() + 5000;             
    if (chargerConnectedState){        
        // charger in connected state 
        if (chargingEnabled){ 
          //if ((timeMinutes > 180) || (chargingCurrent < batFullCurrent)) {         
          if (**chargingCompletedDelay** > 5) {  // chargingCompleted check first after 6 * 5000ms 
= 30sec. 
            chargingCompleted = ((chargingCurrent <= batFullCurrent) || (batteryVoltage >= 
batFullVoltage)); 
          } 
          else {           
            **chargingCompletedDelay**++;  
          } 
 
          if (chargingCompleted) { 
            // stop charging 
            nextEnableTime = millis() + 1000 * enableChargingTimeout;   // check charging 
current again in 30 minutes 
            chargingCompleted = true; 
            enableCharging(false); 
          }  
        } else { 
           //if (batteryVoltage < startChargingIfBelow) { 
              // start charging 
              enableCharging(true); 
              chargingStartTime = millis();   
          //}         
        }     
    } 
    else { 
      // reset to avoid direct undocking after docking   
      **chargingCompleted**       = false; 
      **chargingCompletedDelay**  = 0;  // reset chargingCompleteted delay counter 
    } 
     
  } 
} 

reset the GPS receiver after x min FLOAT

Now and then it happens that the mover stops for a long time with "FLOAT" and waits for a "FIX".
Most of the time, a manual reset of the GPS receiver helps.

This could be automated with an adjustable time in config.h.

define GPS_REBOOT_RECOVERY after FLOAT_Time true

define GPS_REBOOT_RECOVERY FLOAT_Time 30

Asymmetrical route planner

Asymmetrically arranged mowing disc so that it is easier to mow along one edge. Then it is also important that the route planner takes this into account and always approaches the edges on the correct side so that it can get further into the corners.

Record mode: Dock

Hi,

This project is really nice.
I have one question. I was following sunray mobile app guide and I cannot find Record mode: dock.
Can you please write does this option still exist and if yes what can be the reason that I only see Record mode: perimeter | waypoint | exclusion?

Automatic changing mowing patterns to avoid permanant paths

Different mowing patterns, changing automatically, so that the same paths are not always mowed and these do not lead to permanent tracks. For example, a constantly changing mowing angle between the sigle mowing attempts would be sufficient.

add local planner

currently, line tracker uses points from global planner (map points generated by app or dynamic points generated by path planner for obstacle avoidance) -
instead, line tracker should use points from a local planner (to be added, using itself global planner points), so local plans can be dynamically adjusted to surrounding global planner points, can consider robot chassis footprints etc.

Screenshot from 2021-08-27 18-17-01

RingBuffer.cpp contains undefined behaviour

The comparison in class ERingBuffer.endsWith():61 is undefined behaviour.
According to the C++ standard it is only defined to compare two pointers if they belong to the same array, or if one of them points to the 1st element beyond the array. Standard reference further below.

So it is not allowed to check if b is before ringBuf, the begin of the array. This may work, but the compiler is allowed to optimise on this undefined behaviour. And the gcc is known for optimising aggressively.

At the moment it does not seem that the class is been used in the repository. If it is planned to use it, I am happy to provide a correct fix, otherwise I recommend to remove it from the code base.

See C++17 standard 8.9
Comparing unequal pointers to objects is defined as follows:
(3.1) — If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to
the element with the higher subscript compares greater.
(3.2) — If two pointers point to different non-static data members of the same object, or to subobjects of such
members, recursively, the pointer to the later declared member compares greater provided the two
members have the same access control (Clause 14) and provided their class is not a union.
(3.3) — Otherwise, neither pointer compares greater than the other.
4 If two operands p and q compare equal (8.10), p<=q and p>=q both yield true and pq both yield
false. Otherwise, if a pointer p compares greater than a pointer q, p>=q, p>q, q<=p, and q<p all yield true
and p<=q, p<q, q>=p, and q>p all yield false. Otherwise, the result of each of the operators is unspecified.

add retry to path planner

In 'setOperation(OP_MOW)' and 'setOperation(OP_DOCK)' the path planning may fail ('no map route') if robot is in RTK float or RTK invalid solution state. Robot should retry (in certain time intervals) path planning and not stick to the first invalid plan.

PID controller integration

I saw in the file Sunray/sunray/pid.cpp that in line 37, the integrated error is computed as:

esum += e;

Where as in line 41-43:

y = Kp * e+ Ki * Ta * esum + Kd/Ta * (e - eold);

I think the correct way to calculate the integrated error is:

esum += e * Ta;

Whereas y is defined as:

y = Kp * e + Ki * esum + Kd/Ta * (e - eold);

Maybe the current version works as well, but the value of the gain will be different.

line tracking : should we still track lines if robot position is floating?

idea arised from this forum thread topic: https://forum.ardumower.de/threads/probleme-bei-ausfahrt-aus-der-ladestation.24409/post-48976

Think about this: if we assume robot's position as FLOAT it's hard to get track a line. However, if we just track the angle to the target, approaching a target will be more smooth...

IDEA: only track the line (lateral and angle control) if FIX solution - if FLOAT solution only track the angle to target (angle control)...

Below is the general tracking formula used in the tracking controller (more details on this type of controller can be found here: https://medium.com/@dingyan7361/three-methods-of-vehicle-lateral-control-pure-pursuit-stanley-and-mpc-db8cc1d32081 ) .

image

Brushless define

Hi,
In case of JYQD driver, the enable is HIGH level.
in this function with the brushless define the enable is LOW

void AmMotorDriver::begin(){
#ifdef MOTOR_DRIVER_BRUSHLESS
// logic for brushless drivers
CONSOLE.println("MOTOR_DRIVER_BRUSHLESS: yes");
faultActive = LOW;
enableActive = LOW;

If some other driver needs a LOW level, does it make sense to have an additional new define for the JYQD driver? Something like that ?
#ifdef MOTOR_DRIVER_JYQD
enableActive = HIGH
#else
enableActive = LOW;
#endif
thanks

Problem compiling

Hi!

I get this when compiling version 1.0.230:

Sunray-1.0.230\sunray\src\mpu\inv_mpu.c:53:1: error: no return statement in function returning non-void [-Werror=return-type]

Seems to be related to an empty

static inline int reg_int_cb(struct int_param_s *int_param)
{

}

Thanks in advance.

map setSpeed as maximum speed for navigation by joystick from sunray-app

It is possible to navigate the mower by touch-joystick in sunray-app.
In some cases it could be neccessary to navigate the mower very soften, especially when your connected by wifi to the mower.
If parameter is set to true, the speed value from app will be used for maximum speed by joystick control. To navigate soften, change the speed slider for example to 0.10. If you need to let the mower drive long distance without accurate positioning change the speed slider to 0.30.

Add Parameter to config.h:

// It is possible to navigate the mower by touch-joystick in sunray-app. In some cases it could be neccessary to navigate the mower very soften, especially when your connected by
// wifi to the mower. If parameter is set to true, the speed value from app will be used for maximum speed by joystick control. To navigate soften, 
// change the speed slider for example to 0.10. If you need to let the mower drive long distance without accurate positioning change the speed slider to higher values. 
#define USE_SETSPEED_FOR_APPJOYSTICK true  // setting of setSpeed is used for maximum speed of mower control with joystick in sunray-app.
//#define USE_SETSPEED_FOR_APPJOYSTICK false  // mower will drive at a maximum speed of 0.33m/sec by control with joystick in sunray-app.

Change in comm.cpp: (position at line 117 at release 1.0.219)
change

      if (counter == 1){                            
          linear = value;
      } else if (counter == 2){
          angular = value;
      }

to

      if (counter == 1){                            
          linear = value;
          if (USE_SETSPEED_FOR_APPJOYSTICK) {
            // map the manual control value for linear speed from range 0.00 till 0.33 to the range of 0.00 till setSpeed but at least 0.10
            linear = map((value*100),0,33,0,max(setSpeed*100,10));
            linear = linear / 100;
          }
      } else if (counter == 2){
          angular = value;
          if (USE_SETSPEED_FOR_APPJOYSTICK) {
            // map the manual control value for angular speed from range 0.00 till 0.50 to the range of 0.00 till setSpeed but at least 0.10
            angular = map((value*100),0,50,0,max(setSpeed*151,15));
            angular = angular / 100;
          }
       }

Strange error message: "motor malfunction pwm=0,0,-219.43"

I get the following error message some seconds after switching on the mow motor:
"ERROR: motor malfunction pwm=0,0,-219.43 sense=0.00,0.00,0.00"

This can be HW problem of my Ardumower. Nevertheless, it is strange that the SW reports a negative PWM value.

ESP32_BLE and Sunray

Hi!

I am not sure support for the ESP32 is working the latest firmware .230. It seems it references code for the HM-10 module. Serial output from the main board says it can't find Bluetooth.

Jonas

add maximum drive speed limitation in config.h

Since it has been possible to increase the maximum driving speed from 0.39m/sec to 0.59m/sec according to a modification in the Sunray app, I would like to extend the config.h with the following parameter:
#define MOTOR_MAX_SPEED 0.30 // limitation for setSpeed value from Sunray-App (0,01 to 0,59m/sec are possible) to avoid too high speed setting by mistake

Change the line if (floatValue >= 0) setSpeed = floatValue; in comm.cpp (line 79 release V1.0.219) into

          if (floatValue >= 0) {
            setSpeed = floatValue;
            if (setSpeed > MOTOR_MAX_SPEED) setSpeed = MOTOR_MAX_SPEED;   // limitation for setSpeed (please see "MOTOR_MAX_SPEED" in config.h) //SOew
          }

It has happened to me a few times -due to carelessness- that I accidentally set the slider on „too high“ speed value. The drives could not reach this speed, which meant that the mower could not work any longer correctly.
The maximum drive speed for the mower can be limited to a value that matches the system.

Motor error when doing rotation

Rotation speed too high in mode SMOOTH_CURVES causing motor error

Ardumower processing map (mode line) rotate to fast after reaching a waypoint.
This is stopping mowing and indicating motor error

Configuration:

  • Sunray App v.1.0.62
  • FW 1.0.66
  • PCB 1.3 Ardumower with RTK GPS without IMU and without US Sensor
  • #define TICKS_PER_REVOLUTION 696 / 2 // odometry ticks per wheel revolution
  • #define ENABLE_FAULT_DETECTION true
  • #define SMOOTH_CURVES true
  • Speed 0.2 m/s, Pattern angle 0, Lines

Error message in console

  • ERROR: motor malfunction pwm=133,-59,-255.00 sense=0.10,0.18,0.00
    motor error!

Proposal: Check version of linked sunray library

  • Version of sunray library linked should be checked at the startup and a warning message should be printed on console
    if an outdated library is used.

reset GPS receiver after undocking

problem:

Ich hatte gestern noch das Phänomen das der Mover mit einem um 30cm Versatz GPS FIX aus der LS kam. Da er damit auch FIX hatte und auch noch innerhalb des Perimeter war störte den Mover das nicht. Er ist dann natürlich überall gegengerammelt.

Ein manueller Reset des GPS Empfängers brachte dann den richtigen FIX.

Kann man vielleicht optinal über die config.h noch einen zeitgesteuerten Reset des GPS Empfängers nach dem undock auslösen?
Also Startbefehl -> 20sek. (Einstellbar über config.h) später Reset des GPS Empfängers.

Da er ja nach 20sek aus der LS ist, ist dann ja auch immer freie Sicht und guter GPS Empfang. Da kommt dann auch immer der richtige FIX.

solution:
reset GPS receiver after undocking

OBSTACLE_DETECTION_ROTATION endless loop

Situation: robot mower looking down a hill (with freewheel at front):

  1. it rotates and due to ground slippage, OBSTACLE_DETECTION_ROTATION triggers
  2. it tries to drive in reverse a few cm (without effect because driving in reverse a hill does not work on a hill)
  3. it restarts with 1.

and that looping is endless

Idea: temporary turn off 'OBSTACLE_DETECTION_ROTATION' detection one time after three fails (if detected no GPS motion)

detect stuck during rotations

  1. normal Ardumower chassis (freewheel back): if robot cannot rotate to goal (IMU feedback), drive forward a few cm
  2. reverse Ardumower chassis (freewheel front): if robot cannot rotate to goal (IMU feedback), drive reverse a few cm

implement plugin system

add plugin system to App+firmware, so users can add their own code plugin to the firmware and control it via the App

Clock config and Cache enabled

I don't see any clock configuration or SAMD51 cache enable instructions inside the sources.

Why is that so? Did I missed it?
At what frequency runs the CPU?

AmRobotDriver.cpp with Adafruit M4

Looking at the Adafruit Reference for the Grand Central M4 I found out :

Pin Outputs & Pullups
The old-style way of turning on a pin as an input with a pullup is to use pinMode(pin, INPUT)

This is because the pullup-selection register on 8-bit AVR chips is the same as the output-selection register.
For M0 & M4 boards, you can't do this anymore! Instead, use: pinMode(pin, INPUT_PULLUP)

In d AmMotorDriver::begin() there is a mixture of Old an New Style.
In my case the Pin 25 never changed the logic level. After changing to INPUT_PULLUP everthing works fine

introduce config setting to disable mowing motor outside perimeter

currently, we have:

for lanes:
// detect robot being kidnapped? robot will go into error if distance to tracked path is greater than a certain value
#define KIDNAP_DETECT true
#define KIDNAP_DETECT_ALLOWED_PATH_TOLERANCE 1.0 // allowed path tolerance (m)

for perimeter:
#define ALLOW_ROUTE_OUTSIDE_PERI_METER 1.0 // max. distance (m) to allow routing from outside perimeter

request: introduce config setting to disable mowing motor outside perimeter/inside exclusions
"Allow Mow outside Perimeter 0.0"

DOCK_AUTO_START should not apply to docking triggered by rain sensor

Wenn der Mover durch Regensensor oder auch manuell in die Station geschickt wird, der Rasen noch nicht komplett gemäht ist und dann die Ladegrenze erreicht wird will er wieder rausfahren.
Das sollte so nicht sein - eigentlich sollte er nur wieder rausfahren wenn er wegen niedriger Batteriespannung zum Nachladen in die LS fährt.

mower motor power modulation

test this mower motor power modulation, and if it works well integrate it into the code...

void Robot::motorMowControl() {
//bber60
  if (millis() < nextTimeMotorMowControl) return;
  nextTimeMotorMowControl = millis() + 100;
  if (motorMowForceOff) motorMowEnable = false;
  //Auto adjust the motor speed according to cutting power (The goal is On high grass the motor rotate faster)
  //A runningmedian process is used to check each seconde the power value of mow motor
  //if power is low the speed is reduce to have a longer mowing duration and less noise.
  if (motorMowEnable) {
    motorMowPowerMedian.add(motorMowPower);
    if (motorMowPowerMedian.getCount() > 10) { //check each 1 secondes
      int prevcoeff =  motorMowPwmCoeff;
      motorMowPwmCoeff = int((100 * motorMowPowerMedian.getAverage(4)) / (0.5 * motorMowPowerMax));
      if (motorMowPwmCoeff < prevcoeff) {
        //filter on speed reduce to keep the mow speed high for longuer duration
        motorMowPwmCoeff = int((0.1) * motorMowPwmCoeff + (0.9) * prevcoeff);// use only 10% of the new value
      }
      if ((statusCurr == WIRE_MOWING) || (statusCurr == SPIRALE_MOWING)) motorMowPwmCoeff = 100;
      if (motorMowPwmCoeff > 100) motorMowPwmCoeff = 100;
      if (motorMowEnable) {
        motorMowSpeedPWMSet = motorMowSpeedMinPwm + ((double)(motorMowSpeedMaxPwm - motorMowSpeedMinPwm)) * (((double)motorMowPwmCoeff) / 100.0);
      }
      if (motorMowSpeedPWMSet < motorMowSpeedMinPwm) motorMowSpeedPWMSet = motorMowSpeedMinPwm;
      if (motorMowSpeedPWMSet > motorMowSpeedMaxPwm) motorMowSpeedPWMSet = motorMowSpeedMaxPwm;
      //max speed on wire and spirale
      motorMowPowerMedian.clear();
    }
  }
  else
  {
    motorMowSpeedPWMSet = 0;
  }
  if (stateCurr == STATE_ERROR) {
    setMotorMowPWM(0, false); //stop immediatly on error (tilt etc....)
  }
  else
  {
    setMotorMowPWM(motorMowSpeedPWMSet, true);
  }
}

Current Motor calculation inverted

Hi,

in void AmMotorDriver::getMotorCurrent(float &leftCurrent, float &rightCurrent, float &mowCurrent) function
the leftCurrent is based on the Right Sense pin and the right Current is based on the Left Sense pin :
leftCurrent = (((float)ADC2voltage(analogRead(pinMotorRightSense))) + offset) *scale;
rightCurrent = (((float)ADC2voltage(analogRead(pinMotorLeftSense))) + offset) *scale;
should be the opposite no ?
thanks

error: no map route

Dear ardumower sunray enthusiastics, I successfully built the ardumower sunray model with standalone RTK. Everything seems to be working during creating the map, but after map is created and calculated, the tracking lines appears in the map and system writes the error "no map route" and rover doesn't want to start mowing. Is it possible to have such issue due to I am using free license, please? And does it solve after purchase of new license? Or what other problem could be the reason for such error message?
Thank you for your answer in advance. Kind regards, jopl.

Warning mode in high traffic areas

"Warning mode" when pedestrian and vehicle traffic is very likely and requires special attention, e.g. when passing sidewalks between different mowing areas. Warning could be realized by acoustic and/or visual warning.

path trackig going wrong sporadically (robot kidnaps itself)

Very sporadically (every few hours or so), the path tracking is going wrong. If it happens, the mower has good GPS signal (fix), but is driving completely away from the planned path...

The problem might be related to the path-tracking formula (stanley controller):

grafik
https://medium.com/@dingyan7361/three-methods-of-vehicle-lateral-control-pure-pursuit-stanley-and-mpc-db8cc1d32081

Current workaround for this bug (this is NOT a bugfix):
in config.h, activate :
#define KIDNAP_DETECT true
...so robot at least detects if being kidnapped (or is kidnapping itself) :-) (it will then go into ERROR mode)

Here you can see how the robot drives towards a not planned waypoint:
Screenshot from 2020-09-29 13-20-48

Asynchronous buzzer call has no effect

The async buzzer call in battery.cpp (line 129) has no effect if mower is switched off.
buzzer.sound(SND_OVERCURRENT, true);
if ((switchOffAllowedIdle) || (switchOffByOperator)) digitalWrite(pinBatterySwitch, LOW);

"buzzer.sound(SND_OVERCURRENT, false)" should be used instead.

Short term no GPS, navigation can go on

If GPS fails for 10-20 seconds, then continue with compass and odometry. If the wheels have rubber studs or similar, they should have almost no slip. This should allow the mower to drive and navigate some few meters without GPS fix.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.