Sunfounder Sloth won't calibrate, and then falls over

I got a Sunfounder Sloth robot for Christmas. I teach high school computer science, but I'm not as skilled when it comes to robotics, so I'd love a breakdown of what's going wrong here.

I plug the robot in, upload the calibration code, then turn it on. The feet instantly rotate in one direction and it falls over. The legs work fine if I keep them at 0. No changes to the code or the angle of rotation have fixed this. I've tried searching everything, but I can't find a single source that tells me what might be going wrong.

It also seems that it won't recognize the ultrasonic sensor anymore, but that's a whole other can of tomatoes.

Sounds like it is defective. Can you return it for replacement?

Unfortunately no. I actually got a defective servo with it and replaced it with one I had because they need the order number, and it was a gift. So I don't have that number, and I don't know who sent it because it was a teacher gift.

The components shouldn't be too hard to replace. The servos seem to work appropriately, they just won't calibrate right. The sensor simply doesn't work anymore, so that should probably be replaced. Maybe I can get a new nano for it to see if that's the problem.

Are all feet servos tilting at the same angle? If the feet servos are tilting to make it fall over, the rocker arms installed "upside down" or being commanded to that unstable angle by code or are the feet on the right pin assignment? This image shows it standing at 45 degrees.


This video steps you through testing each component, HC05, servos, et c. Video shows "feet" are to rotate to 90 degrees, and the "run" routine is to be commented-out (minute 18:00)

I've triple checked. They are in the correct position, and on the correct pins. I wonder if there's something in the code.

You can post your code here (use the </> code tag).

#include "VarSpeedServo.h"  //include the VarSpeedServo library
#include <NewPing.h>        //include the NewPing library
//#include <Servo.h>

VarSpeedServo RU;  //Right Upper
VarSpeedServo RL;  //Right Lower
VarSpeedServo LU;  //Left Upper
VarSpeedServo LL;  //Left Lower

NewPing sonar(4,3,200); 
                                                     //vel(min), delay_Forward(max) = (5, 2000) 
const int vel = 20, vel_Back = 10;                   //vel(mid), delay_Forward(mid) = (20, 750) 
const int delay_Forward = 750, delay_Back = 1000;    //vel(max), delay_Forward(min)= (256, 50)
                                                     //wonderful ---> (10, 700) (50, 500) (100, 100) (100, 300) (100, 500)
const int array_cal[4] = {0,0,0,0}; 
int RU_Degree = 0, LU_Degree = array_cal[2] + 5;

const int num1 = 6;
const int array_forward[num1][4] =  
{
    {0,-40,0,-20},        
    {30,-40,30,-20},
    {30,0,30,0},
    {0,20,0,40},
    {-30,20,-30,40},
    {-30,0,-30,0},
};

const int num2 = 5; 
const int array_turn[num2][4] =  
{     
    {-40,0,-20,0},
    {-40,30,-20,30},      
    {0,30,0,30},
    {30,0,30,0},
    {0,0,0,0},
};

//#define INSTALL
#define CALIBRATION
//#define RUN

void Servo_Init()
{
    RU.attach(9);   // Connect the signal wire of the upper-right servo to pin 9 
    RL.attach(10);   // Connect the signal wire of the lower-right servo to pin 10 
    LU.attach(11);   // Connect the signal wire of the upper-left  servo to pin 11 
    LL.attach(12);   // Connect the signal wire of the lower-left  servo to pin 12 
}

void Adjust()                            // Avoid the servo's fast spinning in initialization 
{                                        // RU,LU goes from array_cal[0] - 5 ,array_cal[2] + 5 degrees to array_cal[0],array_cal[2] degrees
    for(RU_Degree = array_cal[0] - 5; RU_Degree <= array_cal[0]; RU_Degree += 1) {
        RU.write(RU_Degree);             // in steps of 1 degree
        LU.write(LU_Degree--);           // tell servo to go to RU_Degreeition, LU_Degreeition in variable 'RU_Degree', 'LU_Degree'         
        delay(15);                       // waits 15ms for the servo to reach the RU_Degreeition
    }
}

