Rubik's cube Robot solver

There are 100+ robots cube solver on the Web
Most of them are Lego Mindstorm based, some others use very expensive hardware
Few Arduino based robots in fact

I plan to build something like this:


>> Video <<
but... cheaper :slight_smile:

or more likely


>> Video <<
but with better gripping

The robot will be semi-autonomous
Color recognition and solving to be made at PC level
Cube movements performed under Arduino control, at robot level
(USB cable could be disconnected at this stage)

Solving will be done through already existing software, Cube Explorer appears to be a good candidate
A middleware will be developped (using Processing or Python) to convey information back and forth
between Arduino and cube solver software

Road map:

  • design a suitable gripper
  • robot construction (2 grippers + frame), test and tuning
  • develop Arduino code for moving the Cube
  • develop the software link between solving and moving
  • final integration

I will use this thread as a blog, to share success and failure
and possibly obtain ideas an advices from our community

Not sure to reach destination but the route itself is promising and definitely exciting

Stay tuned :wink:

1 Like

Reserved for additional information, photos and videos

Parts list (PC/webcam version)

Frame:

  • Plywood 10mm

Grippers:

Drawing
Assembly video

EDIT (feb 2018)
The grippers 3D printed version has been released on Thingiverse

This is the Fusion 360 final assembly, including the wrist servo:


exploded view video

the embedded horns are designed for Futaba 25 teeth spline geometry (Futaba/TowerPro/Orion/Savox/ProTekAce/Bluebird...)
I will release the two specific parts for Hitech servo's family (24T) if required
Discussion starts at post #428

Cube:
I use a Dayan GuHong (57 mm), the DaYan ZhanChi is also very good
Do not use the not so smooth genuine Rubik's Cube(tm)
The reference site in China for cubes: http://lightake.com/c/DaYan_001001005

Other parts:

Power supply: 6V, 2000mA
I use two 18650 LiFePO4 batteries for powering both Servo's and Arduino

Software:
Arduino: CubeMover V1.3 NEW VarSpeedServo Library
Python (PC): RubikKasBot V1.3
Solving (PC): Cube Explorer

Testing:
Make sure you test your hardware according to post #6 and post #13

Android version (Autonomous design w/o PC)

Robotic hardware is unchanged, webcam is replaced by an Android smarphone

This version works this way:

Smartphone - take a picture from face #1 and perform color recognition for the 9 facelets
Smartphone - send a Bluetooth command to Arduino to rotate cube to next face
Arduino - rotate Cube to next face
Smartphone - same for all 6 faces
Smartphone - build and send via WiFi a query to a dedicated Rubik's solving server (Montréal, Canada)
Smartphone - receive solution from server (Singmaster notation)
Smartphone - send Bluetooth command to Arduino (Singmaster notation format)
Arduino - move Cube according to command and put the cube in the right order

Typical query to server:
http://nova.polymtl.ca/~simark/solverB/query.php?b=WGGOOOGGG&l=OOGBBYOOY&f=YBBBRRBRR&r=ORBBGGBRR&u=YYRYYGRWW&d=OYYWWWWWW

Corresponding answer from server: OK F U F D'
Clic the query and see for yourself :wink:
EDIT: this server is now offline, see below

Aditional hardware:
HC-05 or HC-06 Bluetooth board for smartphone/Arduino communication (3$ shipped on eBay)

or a very convenient Arduino/HC-05 combo

Software:
Arduino: CubeMover V1.3 NEW VarSpeedServo Library
Smartphone: Rubik's App is available on request (free)

You should understand smartphone connection with HC-05 / HC-06 Bluetooth board
The best approach is to have Joystick BT Commander up and running (same protocol)

EDIT July 28th, 2018
End of technical support

I have been supporting this project for nearly four years, this was an exciting experience
I have since moved to other projects and it's now time for me to retire

All possible questions have been answered in this thread, just read it :wink:
The Android App is here
Feel free to use this thread on a peer to peer basis to share your experience
Please no more PM's

Thanks again for your interest and good luck for your projects

EDIT March 3rd, 2021
** Notice for the android version only **

The nova server from Polytechnique Montreal is offline since last november, please look here for more info

Don't dump your beloved robot yet :wink: , just build a local server on your personal computer

The easy way:
Download Cube Explorer 5.14 from Kociemba, uncompress
Cube Explorer is a stand alone application (no install), in its own folder
Open folder, delete cube514qtm.exe (not used), launch cube514htm.exe
At first run only, the program will create some additional files within its folder
Go to Option/Web Server and check 'Enable Web Server'
You'r all set

