Introduction: Controlling Your Project

This project stems from the experience I got while building the first control panel for our humanoid project, which you can find here- you may want to read up on that. I discovered a few issues with the concept and redesigned the panel. I'll also discuss sensor interfacing, servo testing and other elements of humanoid robotics development at the high school level.

Includes:

  1. Tips for building a good control panel
  2. Using an ATX (old computer power supply) to power your project
  3. Wire management
  4. Joystick testing

Supplies

  • Acrylic plate, or any other baseboard.
  • Raspberry Pi, Arduino, or any other microcontroller you want to use. Be sure to include peripherals such as an LCD screen, an LED and/or buzzer, and some sort of keypad for input. It helps a lot to be able to give commands independently of a computer, hence the input and feedback devices.
  • Standard electronics components (jumper wires, etc.)
  • Small perforated board (5x7cm) and equipment for soldering, drilling, etc. Nothing out of the ordinary.

Step 1: Tips for Building a Control Panel

This is not the first or second panel I've built.

As a result, I've built up some experience with these types of things, learning about some 'rules' the hard way.

  • Position your voltage regulator in the center relative to anything that will draw power from it. Although it may seem neater to have it off to the side, it may cause issues with wire management. Try to position it closer to your modules.
  • That being said, don't put your power supply in the middle of everything. Have your regulated supply delivered close to the modules that need it but separate the mains transformer. It's important to be mindful of safety and short-circuiting. I had been using a high-power battery for some time and although we are generally very careful, ended up shorting out it's terminals and getting serious burns on my hand; if it had been installed in the middle of our electronics a few month's worth of work would have been ruined.
  • Ensure it's easy to access all electronics. Plugging in wires can be a pain otherwise.
  • Don't glue your modules down. I don't care if you think this is the 100% final version and that everything works. You are almost certified to discover some issue later on and modules glued down means that you will have to scrap everything. Use screws and standoffs. Practically every commercially available microcontroller or board has special holes specific to this purpose.
  • Make the entire panel as small as possible. In our first InMoov panel build, I made it at least twice as large. As a result it became cumbersome, difficult to mount, and had a lot of empty, unused space.
  • Mount the entire panel securely. You'll likely be drilling holes into it later, and you made accidentally hit it once or twice. A secure panel won't fall to the ground and crack.
  • Keep electronics farther away from the edges. In case you hit the panel, you want it to be the plastic you are hitting and not the electronics. In our case this wasn't really a problem as the shoulders of the robot were wide enough to provide adequate protection.

We used the LCD Keypad shield found here, shown in the pictures. Some test code is below, which should run the keypad and give you feedback. The Arduino is supposed to use wire.h to communicate with another board, but that hasn't been done yet.

KeypadTest.ino

#include<Wire.h>
#include<LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
constint rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int motor = 0, motorVal = 90;
constint analogPin = A0;
int sensor;
voidsetup() {
Serial.begin(9600); //Starts Serial Com
Wire.begin(); //Begins I2C communication at pin (A4,A5)
lcd.begin(16, 2);
}
voidloop() {
updateSensor();
lcd.setCursor(0, 0);
lcd.print("UP=motor_sel0-11");
lcd.setCursor(0, 1);
lcd.print("Motor: ");
lcd.print(motor);
if (sensor >1000) {
} elseif (sensor >700) {
lcd.clear();
lcd.print("select");
delay(200);
setMotor();
} elseif (sensor >500) {
lcd.clear();
lcd.print("left");
delay(200);
} elseif (sensor >300) {
lcd.clear();
lcd.print("down");
delay(200);
} elseif (sensor >100) {
lcd.clear();
lcd.print("up");
//delay(200);
switchMotor();
} else {
lcd.clear();
lcd.print("right");
delay(200);
Serial.println(sensor);
}
}
voidswitchMotor() {
if (motor < 11) {
motor++;
} else {
motor = 0;
}
delay(200);
}
voidsetMotor() {
updateSensor();
while (sensor < 700 || sensor >1000) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Motor ");
lcd.print(motor);
lcd.print(" pos -/+");
if (sensor >500 && sensor < 700) { //left
decrementMotor();
} elseif (sensor >300) { //down
} elseif (sensor >100) { //up
} else { //right
incrementMotor();
}
lcd.setCursor(0, 1);
lcd.print("Degrees ");
lcd.print(motorVal);
updateSensor();
delay(140);
}
sendMsg();
}
voidincrementMotor() {
if (motorVal <= 180) {
motorVal++;
} else {
error("Ex_lim+");
}
delay(10);
}
voiddecrementMotor() {
if (motorVal >= 0) {
motorVal--;
} else {
error("Ex_lim-");
}
delay(10);
}
voiderror(String message) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Error : ");
lcd.print(message);
delay(1000);
}
voidupdateSensor() {
sensor = analogRead(analogPin);
Serial.println(sensor);
delay(10);
}
voidsendMsg() { //will send a message to slave arduino
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sending motor ");
lcd.print(motor);
lcd.setCursor(0, 1);
lcd.print("");
lcd.print(motorVal);
lcd.print(" deg");
byte x = motor;
byte y = motorVal;
Wire.beginTransmission(8);
Wire.write(x);
Wire.write(y);
Wire.endTransmission();
delay(1000);
lcd.clear();
}
view rawKeypadTest.ino hosted with ❤ by GitHub