bool TooClose()
{
    int tooclose = 0;
    for(int a=0; a<5; a++) {  
        delay(50);
        int din = sonar.ping_in();
        if (din < 7 && din > 0) tooclose++;
    }
    if (tooclose < 5) return 1;   
    return 0;
}

void Forward()
{
    for(int x=0; x<num1; x++) {                    
        RU.slowmove (array_cal[0] + array_forward[x][0] , vel);    
        RL.slowmove (array_cal[1] + array_forward[x][1] , vel);
        LU.slowmove (array_cal[2] + array_forward[x][2] , vel);
        LL.slowmove (array_cal[3] + array_forward[x][3] , vel);
        delay(delay_Forward);
    }
}

void Backward()
{
    for(int z=0; z<4; z++) {    
        for(int y=0; y<num2; y++) {                  
            RU.slowmove (array_cal[0] + array_turn[y][0] , vel_Back);   
            RL.slowmove (array_cal[1] + array_turn[y][1] , vel_Back);
            LU.slowmove (array_cal[2] + array_turn[y][2] , vel_Back);
            LL.slowmove (array_cal[3] + array_turn[y][3] , vel_Back);
            delay(delay_Back); 
        }
    }
}

void setup()  
{
#ifdef INSTALL
    Servo_Init();
  
    RU.slowmove (90 , vel);
    RL.slowmove (90 , vel);
    LU.slowmove (90 , vel);
    LL.slowmove (90 , vel);
    while(1);
#endif

#ifdef CALIBRATION 
    Servo_Init();  
    Adjust();
    
    RL.slowmove (array_cal[1] , vel);
    LL.slowmove (array_cal[3] , vel);
    delay(2000);
    while(1);
#endif

#ifdef RUN 
    Servo_Init();
    Adjust(); 
       
    RL.slowmove (array_cal[1] , vel);
    LL.slowmove (array_cal[3] , vel);
    delay(2000);
#endif
}

void loop() 
{ 
    while(TooClose()) Forward();    
    Backward();    
}

Would you hold the robot (suspended), "facing" you, and turn it on (multiple times, if needed). Describe what does each of the eight (?) servos do? For example...

Front, left, top servo (FLTS) rotates left
FLBS rotates clockwise
FRTS rotates right
FRBS rotates anticlockwise

RLTS ...
RLBS ...
RRTS ...
RRBS ...

There are only 4 servos. Top left does nothing, top right does nothing.

bottom left and right rotate completely sideways clockwise.

Edit: Also, the ultrasonic sensor no longer works, so I suspect something's gone wrong with the Arduino. I've got a replacement on the way.

Good data!
Do you have VarSpeedServo.h or a link to it?

  • Verify servo pins are RU 9, RL 10, LU 11 and LL 12.

Would you add:

  • inside setup() this line:
Serial.begin(115200);
  • inside TooClose() these lines:
        Serial.print(" TOOCLOSE ");
        Serial.println(din);
  • Verify trigger is on 4 and echo is on 3. (I know you said you looked, but I need "trig is 4, echo is 3" verifying). I see only one call to ping()... I don't know if that is good or bad, yet.

Yes, that's available.

/*
  VarSpeedServo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
  Copyright (c) 2009 Michael Margolis.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


/*
  Function slowmove and supporting code added 2010 by Korman. Above limitations apply
  to all added code, except for the official maintainer of the Servo library. If he,
  and only he deems the enhancment a good idea to add to the official Servo library,
  he may add it without the requirement to name the author of the parts original to
  this version of the library.
*/