Now open your favorite Web browser and copy/paste this URL:
http://localhost:8081/?uufuurfddbfllrrlffulllfflffbuuddddddbbrllubbudrrbbbrrr
Response: F U F D' as expected

You may just clic the above query and see for yourself :wink:

For seasoned users:
Install your own Raspberry Py server using Kociemba Python code

Now... the current android App (V1.23) has a burnt in query header (http://nova.polymtl.ca/~simark/solverB/query.php?)
I just modified the App and added an option for entering a specific server URL
To get V2.0 (free), just leave me a PM including your email address

Week one progress & report :wink:

I choosed the Dagu claw MK1 design as a starting point

The final claw should grip the cube (55mm) and open sufficiently (55 X 1.414, thanks pythagoras :wink: ) to allow cube rotation

Here is a first working prototype made with plywood


>> Video <<

During testing, the gripper is controlled with an Android smartphone,
using Joystick BT Commander

That gripper may not have the strength and rigidly to grip and twist a Rubik's cube.

Hi zoomkat, happy to see you here :wink:

This gripper is definitely not rigid enough
I just built three new prototypes with 5mm plywood (was 3mm)
the last one has four rods (versus two) and is now strong enough to twist the cube

OK, new Forum bugs are slowly desappearing, time to post again :slight_smile:

This is the (probably) final design


>> Video <<

Time to start testing with a Rubik's cube

First test with a real Rubik's cube

>> Video <<

Here is the demo code

#define VERSION     "\n\nGripper demo V1.2- @kas2014\n"

// V1.2  use VarSpeedServo library
// V1.1  2 servo's
// V1.0  1 servo

#include <VarSpeedServo.h> 

#define    rot_pin              10                  // Green
#define    pinch_pin             9                  // Yellow

#define    CLOSE                85                  // servo's limits
#define    OPEN                132
#define    CW                    0
#define    MID                  87
#define    CCW                 171

#define    SLOW                 30
#define    FAST                 80

VarSpeedServo pinch_servo;                          // create servo objects
VarSpeedServo rot_servo;

void setup()  {
  pinch_servo.attach(pinch_pin);  
  rot_servo.attach(rot_pin, 580, 2570);
  gripOpen(FAST);
  armCenter(FAST);
  delay(500);
}

void loop() {
  for(int i=0; i<3; i++)  {
    gripClose(SLOW);
    armRight(SLOW);
    gripOpen(SLOW);
    armCenter(SLOW);
  }
  delay(500);

  for(int i=0; i<3; i++)  {
    gripClose(FAST);
    armRight(FAST);
    gripOpen(FAST);
    armCenter(FAST);
  }
  delay(500);

  for(int i=0; i<2; i++)  {
    gripClose(SLOW);
    armLeft(SLOW);
    gripOpen(SLOW);
    armCenter(SLOW);
  }
  while(true);
} 

void armRight(int speed)    { rot_servo.write(CW, speed, true); }
void armLeft(int speed)     { rot_servo.write(CCW, speed, true); }
void armCenter(int speed)   { rot_servo.write(MID, speed, true); }
void gripOpen(int speed)    { pinch_servo.write(OPEN, speed, true); }
void gripClose(int speed)   { pinch_servo.write(CLOSE, speed, true); }

For this project I chose the "VarSpeedServo" library which has some definite advantages
compared to the standard Servo library:

  • adjustable servo's speed (see video)
  • optional code pause until move is complete

Next stop: build a second gripper and assemble the final robot

** EDIT: see post #111 **

Nice looking setup. I'm assuming that the code actually makes the arm do what you claim. But, how is that going to be sufficient to solve a rubik's cube? How is the robot/Arduino going to know what moves to make to solve the cube? How is it going to know where to start? How is it going to know when to stop?

Hi Paul,

Nice looking setup

Thanks

how is that going to be sufficient to solve a rubik's cube?
How is the robot/Arduino going to know what moves to make to solve the cube?

Please refer to post#1, the solving itself will be performed using Cube Explorer
Solution will be passed to Arduino using Singmaster notation
(look here for a visual approach)

Arduino will convert notation into real world moves

The Rubik's cube Robot solver, together with my assistant :wink:

The hardware side is finished, let's produce some code to make it alive

together with my assistant

Your assistant does not look impressed. You need another groupie.

Nicely laid out and good fit.

Your assistant does not look impressed

She is just a bit jealous of this time consuming project :wink:

Nicely laid out and good fit

Thanks zoomkat

First cube moves according to Singmaster notation (see post #8)
< D, B, B', D' >
This sequence will finally bring back the cube to its initial position


>> Video <<

Those 4 moves are hard coded in the Arduino sketch

I will now prepare a sketch with all possible moves:
F (Front), B (Back), U (Up), D (Down), L (Left), R (Right), plus the " ' " and " 2 " variants

For debugging purpose, moves will be entered through Arduino's serial monitor

EDIT: see post #242 (code)

Hi Looking for something to build. I am a relative newbie to Arduino and have made a finished a robot car using a uno board and Robot Shield. I found this quite easy and want to make the rubixcube solver. How hard was this build on a scale of 1-10? Looks good. Need one as I can complete a rubixcude in a couple of days.

Hi bjim525

You just passed from "Lurker" status to "Active Contributor"
Congratulation !! :wink:

How hard was this build on a scale of 1-10?

I would say... 6

The project is challenging both on the Hardware and Software sides
To create the grips, you need a CNC, a Laser cutter or a 3D printer
Do you have access to this type of equipment ??

Assembly is an easy task:


>> Video <<

Should you need additional info, let me know

I am in the progress of saving up the money for a 3d printer. Any ideas which one. The Arduino one looks good. Costs a lot though!!! :money_mouth_face: :money_mouth_face: :money_mouth_face:

Not too familiar with 3D printers :roll_eyes:

Rubik's robot parts are CNC'd using a cheap chinese 30X40 router

I will now prepare a sketch with all possible moves:
F (Front), B (Back), U (Up), D (Down), L (Left), R (Right), plus the " ' " and " 2 " variants

For debugging purpose, moves will be entered through Arduino's serial monitor

Here it is:

#define VERSION       "Cube Mover V1.2  @kas2014\n"

// V1.2: refactored using Cube class
// V1.1: replaced Servo with VarSpeedServo library 
// V1.0: initial release

#include <VarSpeedServo.h> 
#include "cube.h"

// ---------- user adjustments -------------------
#define    DOWN_CLOSE          91 //92
#define    DOWN_OPEN          132
#define    DOWN_CW              6
#define    DOWN_MID            89
#define    DOWN_CCW           172

#define    BACK_CLOSE          84 //85
#define    BACK_OPEN          129
#define    BACK_CW              2
#define    BACK_MID            87
#define    BACK_CCW           171

#define    LOW_SPEED           50 //50
#define    HI_SPEED           100 //80
// -----------------------------------------------

#define    downPinchPin         9
#define    downRotPin          10
#define    backPinchPin         5
#define    backRotPin           6
#define    bipPin              11             // buzzer
#define    myRX                 2
#define    myTX                 3

#define    STX               0x02             // serial data frame delimiters
#define    ETX               0x03

Cube myCube(downPinchPin, downRotPin, backPinchPin, backRotPin);

char cmd[128];                                 // bytes received buffer

void setup() {
  Serial.begin(57600);
  Serial.println(VERSION);
  pinMode(bipPin, OUTPUT);     

  myCube.begin(HI_SPEED);                 // set HIGH servo's speed 
  myCube.downSetLimits(DOWN_CLOSE, DOWN_OPEN, DOWN_CW,DOWN_MID, DOWN_CCW); // set limits for pinch and rotation servo's
  myCube.backSetLimits(BACK_CLOSE, BACK_OPEN, BACK_CW, BACK_MID, BACK_CCW);
  myCube.seize();
  bip(20, 2);                             // bip
}

void loop() {
  if(getSerialData())       parseData(); 
}

// ---------------------------

boolean getSerialData()  {
  if(Serial.available())  {                           // data received from smartphone
    delay(2);
    cmd[0] =  Serial.read();  
    if(cmd[0] == STX)  {
      int i=1;      
      while(Serial.available())  {
        delay(1);
        cmd[i] = Serial.read();
//      Serial.print(cmd[i]);
        if(cmd[i]>'u' || i>124)    {  bip(20, 5);    return false; }    // Communication error  XXX reinitialiser à zero <<<
        if((cmd[i]==ETX))               return true;     // 
        i++;
      }
    }
  }
 return false; 
}

boolean getSerialMonitor()  {  // Serial Monitor fsetting: Newline
  if(Serial.available())  {
    for(int i=0; i<124; i++)    cmd[i] = 0;
    int n = Serial.readBytesUntil('\n', cmd, 124);
//   Serial.print(cmd[0]); Serial.print(" ");
   cmd[n+1] = ETX;
   return true;
  }
 return false; 
}

void parseData()    { // parseData(cmd)
  int i = 0;
  String progress = "";
  while (cmd[i] != ETX) {
//  Serial.print(cmd[i]); mySerial.print(" ");
    switch(cmd[i])  {

      // Move commands  ------------------------------------------------------------
      case 'R':                                                    //  'R' moves
        switch(cmd[i+1])  {
          case '2':
            Serial.print("R2 ");
            myCube.R2();
            break;
          case 39:
            Serial.print("R' ");
            myCube.Rp();
            break;
          default:
            Serial.print("R ");
            myCube.R();
            break;
        }
        break;
      case 'L':                                                    //  'L' moves
        switch(cmd[i+1])  {
          case '2':
            Serial.print("L2 ");
            myCube.L2();
            break;
          case 39:
            Serial.print("L' ");
            myCube.Lp();
            break;
          default:
            Serial.print("L ");
            myCube.L();
            break;
        }
        break;
      case 'U':                                                    //  'U' moves
        switch(cmd[i+1])  {
          case '2':
            Serial.print("U2 ");
            myCube.U2();
            break;
          case 39:
            Serial.print("U' ");
            myCube.Up();
            break;
          default:
            Serial.print("U ");
            myCube.U();
            break;
        }
        break;
      case 'D':       ** snip (9000 caracters limitation) **
      case 'F': 
      case 'B':
       }
        break;

      // Scan commands  -----------------------------------------------------------
      case 'f':                                             // Scan Front side
        myCube.scanFront();
        Serial.println("OKf");
        break;
      case 'r':                                            // Scan Right side
        myCube.scanRight();
        Serial.println("OKr");
        break;
      case 'b':                                            // Scan Back side
        myCube.scanBack();
        Serial.println("OKb");
        break;
      case 'l':                                            // Scan Right side
        myCube.scanLeft();
        Serial.println("OKl");
        break;
      case 'u':                                            // Scan Up side
        myCube.scanUp();
        Serial.println("OKu");
        break;
      case 'd':                                            // Scan Down side
        myCube.scanDown();
        Serial.println("OKd");
        break;
      case 'g':                                           // back to Front side
        myCube.scanFront2();
        Serial.println("OKg");
        break;

      // Other commands  --------------------------------------------------------------
      case 'T':                                          // release gripper pressure
        myCube.seize();
        bip(40, 2);
        Serial.print("seize");
        break;
      case 'S':                                         // change move speed
        switch(cmd[i+1])  {
          case '2':
            myCube.setSpeed(HI_SPEED);
            Serial.print("High Speed");
            break;
          case '1':
            myCube.setSpeed(LOW_SPEED);
            Serial.print("Low Speed");
            break;
        }
        break;
      case 'V':                                         // bips
        switch(cmd[i+1])  {
          case '4':
            bip(80, 4);
            Serial.print("bip (4)");
            break;
          case '2':
            bip(80, 2);
            Serial.print("bip (2)");
            break;
          default:
            bip(80, 1);
            Serial.print("bip ");
            break;
        }
        break;

      default:
          break;
      }
      i++;
  }
  Serial.println();
  bip(20, 2);
}

void bip(int duration, int n)    {            // Bip piezo: duration in ms, n repeats
  for(int i=0; i<n; i++)  {  
     digitalWrite(bipPin, HIGH);        
     delay(duration);
     digitalWrite(bipPin, LOW);         
     delay(75);
  }
}

I also created a library to separate the "logic" of the program from the low-level details
EDIT: see post #43 for complete code including cube.h

According to Singmaster notation,
myCube.L generates a move
myCube.L2 for move
myCube.Lp (L prime) for a <L'> move

For demonstration purpose, the cube was first mixed according to < D B2 R2 U' F' L F2 R D2 R B2 R' >
In this video, I entered < R B2 R' D2 R' F2 L' F U R2 B2 D' > (inverse moves) in the IDE Serial Monitor.


>> Video <<

The cube is restored, as expected :wink:

I will now create a simple Python script (2 buttons) that will:

  • Launch Cube Explorer
  • move the cube, face by face, for color recognition by Cube Explorer
  • request and obtain the solution from Cube Explorer
  • transfer this solution to Arduino

Nice!

I have started to build a cube solver a couple of times but never finished any one. I will follow your project and maybe i will be inspired to build one too. After all you got me to build an balancing bot :slight_smile: