Deep Think Project - Artificial Intelligence Research Project

goodinventor:
I am not sure why you mentioned learning parallel processing

Because at some point, you'll want to spread the control across several processors?

Note: parallel processing is not necessarily multiprocessing.

Yes, I know. It can be used to describe dividing the load across multiple cores or multiple processors.

For parallel processing I think it is more efficient to just get a multi-core single board computer rather than multiple Arduinos. It takes up less space too. Of course, I may want to use two anyways: one for motor control and the other for the sensors and the machine learning. I can use my 86Duino One for driving the servos and D/C motors and some other SBC for the other tasks. If the other SBC has multiple cores I can use the library in C++ to make use of the extra cores. If the project gets this complex I might consider using a small OS to manage all the resources, such as a distribution of embedded Linux.

goodinventor:
I already learned how to blink an LED without using delay(). Once I implement it, I can then work on finding a fast interface for connecting the servos. I think the UARTs will do for this job. I am not sure why you mentioned learning parallel processing, since the Arduino can't multi process anyways. Or maybe you were referring to something else?

Just trying to save you a LOT of time and effort.

Here is the lesson at the address above, with code development and all the whys

This question comes up practically every day on the Arduino forum - "how do I blink two LEDs at different rates?" or "how do I turn on and off two motors at different times?".

One of the problems is that beginners look at the "blink" tutorial program, which is:

void setup()
{
pinMode(13, OUTPUT);
}

void loop()
{
digitalWrite(13, HIGH); // set the LED on
delay(1000); // wait for a second
digitalWrite(13, LOW); // set the LED off
delay(1000); // wait for a second
}

Now this works fine, to blink one LED. But to blink two LEDs you run into problems. It's OK if you want to blink them both at once:

void setup()
{
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
}

void loop()
{
digitalWrite(12, HIGH); // set the first LED on
digitalWrite(13, HIGH); // set the second LED on
delay(1000); // wait for a second

digitalWrite(12, LOW); // set the first LED off
digitalWrite(13, LOW); // set the second LED off
delay(1000); // wait for a second
}

Or, if you want to do one after the other:

void setup()
{
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
}

void loop()
{
digitalWrite(12, HIGH); // set the first LED on
delay(1000); // wait for a second
digitalWrite(12, LOW); // set the first LED off
delay(1000); // wait for a second

digitalWrite(13, HIGH); // set the second LED on
delay(1000); // wait for a second
digitalWrite(13, LOW); // set the second LED off
delay(1000); // wait for a second
}

But what if you want to blink the two LEDs at different rates? Like, once a second for LED 1 and twice a second for LED 2?

This is where the delay function doesn't really help.

Let's look at an analogy. Say you want to cook breakfast. You need to cook:

Coffee - takes 1 minute
Bacon - takes 2 minutes
Eggs - takes 3 minutes

Now a seasoned cook would NOT do this:

Put coffee on. Stare at watch until 1 minute has elapsed. Pour coffee.
Cook bacon. Stare at watch until 2 minutes have elapsed. Serve bacon.
Fry eggs. Stare at watch until 3 minutes have elapsed. Serve eggs.

The flaw in this is that whichever way you do it, something is going to be cooked too early (and get cold).

In computer terminology this is blocking. That is, you don't do anything else until the one task at hand is over.

What you are likely to do is this:

Start frying eggs. Look at watch and note the time.
Glance at watch from time to time. When one minute is up then ...
Start cooking bacon. Look at watch and note the time.
Glance at watch from time to time. When another minute is up then ...
Put coffee on. Look at watch and note the time.
When 3 minutes are up, everything is cooked. Serve it all up.

In computer terminology this is non-blocking. That is, keep doing other things while you wait for time to be up.

goodinventor:
For parallel processing I think it is more efficient to just get a multi-core single board computer rather than multiple Arduinos. It takes up less space too. Of course, I may want to use two anyways: one for motor control and the other for the sensors and the machine learning. I can use my 86Duino One for driving the servos and D/C motors and some other SBC for the other tasks. If the other SBC has multiple cores I can use the library in C++ to make use of the extra cores. If the project gets this complex I might consider using a small OS to manage all the resources, such as a distribution of embedded Linux.

How many MCU's you think are in a Mercedes? By 2000 there were about 500.

Have you seen Arduino Micro or any Teensy board?

One reason to parallel process is to make modular systems that you can plug elements in to. It could be inherently expandable.

Another reason is that you can split event-driven code between processors, adding a cpu while sharing load.

You can run AVR chips stand-alone on breadboard or other like protoboard (solder sockets) as many in a row as you like, connect them all to SPI bus as master + slaves. 328P DIP chip costs me $2.20.

I've seen two people jump to Mega2560 from Uno when UNO + 328P would have done for much less.

This address has everything on making breadboard standalone AVR's featuring the 328P and the 1284P.

The "Mighty" 1284P has 16K RAM, 4K EEPROM, 128K flash, 2 serial, >30 I/O pins.. ports with 8 pins open.

Look up the MIT High/Low Tech site for ATtiny programming. Those have 8 pins, 6 can be I/O and note that while RAM is minimal they still have their own cpu, their own 'attention' to be applied to some task.

Besides that, how do you have a "Deep Think" without parallel processing?

goodinventor:
I already learned how to blink an LED without using delay().

BWoD is like second week Recorder.

Artificial Intelligence is the New York Philharmmonic (or Jimi Hendrix, depending on taste).

...R

Wiring up a whole bunch of AVR MCUs is useful if I want to control a lot of I/O pins very quickly (motor control), but for my deep learning and machine vision it would be cheaper and a more efficient use of space if I just get an SBC or even use a GPU. Also, programming the necessary number of small MCUs to do this is incredibly tedious and difficult to manage. I do think it is a good idea though the next time I need to write to a huge amount of I/O pins. The ATmega328P only has 16 DMIPS of processing power, whereas a Raspberry Pi 2 has nearly 7000 DMIPS! It would take a great many AVR chips to match that kind of processing power, plus it would require much more power due to so many separate chips. An alternative is for me to create a smaller cluster of much more powerful processors, such as a group of low-power ARM Cortex-A Series chips for the machine learning and vision and AVRs or ARM Cortex M Series processors for motor and basic I/O control (including gyroscopes, accelerometers, force sensors, you name it).

They are right for automation but they can also be used to process stream data and serve as neural net nodes.

If I want a lot of pins, I'll use shift registers.

If you code right, 16 MIPS is plenty. Modular design just makes the whole process easier.

Also, programming the necessary number of small MCUs to do this is incredibly tedious and difficult to manage

Only if you use the wrong tools and languages.

The ATmega328P only has 16 DMIPS of processing power

Even fewer available, if you continue to use delay () :smiley:

whereas a Raspberry Pi 2 has nearly 7000 DMIPS!

But very poor I/O drive capabilities, and is a bugger to program for real-time performance

Horses for courses.

I am not saying that I want to use something like a Raspberry Pi for direct motor control. I am saying that I think it would be better to just use my 86Duino One (an Arduino compatible board with a lot of I/O pins) for controlling the motors and the basic sensors and use a more powerful SBC for the deep learning and vision.

And parallel processing techniques make coordinating all the smart parts far easier than having one big program trying to do everything. Do you know how much of your own senses and muscle control is decentralized? Or in vital organs?

Yes, I know. All the parts of the body are made up of tiny units functional units. I have one program now because the code has not gotten big enough to justify multiple programs and controllers. I can implement parallel processing also just by getting a motor controller (or more, depending on how big this gets), and a multi-core controller for other functions (more as the system gets more complex), such as the Parallella-16 Micro-Server. The main problem with just using a bunch of small MCUs is that to match the performance of an SBC they take up a lot more space. Secondly, they consume more power in a large array of chips.

Here is the new code. I got rid of delay() in my main program. In doing so I trimmed 36 bytes out of my program. :slight_smile: However, I feel that using goto is not the most efficient way. The problem is, I can't think of a better way at the moment. Perhaps you can give me further suggestions? In the meantime, I need to also get rid of delay() in the MeccaBrain library and use a faster communication protocol. Then I can think about parallel processing for some of the harder tasks. Thank you all!

//
// Deep Think 0.0.0.2
//
// programmed by George Andrews
//
// NOTE: This program is designed for the Meccanoid G15 (combined
// with the 86Duino One). It is a part of the Deep Think Project
// (first version of the software for the project). The Deep Think
// Project is designed to advance the intelligence (as well as
// motion and structural capabilities) of humanoid robots. This
// version of the program is designed to do the following.
//
// 1. Perform various moves using functions.
// 2. Control the 2 D/C motors with the 86Duino.
//
// Please note that this version is only intended as a demo to
// establish a basic framework for interfacing the Meccanoid
// electronic parts with the 86Duino One for future development.
// This version of the software, however, has the following
// additional capabilities compared to the previous version Deep
// Think 0.0.0.1.
//
// 1. Can interface with the D/C motors as well as the servos and
//    LED eyes.
// 2. Can perform various moves using functions in the program.
//

#include "MeccaBrain.h"

//
// Variables
//

// pin declarations
int LeftArmDaisyChainPin = 21;
int LEDEyePin = 29;
int RightArmDaisyChainPin = 38;
int LeftFootDCMotorPin = 11;
int RightFootDCMotorPin = 13;

// class objects
MeccaBrain LeftArmDaisyChain(LeftArmDaisyChainPin);
MeccaBrain LEDEye(LEDEyePin);
MeccaBrain RightArmDaisyChain(RightArmDaisyChainPin);

// timer variables
unsigned long TimeNeeded;
unsigned long LastTimeUpdate = millis();

//
// Functions
//

void WaveWithLeftArm()
{
  // up
  LeftArmDaisyChain.setServoPosition(0, 200);
  LeftArmDaisyChain.communicate();
  
  // let motors calibrate
  TimeNeeded = 500;
  
  WLAT1:
  if (Time() == 1)
  {
    goto WLAT2;
  }
  else
  {
    goto WLAT1;
  }
  
  // down
  WLAT2:
  LeftArmDaisyChain.setServoPosition(0, 127);
  LeftArmDaisyChain.communicate();
  
  // let motors calibrate
  WLAT3:
  if (Time() == 1)
  {
    goto WLAT4;
  }
  else
  {
    goto WLAT3;
  }
  
  // up again
  WLAT4:
  LeftArmDaisyChain.setServoPosition(0, 200);
  LeftArmDaisyChain.communicate();
  
  // let motors calibrate
  WLAT5:
  if (Time() == 1)
  {
    goto WLAT6;
  }
  else
  {
    goto WLAT5;
  }
  
  // down all the way
  WLAT6:
  LeftArmDaisyChain.setServoPosition(0, 0);
  LeftArmDaisyChain.communicate();
}

void WaveWithRightArm()
{
  // up
  RightArmDaisyChain.setServoPosition(0, 55);
  RightArmDaisyChain.communicate();
  
  // let motors calibrate
  TimeNeeded = 500;
  
  WRAT1:
  if (Time() == 1)
  {
    goto WRAT2;
  }
  else
  {
    goto WRAT1;
  }
  
  // down
  WRAT2:
  RightArmDaisyChain.setServoPosition(0, 127);
  RightArmDaisyChain.communicate();
  
  // let motors calibrate
  WRAT3:
  if (Time() == 1)
  {
    goto WRAT4;
  }
  else
  {
    goto WRAT3;
  }
  
  // up again
  WRAT4:
  RightArmDaisyChain.setServoPosition(0, 55);
  RightArmDaisyChain.communicate();
  
  // let motors calibrate
  WRAT5:
  if (Time() == 1)
  {
    goto WRAT6;
  }
  else
  {
    goto WRAT5;
  }
  
  // down all the way
  WRAT6:
  RightArmDaisyChain.setServoPosition(0, 255);
  RightArmDaisyChain.communicate();
}

void RaiseLeftHand()
{
  LeftArmDaisyChain.setServoPosition(0, 200);
  LeftArmDaisyChain.communicate();
}

void RaiseRightHand()
{
  RightArmDaisyChain.setServoPosition(0, 55);
  RightArmDaisyChain.communicate();
}

void PutDownLeftHand()
{
  LeftArmDaisyChain.setServoPosition(0, 0);
  LeftArmDaisyChain.communicate();
}

void PutDownRightHand()
{
  RightArmDaisyChain.setServoPosition(0, 255);
  RightArmDaisyChain.communicate();
}

void TurnLeft()
{
  analogWrite(RightFootDCMotorPin, 255);
  TimeNeeded = 3000;
  
  TLT1:
  if (Time() == 1)
  {
    // continue...
  }
  else
  {
    goto TLT1;
  }
}

void TurnRight()
{
  analogWrite(LeftFootDCMotorPin, 255);
  TimeNeeded = 3000;
  
  TRT1:
  if (Time() == 1)
  {
    // continue...
  }
  else
  {
    goto TRT1;
  }
}

void MoveForward()
{
  analogWrite(LeftFootDCMotorPin, 255);
  analogWrite(RightFootDCMotorPin, 255);
  TimeNeeded = 3000;
  
  MFT1:
  if (Time() == 1)
  {
    // continue...
  }
  else
  {
    goto MFT1;
  }
}

// bends at elbow with hand in front of robot
void ReceiveWithLeftHand()
{
  LeftArmDaisyChain.setServoPosition(1, 255);
  LeftArmDaisyChain.communicate();
}

// bends at elbow with hand in front of robot
void ReceiveWithRightHand()
{
  RightArmDaisyChain.setServoPosition(1, 0);
  RightArmDaisyChain.communicate();
}

// time without delay()
int Time()
{
  unsigned long Interval = TimeNeeded;
  unsigned long CurrentTime = millis();
  
  if ((CurrentTime - LastTimeUpdate) > Interval)
  {
    LastTimeUpdate = CurrentTime;
    
    // time is up
    return 1;
  }
  else
  {
    // time is not yet up
    return 0;
  }
}

void setup()
{
  // set up DC motors
  pinMode(LeftFootDCMotorPin, OUTPUT);
  pinMode(RightFootDCMotorPin, OUTPUT);
}

void loop()
{
  T1a:
  RaiseLeftHand();
  RaiseRightHand();
  TimeNeeded = 2000;
  
  T1b:
  if (Time() == 1)
  {
    // time is up; continue
    goto T2a;
  }
  else
  {
    // go back and repeat check
    goto T1b;
  }
  
  T2a:
  PutDownLeftHand();
  PutDownRightHand();
  
  T2b:
  if (Time() == 1)
  {
    goto T3a;
  }
  else
  {
    goto T2b;
  }
  
  T3a:
  WaveWithLeftArm();
  
  T3b:
  if (Time() == 1)
  {
    goto T4a;
  }
  else
  {
    goto T3b;
  }
  
  T4a:
  WaveWithRightArm();
  
  T4b:
  if (Time() == 1)
  {
    goto T5a;
  }
  else
  {
    goto T4b;
  }
  
  T5a:
  TurnLeft();
  TimeNeeded = 1000;
  
  T5b:
  if (Time() == 1)
  {
    goto T6a;
  }
  else
  {
    goto T5b;
  }
  
  T6a:
  TurnRight();
  
  T6b:
  if (Time() == 1)
  {
    goto T7a;
  }
  else
  {
    goto T6b;
  }
  
  T7a:
  TimeNeeded = 2000;
  MoveForward();
  
  T7b:
  if (Time() == 1)
  {
    goto T8a;
  }
  else
  {
    goto T7b;
  }
  
  T8a:
  ReceiveWithLeftHand();
  
  T8b:
  if (Time() == 1)
  {
    goto T9a;
  }
  else
  {
    goto T8b;
  }
  
  T9a:
  PutDownLeftHand();
  
  T9b:
  if (Time() == 1)
  {
    goto T10a;
  }
  else
  {
    goto T9b;
  }
  
  T10a:
  ReceiveWithRightHand();
  
  T10b:
  if (Time() == 1)
  {
    goto T11a;
  }
  else
  {
    goto T10b;
  }
  
  T11a:
  PutDownRightHand();
  
  T11b:
  if (Time() == 1)
  {
    goto T1a;
  }
  else
  {
    goto T11b;
  }
}

"goto" is an extremely efficient way of moving around a program . . . if you're a compiler.

For us mere mortals, gotos produce hard-to-read spaghetti code, so don't use them.

  TimeNeeded = 500;
 
  WLAT1:
  if (Time() == 1)
  {
    goto WLAT2;
  }
  else
  {
    goto WLAT1;
  }

All you've done is moved all the blocking that was done in "delay()" out into your own code - you're still blocking.

Look at how the blink without delay avoids blocking.

Regards,
Ray L.

Your multiple, multiple gotos seem to be doing this:

  while(Time() != 1) {
    //wait
  }

If you get just one of those WLAT1 labels wrong or copy-paste a bit of code without thoroughly checking all the labels inside it then lots of aparrently random things will happen.

My code snippet can be pasted anywhere into the code and it won't jump to a random label elsewhere in the code. That's why gotos are bad.

It is still the wrong way to do it. If you need a delay and there is absolutely nothing else that you program needs to do at the same time, then just use the delay() function. "Waiting for motors to calibrate" is a good example where delay should be used.

Is this better? MorganS, the answer is that I intend to have other things going on, but not just yet. I am working on getting the servos to work right now, but it makes sense to go ahead and write a non-blocking version of delay() because in the near future I will add sensors to the code. I would like to implement multitasking in the near future.

//
// Deep Think 0.0.0.2
//
// programmed by George Andrews
//
// NOTE: This program is designed for the Meccanoid G15 (combined
// with the 86Duino One). It is a part of the Deep Think Project
// (first version of the software for the project). The Deep Think
// Project is designed to advance the intelligence (as well as
// motion and structural capabilities) of humanoid robots. This
// version of the program is designed to do the following.
//
// 1. Perform various moves using functions.
// 2. Control the 2 D/C motors with the 86Duino.
//
// Please note that this version is only intended as a demo to
// establish a basic framework for interfacing the Meccanoid
// electronic parts with the 86Duino One for future development.
// This version of the software, however, has the following
// additional capabilities compared to the previous version Deep
// Think 0.0.0.1.
//
// 1. Can interface with the D/C motors as well as the servos and
//    LED eyes.
// 2. Can perform various moves using functions in the program.
//

#include "MeccaBrain.h"

//
// Variables
//

// pin declarations
int LeftArmDaisyChainPin = 21;
int LEDEyePin = 29;
int RightArmDaisyChainPin = 38;
int LeftFootDCMotorPin = 11;
int RightFootDCMotorPin = 13;

// class objects
MeccaBrain LeftArmDaisyChain(LeftArmDaisyChainPin);
MeccaBrain LEDEye(LEDEyePin);
MeccaBrain RightArmDaisyChain(RightArmDaisyChainPin);

// timer variables
unsigned long TimeNeeded;
unsigned long LastTimeUpdate = millis();

//
// Functions
//

void WaveWithLeftArm()
{
  // up
  LeftArmDaisyChain.setServoPosition(0, 200);
  LeftArmDaisyChain.communicate();
  
  // let motors calibrate
  TimeNeeded = 500;

  if (Time() == 1)
  {
    // down
    LeftArmDaisyChain.setServoPosition(0, 127);
    LeftArmDaisyChain.communicate();
  }
  
  if (Time() == 1)
  {
    // up again
    LeftArmDaisyChain.setServoPosition(0, 200);
    LeftArmDaisyChain.communicate();
  }

  if (Time() == 1)
  {
    // down all the way
    LeftArmDaisyChain.setServoPosition(0, 0);
    LeftArmDaisyChain.communicate();
  }
}

void WaveWithRightArm()
{
  // up
  RightArmDaisyChain.setServoPosition(0, 55);
  RightArmDaisyChain.communicate();
  
  // let motors calibrate
  TimeNeeded = 500;
  
  if (Time() == 1)
  {
    // down
    RightArmDaisyChain.setServoPosition(0, 127);
    RightArmDaisyChain.communicate();
  }
  
  if (Time() == 1)
  {
    // up again
    RightArmDaisyChain.setServoPosition(0, 55);
    RightArmDaisyChain.communicate();
  }
  
  if (Time() == 1)
  {
    // down all the way
    RightArmDaisyChain.setServoPosition(0, 255);
    RightArmDaisyChain.communicate();
  }
}

void RaiseLeftHand()
{
  LeftArmDaisyChain.setServoPosition(0, 200);
  LeftArmDaisyChain.communicate();
}

void RaiseRightHand()
{
  RightArmDaisyChain.setServoPosition(0, 55);
  RightArmDaisyChain.communicate();
}

void PutDownLeftHand()
{
  LeftArmDaisyChain.setServoPosition(0, 0);
  LeftArmDaisyChain.communicate();
}

void PutDownRightHand()
{
  RightArmDaisyChain.setServoPosition(0, 255);
  RightArmDaisyChain.communicate();
}

void TurnLeft()
{
  analogWrite(RightFootDCMotorPin, 255);
  TimeNeeded = 3000;

  if (Time() == 1)
  {
    // continue...
  }
}

void TurnRight()
{
  analogWrite(LeftFootDCMotorPin, 255);
  TimeNeeded = 3000;

  if (Time() == 1)
  {
    // continue...
  }
}

void MoveForward()
{
  analogWrite(LeftFootDCMotorPin, 255);
  analogWrite(RightFootDCMotorPin, 255);
  TimeNeeded = 3000;

  if (Time() == 1)
  {
    // continue...
  }
}

// bends at elbow with hand in front of robot
void ReceiveWithLeftHand()
{
  LeftArmDaisyChain.setServoPosition(1, 255);
  LeftArmDaisyChain.communicate();
}

// bends at elbow with hand in front of robot
void ReceiveWithRightHand()
{
  RightArmDaisyChain.setServoPosition(1, 0);
  RightArmDaisyChain.communicate();
}

// time without delay()
int Time()
{
  unsigned long Interval = TimeNeeded;
  unsigned long CurrentTime = millis();
  
  if ((CurrentTime - LastTimeUpdate) > Interval)
  {
    LastTimeUpdate = CurrentTime;
    
    // time is up
    return 1;
  }
  else
  {
    // time is not yet up
    return 0;
  }
}

void setup()
{
  // set up DC motors
  pinMode(LeftFootDCMotorPin, OUTPUT);
  pinMode(RightFootDCMotorPin, OUTPUT);
}

void loop()
{
  RaiseLeftHand();
  RaiseRightHand();
  TimeNeeded = 2000;

  if (Time() == 1)
  {
    // time is up; continue
    PutDownLeftHand();
    PutDownRightHand();
  }
  
  if (Time() == 1)
  {
    WaveWithLeftArm();
  }
  
  if (Time() == 1)
  {
    WaveWithRightArm();
  }
  
  if (Time() == 1)
  {
    TurnLeft();
    TimeNeeded = 1000;
  }
  
  if (Time() == 1)
  {
    TurnRight();
  }
  
  if (Time() == 1)
  {
    TimeNeeded = 2000;
    MoveForward();
  }
  
  if (Time() == 1)
  {
    ReceiveWithLeftHand();
  }
  
  if (Time() == 1)
  {
    PutDownLeftHand();
  }
  
  if (Time() == 1)
  {
    ReceiveWithRightHand();
  }
  
  if (Time() == 1)
  {
    PutDownRightHand();
  }
  
  if (Time() == 1)
  {
    // continue...
  }
}

I would suggest to visit less marketing and more technical and programming lectures.

Very funny. I have attended none of either. I taught myself everything I know about Arduino and programming.

For those of you who are entangled in this interminable thread, I present two data points.

Pete