/*
  Updated 2013 by Philip van Allen (pva), 
  -- updated for Arduino 1.0 +
  -- consolidated slowmove into the write command (while keeping slowmove() for compatibility
     with Korman's version)
  -- added wait parameter to allow write command to block until move is complete
  -- added sequence playing ability to asynchronously move the servo through a series of positions, must be called in a loop

  
  A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
  The servos are pulsed in the background using the value most recently written using the write() method

  Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
  Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
  The sequence used to sieze timers is defined in timers.h

  The methods are:

   VarSpeedServo - Class for manipulating servo motors connected to Arduino pins.

   attach(pin )  - Attaches a servo motor to an i/o pin.
   attach(pin, min, max  ) - Attaches to a pin setting min and max values in microseconds
   default min is 544, max is 2400  
 
   write(value)     - Sets the servo angle in degrees.  (invalid angle that is valid as pulse in microseconds is treated as microseconds)
   write(value, speed) - speed varies the speed of the move to new position 0=full speed, 1-255 slower to faster
   write(value, speed, wait) - wait is a boolean that, if true, causes the function call to block until move is complete

   writeMicroseconds() - Sets the servo pulse width in microseconds 
   read()      - Gets the last written servo pulse width as an angle between 0 and 180. 
   readMicroseconds()  - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
   attached()  - Returns true if there is a servo attached. 
   detach()    - Stops an attached servos from pulsing its i/o pin. 

   slowmove(value, speed) - The same as write(value, speed), retained for compatibility with Korman's version

   stop() - stops the servo at the current position

   sequencePlay(sequence, sequencePositions); // play a looping sequence starting at position 0
   sequencePlay(sequence, sequencePositions, loop, startPosition); // play sequence with number of positions, loop if true, start at position
   sequenceStop(); // stop sequence at current position

 */

#ifndef VarSpeedServo_h
#define VarSpeedServo_h

#include <inttypes.h>

/* 
 * Defines for 16 bit timers used with  Servo library 
 *
 * If _useTimerX is defined then TimerX is a 16 bit timer on the curent board
 * timer16_Sequence_t enumerates the sequence that the timers should be allocated
 * _Nbr_16timers indicates how many 16 bit timers are available.
 *
 */

// Say which 16 bit timers can be used and in what order
#if defined(__AVR_ATmega1280__)  || defined(__AVR_ATmega2560__)
#define _useTimer5
#define _useTimer1 
#define _useTimer3
#define _useTimer4 
typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega32U4__)  
#define _useTimer3
#define _useTimer1 
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#else  // everything else
#define _useTimer1
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ;                  
#endif

#define VarSpeedServo_VERSION           2      // software version of this library

#define MIN_PULSE_WIDTH       544     // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH      2400     // the longest pulse sent to a servo 
#define DEFAULT_PULSE_WIDTH  1500     // default pulse width when servo is attached
#define REFRESH_INTERVAL    20000     // minumim time to refresh servos in microseconds 

#define SERVOS_PER_TIMER       12     // the maximum number of servos controlled by one timer 
#define MAX_SERVOS   (_Nbr_16timers  * SERVOS_PER_TIMER)

#define INVALID_SERVO         255     // flag indicating an invalid servo index

#define CURRENT_SEQUENCE_STOP   255    // used to indicate the current sequence is not used and sequence should stop


typedef struct  {
  uint8_t nbr        :6 ;             // a pin number from 0 to 63
  uint8_t isActive   :1 ;             // true if this channel is enabled, pin not pulsed if false 
} ServoPin_t   ;  

typedef struct {
  ServoPin_t Pin;
  unsigned int ticks;
	unsigned int target;			// Extension for slowmove
	uint8_t speed;					// Extension for slowmove
} servo_t;

typedef struct {
  uint8_t position;
  uint8_t speed;
} servoSequencePoint;

class VarSpeedServo
{
public:
  VarSpeedServo();
  uint8_t attach(int pin);           // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
  uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. 
  void detach();
  void write(int value);             // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
  void write(int value, uint8_t speed); // Move to given position at reduced speed.
          // speed=0 is identical to write, speed=1 slowest and speed=255 fastest.
          // On the RC-Servos tested, speeds differences above 127 can't be noticed,
          // because of the mechanical limits of the servo.
  void write(int value, uint8_t speed, bool wait); // wait parameter causes call to block until move completes
  void writeMicroseconds(int value); // Write pulse width in microseconds 
  void slowmove(int value, uint8_t speed);
  void stop(); // stop the servo where it is
  
  int read();                        // returns current pulse width as an angle between 0 and 180 degrees
  int readMicroseconds();            // returns current pulse width in microseconds for this servo (was read_us() in first release)
  bool attached();                   // return true if this servo is attached, otherwise false 

