Monday, January 13, 2014

Face following RC Car using Android + Arduino + USB Host Shield

I have been working with an Arduino + RC Car setup for a quite a sometime in this post and this post. Finally I wanted to do something with it by integrating it to an Android phone. After some googling I found that I can connect the Android + Arduino using USB Host Shield in two different ways. One is where the Arduino + USB Host Shield acts as an Android Accessory (I have a done a post using that too) and the other one is where the Arduino + USB Host Shield is connected through Android Debug Bridge (ADB). I used the ADB method because I have already tried the Accessory method.

The uniqueness in this approach is that many people have done RC Car Hacks using Arduino to control the Remote Control not directly the car itself. The following hack directly controls the car instead of sending commands using the Remote control.

So I have already connected the RC Car's control pins to Arduino (Check out the previous posts) and so started coding my arduino sketch which is below.

 #include <SPI.h>  
 #include <Adb.h>  
 #define FW_PIN 3  
 #define BW_PIN 4  
 #define LF_PIN 5  
 #define RT_PIN 6  
 // Adb connection.  
 Connection * connection;  
 // Elapsed time for ADC sampling  
 long lastTime;  
 void actuate(int pin);  
 void turnLeft();  
 void turnRight();  
 // Event handler for the shell connection.   
 void adbEventHandler(Connection * connection, adb_eventType event, uint16_t length, uint8_t * data)  
 {  
  int i;  
  // Data packets contain two bytes, one for each servo, in the range of [0..180]  
  if (event == ADB_CONNECTION_RECEIVE)  
  {  
   Serial.println(data[0], DEC);  
   if(data[0] == 0x01) {  
     actuate(FW_PIN);  
   } else if(data[0] == 0x02) {  
     actuate(BW_PIN);   
   } else if(data[0] == 0x03) {  
     turnLeft();   
   } else if(data[0] == 0x04) {  
     turnRight();   
   }  
  }  
 }  
 void setup()  
 {  
  Serial.begin(9600);  
  // Note start time  
  lastTime = millis();  
  pinMode(FW_PIN, OUTPUT);  
  pinMode(BW_PIN, OUTPUT);  
  pinMode(LF_PIN, OUTPUT);  
  pinMode(RT_PIN, OUTPUT);  
  // Initialise the ADB subsystem.   
  ADB::init();  
  // Open an ADB stream to the phone's shell. Auto-reconnect  
  connection = ADB::addConnection("tcp:4567", true, adbEventHandler);   
 }  
 void loop()  
 {  
  lastTime = millis();  
  // Poll the ADB subsystem.  
  ADB::poll();  
  delay(50);  
 }  
 void actuate(int pin) {  
  digitalWrite(pin, HIGH);  
  delay(50);  
  digitalWrite(pin, LOW);   
 }  
 void turnLeft() {  
  digitalWrite(LF_PIN, HIGH);  
  actuate(FW_PIN);  
  digitalWrite(LF_PIN, LOW);  
 }  
 void turnRight() {  
  digitalWrite(RT_PIN, HIGH);  
  actuate(FW_PIN);  
  digitalWrite(RT_PIN, LOW);  
 }  

And for the Android Face detection app I made use of OpenCV Sample - face detection app which is available in the OpenCV for Android SDK version 2.4.7 samples directory. I added the microbridge server source codes which I downloaded from ADB Microbridge google code site and did slight following modifications to OpenCV Android Face detection app.

AndroidManifest.xml

 <uses-permission android:name="android.permission.INTERNET">  

Because the Microbridge server is a TCP Server which listens on port 4567.

FdActivity.java


1:  package org.opencv.samples.facedetect;  
2:  ...  
3:  public class FdActivity extends Activity implements CvCameraViewListener2 {  
4:       ...       
5:       private Server server;  
6:       /** Called when the activity is first created. */  
7:       @Override  
8:       public void onCreate(Bundle savedInstanceState) {  
9:            ...  
10:            // Create TCP server  
11:            server = null;  
12:            try {  
13:                 server = new Server(4567);  
14:                 server.start();  
15:            } catch (IOException e) {  
16:                 Log.e("microbridge", "Unable to start TCP server", e);  
17:                 System.exit(-1);  
18:            }            
19:       }  
20:       public Mat onCameraFrame(CvCameraViewFrame inputFrame) {  
21:            ...  
22:            Rect firstFace = null;  
23:            if(facesArray.length > 0) { 
24:                 Size size = mRgba.size(); 
25:                 firstFace = facesArray[0];  
26:                 byte[] data = new byte[1];  
27:                 if(firstFace.x > size.width / 2) {  
28:                      data[0] = 0x03;  
29:                 } else if(firstFace.x < size.width / 2) {  
30:                      data[0] = 0x04;  
31:                 }   
32:                 try {  
33:                      server.send(data);  
34:                 } catch (IOException e) {  
35:                      // TODO Auto-generated catch block  
36:                      e.printStackTrace();  
37:                 }  
38:            }  
39:            return mRgba;  
40:       }       
41:  }  

The logic I have used here is if the First Detected Face is in the right half of the image then turn right or else turn left and go forward. In order for Android and Arduino to communicate using ADB USB Debugging must be enabled in Android.

Finally you can see the entire thing working together in the below video.





The USB cable to Arduino is only connected to power up the whole unit. I tried a 9v Battery and you can see the battery connector connected to the Arduino but the phone was also drawing power from the battery thus it wasn't enough to power up the system. But I think a 12v Battery may be sufficient enough.

If anyone need the full source codes you can find it at github.com/shazin/facecar

References
Trackbacks/Pings

No comments: