Image Processing and Object tracking Robot


#61

Hey Guys,

Hope you are well.

Today we tested the code we have been working on with a green cup, :grin: the results were pretty intresting, find the demo below;

Demo

The demo was able to colors and output it to the various leds i.e Red, Green and Blue.

The Code is available Here.


#62

The workout is pretty straight forward, the program is started from the shell file, start.sh ,which prompts the raspberry pi camera to start up for 5 seconds, capture and save the output image to /home/pi/camera/output.jpg

raspistill -vf -hf -o /home/pi/camera/output.jpg sudo node index.js


#63
       'use strict'
        const vision = require('node-cloud-vision-api'),
            // five = require('johnny-five'),
            // Oled = require('oled-js');
            ColorThief = require('color-thief'),
        colorThief = new ColorThief(),
        fs = require('fs'),
        image = fs.readFileSync('/home/pi/camera/output.jpg'),
        rgb = colorThief.getColor(image),
        onecolor = require('onecolor'),
        rgbCode = 'rgb( ' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')', // 'rgb(r, g, b)'
        colored = onecolor(rgbCode).rgb(),
        hex = onecolor(rgbCode).hex(),
        namer = require('color-namer'),
        names = namer("#FF0000"),
        five = require("johnny-five"),
        board = new five.Board();
    require('dotenv').config();
    // init with auth
    vision.init({
        auth: process.env.KEY
    })
    // construct parameters
    const req = new vision.Request({
        image: new vision.Image('/home/pi/camera/output.jpg'),
        features: [
            new vision.Feature('FACE_DETECTION', 4),
            new vision.Feature('LABEL_DETECTION', 10),
        ]
    })

    // send single request
    vision.annotate(req).then((res) => {
            // handling response

            // console.log(JSON.stringify(res.responses));
            // SHow latest results
            // console.log(JSON.stringify(res.responses[0].labelAnnotations[0].description));
            var processed = JSON.stringify(res.responses[0].labelAnnotations[0].description);
            console.log('Identified:', JSON.parse(processed));
        }, (e) => {
            console.log('Error: ', e)
        })
        //call board
        // board.on('ready', function() {
        //     console.log('Connected to arduino');
        //     var led = new five.Led(13);
        //     led.blink();
        // });
    var mainPalette = colorThief.getPalette('/home/pi/camera/output.jpg', 8);
    console.log(mainPalette);
    console.log('Color RGB Ratio : ');
    console.log(colored);
    console.log('Color HEX Ratio : ');
    console.log(hex);
    var identifiedcolor = namer(hex);
    console.log('Color Name is:', identifiedcolor.basic[0].name);
    var maincolor = identifiedcolor.basic[0].name;
    board.on("ready", function() {
        // let 3 be  green
        // let 5 be blue
        // let 6 be red
        console.log(maincolor);
        var array = new five.Leds([3, 5, 6]);
    if (maincolor == 'red') {
      array[2].blink();
    } else if (maincolor == 'blue') {
      array[1].blink();
    } else if (maincolor == 'green') {
      array[0].blink();
    }
     else {
       console.log('Color Identified is not Basic');
     }
    });

#64

Hello Guys,

We have updated the code on AGV navigation to include MPU6050 Gyroscope. For the code to compile you need this library by Jeff Rowberg. I will review three fuunctions we have added to support MPU6050, the first function is for retrieving data from the gyro buffer.

/* This function returns the values of yaw, pitch and roll in degrees
 *  for our case we will just be using the values of Yaw
 *  */
void get_yaw()
{

  // Get INT_STATUS byte
  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024)
  {
    // reset so we can continue cleanly
    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));
    return;
  }

  if (mpuIntStatus & 0x02)  // otherwise continue processing
  {
    // check for correct available data length
    if (fifoCount < packetSize)
      return; //  fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);

    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    // flush buffer to prevent overflow
    mpu.resetFIFO();

    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    mpuPitch = ypr[PITCH] * 180 / M_PI;
    mpuRoll = ypr[ROLL] * 180 / M_PI;
    mpuYaw  = ypr[YAW] * 180 / M_PI;

    // flush buffer to prevent overflow
    mpu.resetFIFO();

    // flush buffer to prevent overflow
    mpu.resetFIFO();
    Serial.println(mpuYaw);
   
    // flush buffer to prevent overflow
    mpu.resetFIFO();

  }
} 

Whenever the function above is called, it returns the pitch, yaw and roll, however for our application we just need the yaw value. After getting this values the code flushes the buffer to prevent overflow. The code is adequately commented so it will be easy to follow.

The other function we added is a function to turn the robot 10 degrees right. Here is the function

// Turn the robot 10 degrees right
void turn10_right()
{
  get_yaw();
  float angleZ = mpuYaw;// The poition of the robot right now
  float NangleZ = mpuYaw + 10;//New position of the robot after turning 10 degrees
  // Do this once
  while(1){
    while(angleZ < NangleZ)
    {
      get_yaw();
      turn_right();
    }
    brake();// Stop

    break;// break from this llop once the condition is satisfied
  }
}

The function gets the Yaw value of the robot at that instance, increments it by 10 degrees, then turns the robot to the right until the condition is satisfied i.e the robot has turned 10 degrees from the previous position. A function for turning the robot 10 degrees to the left is shown below (it’s just an alternate of the one shown above)

// turn the robot 10 degrees to the left 
void turn10_left()
{
  get_yaw();
  float angleZ = mpuYaw; //The poition of the robot right now
  float NangleZ = mpuYaw - 10;//New position of the robot after turning 10 degrees
  // Do this once
  while(1){
    while(angleZ > NangleZ)
    {
      get_yaw();
      turn_left();
    }
    brake();// Stop

    break;// break from this llop once the condition is satisfied
  }
}

The full code containing the initialization and how to connect the mpu6050 to the board is shown below

// MPU Connection
//
// VCC - 5v
// GND - GND
// SCL - A5 (w/ 10k PuR)
// SDA - A4 (w/ 10k PuR)
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
float mpuPitch = 0;
float mpuRoll = 0;
float mpuYaw = 0;

// define MPU instance
MPU6050 mpu;                    // class default I2C address is 0x68; specific I2C addresses may be passed as a parameter here

// MPU control/status vars
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

// relative ypr[x] usage based on sensor orientation when mounted, e.g. ypr[PITCH]
#define PITCH   1     // defines the position within ypr[x] variable for PITCH; may vary due to sensor orientation when mounted
#define ROLL  2     // defines the position within ypr[x] variable for ROLL; may vary due to sensor orientation when mounted
#define YAW   0     // defines the position within ypr[x] variable for YAW; may vary due to sensor orientation when mounted

const int InA1 = 9; //Counter_ClockWise Input For the first Set of wheels(Right Side)
const int InA2 = 4; //Counter-Clockwise input for the second set of wheels(Left Side)  
const int InB1 = 3; //Clockwise input for the first set of wheels(Right Side)RED
const int InB2 = 8; //Clockwise input for the Second Set of Wheels(Left Side)WHITE
const int PWM1 = 5; // Speed Input for the first set of wheels(Right Side)
const int PWM2 = 6; // Speed Input for the Second Set Of Wheels(Left Side)

int Trigpin =11 ;
int Echopin = 10;

const int Trigpin1 = 2;
const int Echopin1 = 12;
// Move Forward
int move_forward()// Funtion to drive the Rover Forward
{
  
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, HIGH);
  digitalWrite(InB1, LOW);
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 100);//EFT SIDE//100
  analogWrite(PWM2, 100); //RIGHT SIDE
}
//Move forward slower than <Move Forwar>
void approach_forward()
{
 digitalWrite(InA1, HIGH);
  digitalWrite(InA2, HIGH);
  digitalWrite(InB1, LOW);
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 60);//EFT SIDE//100
  analogWrite(PWM2, 60); //RIGHT SIDE
}
//Move Forward Faster than <Move Forward>
int gravity_forward()// Funtion to drive the Rover Forward
{
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, HIGH);
  digitalWrite(InB1, LOW);
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 180); //LEFT SIDE//120
  analogWrite(PWM2, 180); //RIGHT SIDE
}