  uint8_t sequencePlay(servoSequencePoint sequenceIn[], uint8_t numPositions, bool loop, uint8_t startPos);
  uint8_t sequencePlay(servoSequencePoint sequenceIn[], uint8_t numPositions); // play a looping sequence starting at position 0
  void sequenceStop(); // stop movement
private:
   uint8_t servoIndex;               // index into the channel data for this servo
   int8_t min;                       // minimum is this value times 4 added to MIN_PULSE_WIDTH    
   int8_t max;                       // maximum is this value times 4 added to MAX_PULSE_WIDTH
   servoSequencePoint * curSequence; // for sequences
   uint8_t curSeqPosition; // for sequences

};

#endif

I am unsure of my version of NewPing.h because when I compiled with your VarSpeedServo.h, I received many errors pointing to your VarSpeedServo.h (that should not happen). So, would you post your NewPing.h file so I can use as much of your setup as possible?

That's a standard library. It's already installed.

Edit: hop into the library manager and search it. It should show up. It's for the ultrasonic sensor.

I think I got the right one. I checked here...

And redirected to here...
https://bitbucket.org/teckel12/arduino-new-ping/wiki/Home

Yep. That's the right one.

It looks like this simulator is not liking things... (my sketch is robot.ino)...

/tmp/ccgHnDYK.ltrans0.ltrans.o: In function `global constructors keyed to 65535_0_sketch.ino.cpp.o.1752':
<artificial>:(.text.startup+0x5c): undefined reference to `VarSpeedServo::VarSpeedServo()'
<artificial>:(.text.startup+0x64): undefined reference to `VarSpeedServo::VarSpeedServo()'
<artificial>:(.text.startup+0x6c): undefined reference to `VarSpeedServo::VarSpeedServo()'
<artificial>:(.text.startup+0x74): undefined reference to `VarSpeedServo::VarSpeedServo()'
<artificial>:(.text.startup+0x84): undefined reference to `NewPing::NewPing(unsigned char, unsigned char, unsigned int)'
/tmp/ccgHnDYK.ltrans0.ltrans.o: In function `Servo_Init':
robot.ino:65: undefined reference to `VarSpeedServo::attach(int)'
robot.ino:66: undefined reference to `VarSpeedServo::attach(int)'
robot.ino:67: undefined reference to `VarSpeedServo::attach(int)'
robot.ino:68: undefined reference to `VarSpeedServo::attach(int)'
/tmp/ccgHnDYK.ltrans0.ltrans.o: In function `Adjust':
robot.ino:74: undefined reference to `VarSpeedServo::write(int)'
robot.ino:75: undefined reference to `VarSpeedServo::write(int)'
/tmp/ccgHnDYK.ltrans0.ltrans.o: In function `setup':
robot.ino:134: undefined reference to `VarSpeedServo::slowmove(int, unsigned char)'
robot.ino:135: undefined reference to `VarSpeedServo::slowmove(int, unsigned char)'
collect2: error: ld returned 1 exit status

Error during build: exit status 1

Can you edit the sketch pins to check that all servos move?

They do. The initial calibration program simply isn't working.

Is the sketch you posted in #7 the calibration sketch? see below...

If so, and given:

  1. UL - no movement
  2. UR - no movement
  3. LL - rotation
  4. LR - rotation

I would edit the sketch to "swap" UL/UR pin assignments with LL/LR pin assignments, and if the problem does not follow the change, the servos are bad. If the problem follows, I would look for available PWM pins on the Arduino and edit the sketch, then test the good servos with those new pins.

For the Sonar, I would look for a different trigger and echo pin.

[late edit]
I just stumbled upon the if-defined lines, one is #ifdef INSTALL and #ifdef CALIBRATION and #ifdef RUN... but nowhere in the sketch is a #define of any. (found them). You will need to insure "define" INSTALL or CALIBRATION or RUN for anything to be called. The following video at 17:52.

Your sketch and libraries are certainly good, and the issue is physical.

I removed VarSpeedServo.h from this sim and used Servo.h and with a little editing (VarSpeedServo indicated that the abstractions offending the sim were just re-writes of servo.write(pin)). I now see my simulation working and walking and moving when I simulate an obstacle, with the sonar on its original pin.

I would find new PWM pins on your Arduino for all the servos and two normal, digital pins for the sonar.

Here's the sim link if you want to see it "walk" forward until it detects an object. Adjust the sonar to less than 7 (but not zero) and it will walk backward for a few, "turn" five degrees and walk forward again.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.