Step 2: Using an ATX

There are plenty of tutorials on this but I might as well go over it here. If you have a spare broken computer or two lying around (as most schools do) then you can disassemble it and use it as a high-power supply for your project. This is preferable to using batteries or regular transformers because it gives you a versatile +3.3V, +5V, +12V or -12V output. Power is typically over 100W for the 5V channel. However, you need to make modifications to it before you can start using it.

The images above include the pinout of a typical ATX connector. Generally, the wires you are concerned with are yellow (+12V), blue (-12V), red (+5V), orange (+3.3V), and black (common ground). These are the ones you would power your equipment with, although I like to include a secondary voltage regulator for extra protection when powering microcontrollers.

You need to make some modifications before you use the ATX. Whenever your ATX is plugged in, it is always receiving mains current, however only provides output power when you short the green wire with ground. This is how your computer is powered on only when you press the button; even though it is plugged in all the time. I would dedicate a switch to switching the whole system on and off.

It's helpful to have some indicator lights when troubleshooting. The purple wires provide a steady +5V output when the ATX is "off" or on "standby". The gray wire provides +5V output when you power the ATX "on". You can connect an LED to each of these outputs, using a 330Ω resistor to prevent the LED from burning out.

When soldering or making modifications to the ATX, ensure it is unplugged and you have left it alone for a few minutes for the internal capacitors to discharge. These can be just as dangerous as the mains power supply in itself. Always cover your bare wires with electrical tape or heat shrink tubing. The ATX should switch off if it detects a short circuit, but it's better not to test this theory.

Alternatively, you can buy an ATX breakout board such as the one here.

When mounting your ATX, keep it farther away from the circuit board so that loose wires don't get stuck in the fan or short something out. There's a photo showing how we mounted ours.

Step 3: Wire Management

Having good cable management when working on a large project is an absolute must. We learned this the hard way.

Use wire wrap to keep cables together and zip ties to anchor it down at strategic points. Label and test all connections prior to this, however; you will save yourself a lot of trouble by doing this.

Always be sure to account for joint rotation and other mechanical features of your project. In our robot, I had to account for multiple degrees of freedom and movement when completing the cable management for the arm. This meant approximately 40% more cable had to be used.

When working with extensive sets of servos, LEDs, or other such devices it's a good idea to build a breakout board of sorts. The purpose of such a device is to minimize the tangle of wires you're dealing with by creating a plug-in system that has power, ground, and signal wiring pre-wired. That way, all you have to do when making your connections is plug the cables in the board, and not worry about connections to the microcontroller.

Step 4: Using Joysticks for Testing

Granted, many projects are meant to operate with live remote control- that is, the user controls something in real-time. However, others operate off complex algorithms designed to move motors and other actuators based on information that may not be readily available. If you run your motors from your complex code and they malfunction, there's no way for you to tell if it's the code or the motor connections that are giving you trouble.

A joystick interface is a great way to test your project in advance of implementing troublesome code. Joystick modules such as the ones here are inexpensive and can serve many purposes. A joystick module will return two variable voltages from two perpendicularly-mounted potentiometers, and may also have a third output for pushing down on the joystick itself. The two potentiometer pins should go to a pin that can read an analog signal.

Our final project was intended to run off some forward and inverse kinematics algorithms which we didn't have done at the same time as the physical structure of the robot, so I wrote up some code to control the left arm's main functions (lift, bicep extension, etc.) This let us fine-tune limits on the motor range and establish any mechanical issues.

The Fritzing diagram for typical joystick interfacing is above. I included some code below from our project that illustrates how you can use the joysticks. It is commented and self-explanatory. You should base your test code on a similar structure.

joystickControl.ino