// Move in Reverse
int reverse()//Reverse at normal speed
{
  digitalWrite(InA1, LOW);
  digitalWrite(InA2, LOW );
  digitalWrite(InB1, HIGH);
  digitalWrite(InB2, HIGH);
  analogWrite(PWM1, 100); //Set Speed for the RIGHT Side
  analogWrite(PWM2, 100); //Set Speed for the LEFT Side
}
//slow Reverse
int slow_reverse()
{
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, HIGH);
  digitalWrite(InB1, LOW);
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 500);//Set Speed for the RIGHT Side
  analogWrite(PWM2, 500);//Set Speed for the LEFT Side
}

//Brake
int brake()
{
  digitalWrite(InA1, LOW);
  digitalWrite(InA2, LOW);
  digitalWrite(InB1, LOW);
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 0);//Set Speed for the RIGHT Side
  analogWrite(PWM2, 0);//Set Speed for the LEFT Side
}


//Turn LEFT
void turn_left()
{
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, LOW); //Move the Left side Forward
  digitalWrite(InB1, LOW); //MOve the RIGHT Side in Reverse
  digitalWrite(InB2, HIGH);
  analogWrite(PWM1, 185);//Set Speed for the RIGHT Side
  analogWrite(PWM2, 185);//Set Speed for the LEFT Side
}

void slowturn_left()
{
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, LOW); //Move the Left side Forward
  digitalWrite(InB1, LOW); //MOve the RIGHT Side in Reverse
  digitalWrite(InB2, HIGH);
  analogWrite(PWM1, 120);//Set Speed for the RIGHT Side
  analogWrite(PWM2, 120);//Set Speed for the LEFT Side
}



void reverse_slowleft()
{
  digitalWrite(InA1, HIGH);
  digitalWrite(InA2, LOW); //Move the Left side Forward
  ///digitalWrite(InB1, LOW); //MOve the RIGHT Side in Reverse
  digitalWrite(InB2, HIGH);
  //analogWrite(PWM1, 180);//Set Speed for the RIGHT Side 180
   analogWrite(PWM2, 180); //Set Speed for the LEFT Side
}


//Turn RIGHT
void turn_right()
{
  digitalWrite(InA1, LOW);
  digitalWrite(InA2, HIGH); //Move the Right side Forward
  digitalWrite(InB1, HIGH); //Move thee Left Side In Reverse
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 225); //Set Speed for the RIGHT Side
  analogWrite(PWM2, 225);//Set Speed for the LEFT Side
}
void slowturn_right()
{
  digitalWrite(InA1, LOW);
  digitalWrite(InA2, HIGH); //Move the Right side Forward
  digitalWrite(InB1, HIGH); //Move thee Left Side In Reverse
  digitalWrite(InB2, LOW);
  analogWrite(PWM1, 225); //Set Speed for the RIGHT Side
  analogWrite(PWM2, 150);//Set Speed for the LEFT Side
}

float get_range() // get values from the front sonar
{
  digitalWrite(Trigpin, LOW);
  delayMicroseconds(2);
  digitalWrite(Trigpin, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trigpin, LOW);
 int cm = pulseIn(Echopin, HIGH)/58;
float  dist = cm;
  Serial.println(cm);  
  return (dist);
}

float stop_dist()// get distance from the side sonar
{
  digitalWrite(Trigpin1, LOW);
  delayMicroseconds(2);
  digitalWrite(Trigpin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trigpin1, LOW);
  int cm = pulseIn(Echopin1, HIGH) / 58;
 float sdist = cm;
  Serial.println(cm);
  return (sdist);
}
/* This function returns the values of yaw, pitch and roll in degrees
 *  for our case we will just be using the values of Yaw
 *  */
