Introduction
This is the part one from a series of four blogs. By the end of this series, I want to built an Arduino based kit car with “mechanum” wheels and which can be controlled in following ways:
- Program (for preprogrammed actions)
- Android App
- Voice commands
- Orientation sensor controlled
- Button tap or type commands
- Magic Wand using gestures
Additionally, I want to secure the magic wand with a secret spell or gesture.
This touches many interesting areas like motor control, bluetooth to bluetooth communication, building an android app, machine learning inference on microcontrollers and many more. Let’s get started.
Mechanum Wheel
First, what the heck is a mechanum wheel? It’s a tireless wheel invented by a Swedish inventor Bengt Erland Ilon, while working for a company named Mechanum AB. It has a pair of right handed and left handed wheels as shown below.
When controlled in a certain patterns, it can not only rotate on a dime like a battle tank, but also gets almost magical powers to move sideways like a crab or even diagonally. It can even pivot on any of its wheels or any of its axels. Take a look. I do not know of anything else with a wheel, that is capable of doing this.
This enables any platform with a mechanum wheel, to move in any direction on a flat surface, also called “omni directional” capability. This is extremely useful for movement in tight spaces for example on aircraft carriers, airports, warehouses etc.
If you have ever noticed and wondered how your luggage containers move sideways or forward on the same platform while being loaded on the airplane, well, now you know.
Building the prototype car
Following are the components needed:
- Acrylic Car kit – typically comes with standard wheels, TT motors etc.
- 2 pairs – Right handed and Left handed Mechanum wheels
- 4# – of TT Geared 3V-6V DC motors
- Arduino Uno
- HM-10 bluetooth module
- 2# – L298N motor control module
- 2# – LiPO battery greater than 7.4V
- 3# – 1K Ohm resistors connect in 2:1 (or a 10K Ohm and a 4.7K Ohm)
- Breadboard
- Jumper wires
Connections are as shown below.
Words of Caution:
- HM-10 module needs to use level shifter or voltage divider for its Rx receiving line which is connected to TX line of Arduino Uno. This is because logic level of Arduino is 5V while HM-10 is only 3.3V logic level tolerant. I have not shown it in the circuit diagram below. (You can read more on Tx Rx connections in my other blog here)
- Although I have taken every care to show the connection correctly, mistakes happen. Please test your connection carefully. Voltage and current involved is sufficient to ruin the modules or even start fires.
Truth be told, I have so far fried a bluetooth module, caused smoke and melted wires due to incorrect connections and learned some very valuable lessons.
Code
Next, upload the below code on Arduino. This program will demo the various patterns of motion that a mechanum wheel car can perform. The code has various functions corresponding to each of those motions. In each function, specific motors are switched on or off and also the direction of those motors are controlled to get the desired motion. The program also has code related to bluetooth and calling various functions based on the codes received over bluetooth. This will be used in the part two of this blog series.
Refer the following image to understand how mechanum wheels achieve the magic tricks.
// Control all 4 wheels independantly // For mechanum wheels // All turns need change as only 2 motors are used and turns are straight line instead of curve of typical turn // 2 more motions - glideright, glideleft are enabled by mechanum // No change needed for forward, back, rotateright, rotateleft // // motor 1 #define ENA 10 #define IN1 9 #define IN2 8 // motor 2 #define ENB 11 #define IN3 13 #define IN4 12 // motor 3 #define ENC 5 #define IN5 7 #define IN6 6 // motor 4 #define END 3 #define IN7 4 #define IN8 2 // LED lights //#define LED 13 //// LED Boolean //bool Mode = 0; // for gesture received from bluetooth char getstr = 'D'; //initialized to stop all ////For voice vontrol using AMR_Voice bluetooth app //String voice; //char c; // car speed default @ 80 %, if not provided at the start of the communication int carSpeed = 150; // 80 % 200 = ((80*100)/255) // relative speed divider for motor A, B, C, D int relA = 1; int relB = 1; int relC = 1; int relD = 1; //counter int i; // delay int wait = 1000; //void led() //{ // Mode = !Mode; // toggle mode // digitalWrite(LED, Mode); // // } // void test() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A analogWrite(ENA, carSpeed / relA); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); delay(wait); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); delay(wait); analogWrite(ENA, LOW); // motor B analogWrite(ENB, carSpeed / relB); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); delay(wait); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); delay(wait); analogWrite(ENB, LOW); // motor C analogWrite(ENC, carSpeed / relC); digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); delay(wait); digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); delay(wait); analogWrite(ENC, LOW); // motor D analogWrite(END, carSpeed / relD); digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); delay(wait); digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); delay(wait); analogWrite(END, LOW); Serial.println("TESTING"); } void forward() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("Forward"); delay(wait); } void back() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("Back"); delay(wait); } void rotateright() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("RotateRight"); delay(wait); } void rotateleft() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("RotateLeft"); delay(wait); } void mforwardrightturn() { // Relative speed divider relA = 1; relC = 1; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); Serial.println("MechanumForwardRightTurn"); delay(wait); } void mbackrightturn() { // Relative speed divider relB = 1; relD = 1; // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("MechanumBackRightTurn"); delay(wait); } void mforwardleftturn() { // Relative speed divider relB = 1; relD = 1; // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("MechanumForwardLeftTurn"); delay(wait); } void mbackleftturn() { // Relative speed divider relA = 1; relC = 1; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); Serial.println("MechanumBackLeftTurn"); delay(wait); } void glideright() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("MechanumGlideRight"); delay(wait); } void glideleft() { // Relative speed divider relA = 1; relB = 1; relC = 1; relD = 1; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("MechanumGlideRight"); delay(wait); } void forwardrightturn() { // Relative speed divider relA = 1; relB = 2; relC = 2; relD = 1; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("ForwardRightTurn"); delay(wait); } void backrightturn() { // Relative speed divider relA = 1; relB = 2; relC = 2; relD = 1; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("BackRightTurn"); delay(wait); } void forwardleftturn() { // Relative speed divider relA = 2; relB = 1; relC = 1; relD = 2; // motor A digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, HIGH); digitalWrite(IN6, LOW); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, HIGH); digitalWrite(IN8, LOW); analogWrite(END, carSpeed / relD); Serial.println("ForwardLeftTurn"); delay(wait); } void backleftturn() { // Relative speed divider relA = 2; relB = 1; relC = 1; relD = 2; // motor A digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); analogWrite(ENA, carSpeed / relA); // motor B digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); analogWrite(ENB, carSpeed / relB); // motor C digitalWrite(IN5, LOW); digitalWrite(IN6, HIGH); analogWrite(ENC, carSpeed / relC); // motor D digitalWrite(IN7, LOW); digitalWrite(IN8, HIGH); analogWrite(END, carSpeed / relD); Serial.println("BackLeftTurn"); delay(wait); } // stop all motors void stop() { digitalWrite(ENA, LOW); digitalWrite(ENB, LOW); digitalWrite(ENC, LOW); digitalWrite(END, LOW); Serial.println("Stop all motors"); } // all pins to low void stopall() { // motor A digitalWrite(ENA, LOW); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); // motor B digitalWrite(ENB, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); // motor C digitalWrite(ENC, LOW); digitalWrite(IN5, LOW); digitalWrite(IN6, LOW); // motor D digitalWrite(END, LOW); digitalWrite(IN7, LOW); digitalWrite(IN8, LOW); // digitalWrite(LED, LOW); Serial.println("Stop!"); delay(wait); } void demo() { // test(); delay(wait); forward(); back(); rotateleft(); rotateright(); // Mechanum specific glideright(); glideleft(); mforwardrightturn(); mbackrightturn(); mforwardleftturn(); mbackleftturn(); // Regular forwardrightturn(); backrightturn(); forwardleftturn(); backleftturn(); // Stop stopall(); } void bluetooth() { if (Serial.available() >0) { getstr = Serial.read(); switch(getstr) { // case 'O': led(); break; //LED toggle case 'F': forward(); break; //Forward case 'B': back(); break; //Back case 'L': glideleft(); break; //Rotate Left case 'R': glideright(); break; //Rotate Right case 'G': forwardleftturn(); break; //Forward Left Turn case 'I': forwardrightturn; break; //Forward Right Turn case 'H': backleftturn(); break; //Back Left Turn case 'J': backrightturn(); break; //Back Right Turn case 'W': rotateleft(); break; //Rotate Left case 'U': rotateright(); break; //Rotate Right case 'S': stop(); break; //Stop case 'D': stopall(); break; //Stop All case '0': carSpeed = 0; break; //Speed 0 % case '1': carSpeed = 25; break; //Speed 10 % case '2': carSpeed = 50; break; //Speed 20 % case '3': carSpeed = 75; break; //Speed 30 % case '4': carSpeed = 100; break; //Speed 40 % case '5': carSpeed = 125; break; //Speed 50 % case '6': carSpeed = 150; break; //Speed 60 % case '7': carSpeed = 175; break; //Speed 70 % case '8': carSpeed = 200; break; //Speed 80 % case '9': carSpeed = 225; break; //Speed 90 % case 'q': carSpeed = 255; break; //Speed 100 % default: break; } Serial.println (getstr); } } void setup() { Serial.begin(9600); // set all the motor control pins to outputs pinMode(ENA, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(ENB, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(ENC, OUTPUT); pinMode(IN5, OUTPUT); pinMode(IN6, OUTPUT); pinMode(END, OUTPUT); pinMode(IN7, OUTPUT); pinMode(IN8, OUTPUT); // set all the LED control pins to outputs // pinMode(LED, OUTPUT); // Initialize to stop all stop(); } void loop() { // demo(); bluetooth(); }
Next step
The next part of this blog talks about building an android app which will connect with the car over bluetooth. With the app, we will be able to control the car using phone as a steering wheel and also through voice commands, touch controls etc.
Thanks for reading.
You might also like