#include<Wire.h>
#include<Servo.h>
Servo thumb;//initialize servos being used
Servo index;
Servo middle;
Servo ring;
Servo pinkie;
Servo wrist;
Servo neckUpDown;
Servo neckRot;
Servo bicep;//360 deg servo
Servo shoulderRot;//360 deg servo
Servo shoulderOut;//360 deg servo
Servo shoulderLift;//360 deg servo
//Our 360 degree servos were modified by us, so each has a slightly different no-movement angle.
//We use these variables to simplify the code structure.
int bicep0 = 94;
int shoulderRot0 = 96;
int shoulderOut0 = 114;//up = 118
int shoulderLift0 = 89;
//These are variables that we'll use to convert the joystick potentiometer reading to
//a value that we add onto the servo zero val. This increases the speed in one direction or another.
int a, b, c, d;
int joy1X = A0; //configure which pins belong to which potentiometer
int joy1Y = A1;
int joy2X = A2;
int joy2Y = A3;
int x1, x2, y1, y2; //These are variables that store raw potentiometer readings.
voidsetup() {
Serial.begin(9600);
thumb.attach(9);
index.attach(8);
middle.attach(13);
ring.attach(12);
pinkie.attach(3);
wrist.attach(10);
//neckUpDown.attach(9);
//neckRot.attach(9);
bicep.attach(5);
shoulderRot.attach(7);
shoulderOut.attach(6);
shoulderLift.attach(4);
thumb.write(0);
index.write(0);
middle.write(0);
ring.write(0);
pinkie.write(0);
wrist.write(90); //the servo motors tend to move a little each time we reset, so this keeps them from
//moving too far and breaking a joint.
}
voidloop() { //main code
x1 = analogRead(joy1X); //rest = 512, up = 1024, down = 0. Assign a reading to a variable
y1 = analogRead(joy1Y);
x2 = analogRead(joy2X);
y2 = analogRead(joy2Y);
if (moved(x1)) { //if joystick has been moved, move the corresponding motor by the value of that joystick.
moveShoulderLift(x1);
} else {
moveShoulderLift(512); //otherwise 'move' the motor by the neutral value, which keeps it from turning.
}
if (moved(y1)) { //etc. Cycle through all potentiometers.
moveShoulderOut(y1);
} else {
moveShoulderOut(512);
}
if (moved(x2)) {
moveBicep(x2);
} else {
moveBicep(512);
}
if (moved(y2)) {
moveShoulderRot(y2);
} else {
moveShoulderRot(512);
}
delay(10); //delay to avoid errors in measurement.
}
boolean moved(int val) { //checks if potentiometer has moved. Although in theory it should return
//512 at neutral position, the real-life potentiometer may have deviations, therefore we use this to account for that.
if (val >520 || val < 500) {
returntrue;
} else {
returnfalse;
}
}
voidmoveShoulderLift(int val) { //methods that are unique to each motor
if (val < 512) {//if moved one way
a = (512 - val) / 10; //convert the pot reading to something the servo can accelerate by. You can adjust
//the /10 if you wish to experiment.
shoulderLift.write(shoulderLift0 + a);//tell servo to go to neutral position + the reading from the joystick.
} elseif (val >512) {//if moved the other way
a = (val - 512) / 10;
shoulderLift.write(shoulderLift0 - a);
} else {
shoulderLift.write(shoulderLift0);//otherwise stop the motor
}
}
voidmoveShoulderOut(int val) {//etc.
if (val < 512) {
b = (512 - val) / 10;
shoulderOut.write(shoulderOut0 - b);
} elseif (val >512) {
b = (val - 512) / 10;
shoulderOut.write(shoulderOut0 + b);
} else {
shoulderOut.write(shoulderOut0);
}
}
voidmoveBicep(int val) {
if (val < 512) {
c = (512 - val) / 10;
bicep.write(bicep0 + c);
} elseif (val >512) {
c = (val - 512) / 10;
bicep.write(bicep0 - c);
} else {
bicep.write(bicep0);
}
}
voidmoveShoulderRot(int val) {
if (val < 512) {
d = (512 - val) / 10;
shoulderRot.write(shoulderRot0 - d);
} elseif (val >512) {
d = (val - 512) / 10;
shoulderRot.write(shoulderRot0 + d);
} else {
shoulderRot.write(shoulderRot0);
}
}

Step 5: Conclusion

My original intent was to post a series of Instructables devoted to this InMoov robot project, but unexpected difficulties and overall a limited time frame have cut down on that. I've written up on what I could, and perhaps I'll post a complete Instructable on the entire project. For now, this will have to suffice.

Last updated: 01/18/2020