void get_yaw()
{

  // Get INT_STATUS byte
  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024)
  {
    // reset so we can continue cleanly
    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));
    return;
  }

  if (mpuIntStatus & 0x02)  // otherwise continue processing
  {
    // check for correct available data length
    if (fifoCount < packetSize)
      return; //  fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);

    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;

    // flush buffer to prevent overflow
    mpu.resetFIFO();

    // display Euler angles in degrees
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    mpuPitch = ypr[PITCH] * 180 / M_PI;
    mpuRoll = ypr[ROLL] * 180 / M_PI;
    mpuYaw  = ypr[YAW] * 180 / M_PI;

    // flush buffer to prevent overflow
    mpu.resetFIFO();

    // flush buffer to prevent overflow
    mpu.resetFIFO();
    Serial.println(mpuYaw);
   
    // flush buffer to prevent overflow
    mpu.resetFIFO();

  }
} 

/// Turn the robot 10 degrees right
void turn10_right()
{
  get_yaw();
  float angleZ = mpuYaw;// The poition of the robot right now
  float NangleZ = mpuYaw + 10;//New position of the robot after turning 10 degrees
  // Do this once
  while(1){
    while(angleZ < NangleZ)
    {
      get_yaw();
      turn_right();
    }
    brake();// Stop

    break;// break from this llop once the condition is satisfied
  }
}
 // turn the robot 10 degrees to the left 
void turn10_left()
{
  get_yaw();
  float angleZ = mpuYaw; //The poition of the robot right now
  float NangleZ = mpuYaw - 10;//New position of the robot after turning 10 degrees
  // Do this once
  while(1){
    while(angleZ > NangleZ)
    {
      get_yaw();
      turn_left();
    }
    brake();// Stop

    break;// break from this llop once the condition is satisfied
  }
}

void setup()
{
    // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif

  Serial.begin(115200);
  while (!Serial);      // wait for Leonardo enumeration, others continue immediately

  // initialize device
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();

  // verify connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP"));
  devStatus = mpu.dmpInitialize();


  // INPUT CALIBRATED OFFSETS HERE; SPECIFIC FOR EACH UNIT AND EACH MOUNTING CONFIGURATION!!!!

  mpu.setXGyroOffset(118);
  mpu.setYGyroOffset(-44);
  mpu.setZGyroOffset(337);
  mpu.setXAccelOffset(-651);
  mpu.setYAccelOffset(670);
  mpu.setZAccelOffset(1895);

  // make sure it worked (returns 0 if so)
  if (devStatus == 0)
  {
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP"));
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)"));
    mpuIntStatus = mpu.getIntStatus();

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  }
  else
  {
    // ERROR!
    // 1 = initial memory load failed, 2 = DMP configuration updates failed (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed code = "));
    Serial.println(devStatus);
  }
  //Define motor and distance sensor pins
  pinMode(InA1, OUTPUT);
  pinMode(InA2, OUTPUT);
  pinMode(InB1, OUTPUT);
  pinMode(InB2, OUTPUT);
  pinMode(PWM1, OUTPUT);
  pinMode(PWM2, OUTPUT);
  pinMode(Echopin, INPUT);
  pinMode(Trigpin, OUTPUT);
}

void loop()
{
//turn10_left();
turn10_right();
  
}

You can read more about connecting MPU6050 to Arduino here.


#65

The progress is really good. You fabricated the chassis yourself ?


#66

Thanks @Karanja, yes we did fabricate the chassis ourselves.


#67

Hi guys, today we mounted an arm to the robot, the arm has six servos but two are for the gripper. The gripper servos have been reversed and share a signal pin. Don’t mind the wiring it’s quite clumsy for now, will work on that.:relaxed:
The image below shows how the servos are mounted and to which digital pins they are connected to the board.


#68

Below is the controller board for the arm, it’s also based on the Atmega328 as is the controller to the AGV, the only difference been this particular board is mostly dedicated for servo contol


#69

Hello guys, after mounting the arm we had to write a short code to test whether all the servos were working and working the right way. The ARM controller board is a slave to the AGV controller board and they communicate via I2C. Thr routine below is used to receive an event from the master.

//Wait for an event on the I2C bus
void receiveEvent(int howMany)
{
  while (1 < Wire.available() )
  {
    char c = Wire.read();// When an event is available read the content
    Serial.print(c);// Print out the content
  }
  x = Wire.read();// Equate the content to x; X is passed to the main loop to determine the action to be taken


}

The value of x from the above function is passed to the main loop to determine what action is to be taken as shown below

void loop()
{
  Serial.println(x);
  if ( x == 1)// if x is equal to one pick item
  {
   pick_item();// Pick item
    x = 0;
    delay(500);
    Exit(); // let the master know you are done
  }

  else {
    Exit();
  }
}

One more think, instead of using the normal I2C protocol to indicate the end of an event, we created a function to write a digital pin high momentarily to indicate to the master that the slave is done with what it was executing.

 // indicates  end of an event
  void Exit()
  {
    digitalWrite(Done, HIGH);
    delay(500);
    digitalWrite(Done, LOW);
  }

This way, the I2C communication time is reduced.
See the whole test code below.

  #include <Servo.h>
  #include <Wire.h>
  
  Servo joint3;
  Servo joint2;
  Servo joint1;
  Servo base;
  Servo gripper;

  
  const int Done = 13;
  int pos = 0;
  int x = 0;
  
  void set_base()
  {
    base.write(170);
    for (pos = 170; pos >= 134 ; pos --)
    {
      base.write(pos);
      delay(20);
    }
  }
  
  void receiveEvent(int howMany)
  {
    while (1 < Wire.available() )
    {
      char c = Wire.read();
      Serial.print(c);
    }
    x = Wire.read();
  
  
  }
  
 // indicates  end of an event
  void Exit()
  {
    digitalWrite(Done, HIGH);
    delay(500);
    digitalWrite(Done, LOW);
  }
  
  void home_position()
  {
    for (pos = 170; pos <= 180; pos += 1)
    {
      int p = map(pos, 170, 180, 10, 0);
      int s = map(pos, 170, 180, 150, 160);
      int g = map(pos, 170, 180, 50, 40);
      int b = map(pos, 170, 180, 90, 106);
      base.write(b);
      joint1.write(pos);
      joint2.write(p);
      joint3.write(p);
      gripper.write(g); //open grip 60-110
      delay(50);
    }
  }
  
  void pick_item1()
  {
    for (pos = 180; pos >= 30; pos -= 1)
    {
      int v = map(pos, 180, 30, 0, 80);
      joint1.write(pos);
      joint2.write(v);
      delay(20);
    }
  }

  
  void grab_one()
  {
    for (pos = 60; pos <= 130; pos += 1)
    {
      gripper.write(pos);
    }
  }
  
  void j3return_item()
  {
  for (pos = 0; pos<=150; pos +=1)
  {
  joint3.write(pos);
  delay(20);
  }
  }
 
  
  void j2return_item()
  {
  for (pos = 80; pos <= 150; pos += 1)
  {
  joint2.write(pos);
  delay(20);
  }
  }
  
  void return_item()
  {
    for (pos = 30; pos <= 110; pos += 1)
    {
      int v = map(pos, 30, 180, 150, 70);
      joint1.write(pos);
      joint2.write(v);
      //
      //joint2.write(v);
      delay(20);
    }
  }
  
  
  void return_j2j3item()
  {
  for (pos = 150; pos >= 0; pos -= 1)
  {
   int p = map(pos,150, 0, 70, 0);
   int s = map(pos, 150,0,110,178);
  joint2.write(p);
  joint3.write(pos);
  joint1.write(s);
  delay(20);
  }
  }
 
  
  void pick_item()
  {
    pick_item1();
    delay(2000);
    grab_one();
    delay(500);
   j3return_item();
    delay(500);
    j2return_item();
    delay(500);
    return_item();
    delay(500);
   return_j2j3item();
    delay(500);
   //return_ball();
  }


    void setup()
{
  Serial.begin(9600);
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  pinMode(Done, OUTPUT);

  joint1.attach(9);
  joint2.attach(3);
  joint3.attach(6);
  gripper.attach(5);
 
base.attach(10);
  home_position();
  delay(500);
    
 Exit();
}

void loop()
{
  Serial.println(x);
  if ( x == 1)// if x is equal to one pick item
  {
   pick_item();// Pick item
    x = 0;
    delay(500);
    Exit(); // let the master know you are done
  }

  else {
    Exit();
  }
}

#70

Hello Guys,

The arm is working though currently set back by a few bugs, which we are working to on.
Will post a video soon though :relaxed:


#71

Hi Guys,

On the last post we had the code on the arm, but as I had indicate the arm controller board is a slave to the AGV controller board communicating via I2C communication protocol, therefore we need to update the AGV code to enable it communicate with the ARM. Below are the changes.

First, the AGV code has to initiate a communication with the arm as shown below.

// Pick item one
void pick_item()
{
  int j = 0;// declare an interger j and equate it to zero
  while (j == 0)// While j is 0 carry out the instructions below 
  {
    int b = 1;// I2C passes values as 'char' 
    Wire.beginTransmission(4);// Begin communication with slave 4 (arm controller)
    Wire.write(b);// pass char be to b
    Wire.endTransmission();// End transmission
    Serial.println(b);
    j++;// increment j to disatisfy condition on the while loop
  }
}

The snippet above tells the arm controller is supposed to do.
The AGV board has to know when the arm is done with the action above, the code below does just that.

// Wait for the arm to indicate its done with action
void wait_arm()
{
  int w = 0;// declare an integer w and equate it to zero
  while (w == 0)// while w is equal to zero carry out the foolowing actions
  {
    int Exit = digitalRead(Done);// Read the value of pin declared as Done
    Serial.println(Exit, DEC);
    if (Exit == HIGH)// If the value returned is HIGH equate w to 1 and exit the loop
    {
      w = 1;
    }
    else// else if the value is LOW continue looping
    {
      brake();
      w = 0;
    }
  }

#72

Hey guys,

Hope you are well.

Earlier in the thread there was a concern how the raspberry pi and the ATMEGA micro-controller. There were two suggestions;

  • Over USB
  • Using pinouts( Hardware Serial UART )

Having tried out, it has presented several challenges including;

  • The USB subsystem on the RPi is not excellent and is prone to power issues with a lot of comms over it.
  • If you’re using a USB port for WiFi networking, putting other devices on there (especially ones doing a lot of data transfer) can cause contention problems. - (this is not the case with pi 3 though)
  • The arduino (microcontoller) will attempt to draw power through the RPi, again leading to more power stability woes - and this is exacerbated when using a battery.
  • You take up a USB port.

UART to the rescue (maybe?) :relieved:

On the RPi, we have the ability to use a dedicated hardware serial port that is available via the GPIO. This means we can skip USB and run our comms directly to the UART on broadcomm chip which is also exposed to the linux kernel. This means it’s faster, more stable, doesn’t block anything else running over USB and keeps a second USB port available. And is not permission sensitive.

Am currently this out and will post the full process.

Cheers.


#73

Thanks @denzel for that, in the image processing part of the code there is kinda of a lag, takes about 90sec for the pi to respond, Could that be associated with the us using the USB port.

Also, after taking a couple of images and processing them, the PI just freezes and returns nothing, still working on that. Haven’t figured out if we are overloading the processor or the code is redundant. Will try to solve the problem, but if anyone has a solution to this I would really appreciate,( will save me a lot of time doing trial and error :wink:)

Thanks.


#74

Why cant we use the pi board alone? I think it kind of saves time in terms of communication with the Atmel chip. All that is to be done is build a seperate pcb for the sensors and motors, but all are to be controlled from the pi’s GPIO. Incase the pins are not enough, we could use pin extension chips like the 74HC595 8 bit shift register. Converts 3 pins into 8.


#75

Welcome aboard @Kithinji ,

We had initially thought of that but settled on having different controller boards in order to distribute the processing and leave the pi with the task of image processing and as it is seems quite a big task too for the pi.


#76

Let me take a look at this over the weekend


#77

Would be interesting to see a flow chart of state transition diagram to help understand the flow logic.


#80

Hi. I’ve been researching a similar problem to yours and found that having a streaming server on my board allows good live video. A reference you could use is: http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets . The author claims 320x240 @ 30fps. Hope this helps. O


#81

Thanks @Oscar, will try this out, hopefully i w.ill get better results


#82

A useful product for such a project could be http://www.robotshop.com/en/pixy-cmucam5-image-sensor.html