Mercurial > pkubik
changeset 17:07e5364d368e default tip
Add a high-level arduino controller
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Thu, 26 Feb 2015 18:18:06 +0100 |
| parents | dd935af37b17 |
| children | |
| files | controller/battery.ino controller/beep.ino controller/servos.ino dice/battery.h dice/battery.ino dice/beep.h dice/beep.ino dice/clock.h dice/clock.ino dice/creep.h dice/creep.ino dice/dice.ino dice/leg.h dice/leg.ino dice/misc.h dice/misc.ino dice/serial.h dice/serial.ino dice/servos.h dice/servos.ino |
| diffstat | 20 files changed, 590 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/controller/battery.ino Sat Feb 14 14:47:37 2015 +0100 +++ b/controller/battery.ino Thu Feb 26 18:18:06 2015 +0100 @@ -1,4 +1,4 @@ -#define BATTERY_PIN A5 +#define BATTERY_PIN A4 void battery_setup() { pinMode(BATTERY_PIN, INPUT); @@ -12,9 +12,8 @@ static int battery_fail = 0; int reading = get_battery(); - if (reading < 100) { + if (reading < 40) { battery_fail += 1; - tone(12, reading, 200); } else { battery_fail = 0; }
--- a/controller/beep.ino Sat Feb 14 14:47:37 2015 +0100 +++ b/controller/beep.ino Thu Feb 26 18:18:06 2015 +0100 @@ -1,4 +1,4 @@ -#define BEEP_PIN 12 +#define BEEP_PIN 3 void beep_setup() { pinMode(BEEP_PIN, OUTPUT);
--- a/controller/servos.ino Sat Feb 14 14:47:37 2015 +0100 +++ b/controller/servos.ino Thu Feb 26 18:18:06 2015 +0100 @@ -8,41 +8,41 @@ static const unsigned char SERVO_PINS[SERVOS] = { // ankle, knee, hip - 2, 3, 5, // front left - 9, 8, 6, // hind left - 10, 11, 13, // hind right - 17, 16, 14 // front right + 4, 5, 6, // front left + 9, 8, 7, // hind left + 10, 11, 12, // hind right + 16, 15, 14 // front right }; static const char SERVO_REVERSE[SERVOS] = { // ankle, knee, hip - -1, -1, 1, // front left - 1, 1, 1, // hind left - -1, -1, -1, // hind right - 1, 1, -1 // front right + -1, 1, 1, // front left + 1, -1, -1, // hind left + -1, 1, 1, // hind right + 1, -1, -1 // front right }; static const char SERVO_TRIM[SERVOS] = { // ankle, knee, hip - -6, 0, -8, // front left - 2, -8, -8, // hind left - -4, 0, 4, // hind right - 0, 2, 0 // front right + -10, -5, -10, // front left + 10, -5, -10, // hind left + 0, 0, 10, // hind right + 0, 0, 10 // front right }; static Servo servos[SERVOS]; static const double HOME_POSITION[SERVOS] = { // ankle, knee, hip - -PI4, 0, 0, // front left - -PI4, 0, 0, // hind left - -PI4, 0, 0, // hind right - -PI4, 0, 0 // front right + 0, 0, 0, // front left + 0, 0, 0, // hind left + 0, 0, 0, // hind right + 0, 0, 0 // front right }; static const double FOLD_POSITION[SERVOS] = { // ankle, knee, hip - 0, -PI2, -PI4, // front left - 0, -PI2, PI4, // hind left - 0, -PI2, PI4, // hind right - 0, -PI2, -PI4 // front right + PI2, PI2, -PI4, // front left + -PI2, -PI2, -PI4, // hind left + PI2, PI2, -PI4, // hind right + -PI2, -PI2, -PI4 // front right }; void servo_move(unsigned char servo, double rads) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/battery.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,8 @@ +#ifndef BATTERY_H +#define BATTERY_H + +void battery_setup(void); +int get_battery(void); +void battery_check(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/battery.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,29 @@ +#include "beep.h" + +#define BATTERY_PIN A3 +#define MIN_BATTERY 368 // About 3.59V. + + +void battery_setup() { + pinMode(BATTERY_PIN, INPUT); +} + +int get_battery() { + return analogRead(BATTERY_PIN); +} + +void battery_check() { + static int battery_fail = 0; + int reading = get_battery(); + + if (reading < MIN_BATTERY) { + battery_fail += 1; + beep(reading, 200); + } else { + battery_fail = 0; + } + if (battery_fail > 40) { + /* If it stays bad for 4s, shut down. */ + servo_shutdown(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/beep.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,9 @@ +#ifndef BEEP_H +#define BEEP_H + +extern bool beep_mute; + +void beep_setup(void); +void beep(int frequency, int duration); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/beep.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,20 @@ +#define BEEP_PIN 3 + +bool beep_mute = true; + + +void beep_setup() { + pinMode(BEEP_PIN, OUTPUT); + beep(1760, 50); + delay(100); + beep(1975, 50); + delay(50); + beep(1760, 50); +} + +void beep(int frequency, int duration) { + if (beep_mute) { + return; + } + tone(BEEP_PIN, frequency, duration); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/clock.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,9 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#define FPS 32 + +const unsigned char TICK = 1000 / FPS; +void tick(unsigned int ms); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/clock.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,14 @@ +#include "clock.h" + +void tick(unsigned int ms) { + // Wait remaining time to delay ms milliseconds since last call. + static unsigned long last = 0; + unsigned long now = millis(); + long wait = last + ms - now; + if (wait > 0) { + delay(wait); + } else { + // Somehow engage a time machine here to go back in time. + } + last = now; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/creep.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,14 @@ +#ifndef CREEP_H +#define CREEP_H + +/* Walking speed. */ +extern double creep_dx; // Sideways. +extern double creep_dy; // Forward. +extern double creep_spread; +extern double creep_rotation; +extern double creep_height; + +/* Perform one step of the creep gait with the current speed. */ +void creep(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/creep.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,126 @@ +#include "creep.h" +#include "misc.h" +#include "leg.h" +#include "clock.h" +#include "beep.h" + + +const unsigned int tones[4] = {880, 988, 1318, 1175}; + +/* Creep gait parameters. */ + +/* How far forward to move them when making a step? */ +#define STRIDE 12.0 + +/* How much to shift the body? */ +#define SHIFT 8.0 + +/* In how many steps should the body be shifted? */ +#define STEPS 4 + +/* What is the home position of the legs? */ +#define HOME ((COXA + FEMUR) / SQRT2) + +/* Given current leg, which leg to move next? */ +const unsigned char _NEXT_LEG[4] = {2, 0, 3, 1}; + +/* Body position. */ +static double _shift_x = 0.0; +static double _shift_y = 0.0; + +/* Which legs are touching the ground? */ +static bool _on_ground[4] = {true, true, true, true}; + +/* Walking speed. */ +double creep_dx = 0.0; // Sideways. +double creep_dy = 0.0; // Forward. +double creep_rotation = 0.0; // Rotation. +double creep_height = TIBIA * 0.75; // Body height. +double creep_spread = 0.0; // Leg spread. + +/* +Move all legs that are on the ground backwards, pushing the robot forward with +the current walking speed. This is called every tick, to make the robot move +continuously. +*/ +void _creep_move() { + for (unsigned char leg=0; leg < 4; ++leg) { + if (_on_ground[leg]) { + move_leg( + leg, + leg_position[leg][0] - creep_dx * LEG_X[leg] / 2, + leg_position[leg][1] - creep_dy * LEG_Y[leg] / 2, + -creep_height + ); + rotate_leg_by(leg, -creep_rotation * LEG_X[leg] * LEG_Y[leg] / 2); + } + } +} + +/* Called every frame. */ +void _creep_tick() { + _creep_move(); + tick(TICK); +} + +/* Shifts the whole body by defined amount. */ +void _shift_body_by(double dx, double dy) { + for (unsigned char leg=0; leg < 4; ++leg) { + move_leg_by(leg, dx * LEG_X[leg], dy * LEG_Y[leg], 0); + } + _shift_x += dx; + _shift_y += dy; +} + +/* Shifts the body away from the given leg, for balance. */ +void _shift_body(unsigned char leg) { + double dx = (LEG_X[leg] * SHIFT - _shift_x) / STEPS; + double dy = (LEG_Y[leg] * SHIFT - _shift_y) / STEPS; + for (unsigned char i = 0; i < STEPS; ++i) { + _shift_body_by(dx, dy); + _creep_tick(); + } +} + +/* Perform a full step with a single leg. */ +void _creep_step(unsigned char leg) { + _shift_body(leg); + _on_ground[leg] = false; + beep(440, 5); + for (unsigned char i = 0; i < 3; ++i) { + move_leg_by(leg, 0, 0, 10); + _creep_tick(); + } + beep(tones[leg], 25); + move_leg( + leg, + HOME + (_shift_x + creep_dx * STRIDE) * LEG_X[leg] + creep_spread, + HOME + (_shift_y + creep_dy * STRIDE) * LEG_Y[leg] + creep_spread, + leg_position[leg][2] + ); + _creep_tick(); + _creep_tick(); + _creep_tick(); + _on_ground[leg] = true; + beep(1865, 5); + for (unsigned char i = 0; i < 3; ++i) { + move_leg_by(leg, 0, 0, -10); + _creep_tick(); + } +} + +/* Perform one step of the creep gait with the current speed. */ +void creep() { + static unsigned char leg = 0; + + if ( + (creep_dx < -0.01 || creep_dx > 0.01) || + (creep_dy < -0.01 || creep_dy > 0.01) || + (creep_rotation < -0.01 || creep_rotation > 0.01) + ) { + _creep_step(leg); + leg = _NEXT_LEG[leg]; + } else { + _creep_tick(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/dice.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,21 @@ +#include "servos.h" +#include "beep.h" +#include "battery.h" +#include "creep.h" +#include "leg.h" +#include "misc.h" +#include "serial.h" + + +void setup() { + battery_setup(); + servo_setup(); + beep_setup(); + serial_setup(); +} + + +void loop() { + creep(); + serial_loop(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/leg.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,17 @@ +#ifndef LEG_H +#define LEG_H + +#define COXA 8.0 +#define FEMUR 20.5 +#define TIBIA 28.5 +#define BASE 18.5 + +const char LEG_X[4] = {-1, -1, 1, 1}; +const char LEG_Y[4] = {1, -1, -1, 1}; +extern double leg_position[4][3]; + +bool move_leg(unsigned char leg, double x, double y, double z); +bool move_leg_by(unsigned char leg, double dx, double dy, double dz); +bool rotate_leg_by(unsigned char leg, double angle); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/leg.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,82 @@ +#include "leg.h" +#include "misc.h" + +#define HOME ((COXA + FEMUR) / SQRT2) +const unsigned char LEG_JOINT[4][3] = { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}, + {9, 10, 11}, +}; + +double leg_position[4][3] = { + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, +}; + + +double _solve_triangle(double a, double b, double c) { + // Calculate the angle between a and b, opposite to c. + a = abs(a); + b = abs(b); + c = abs(c); + if (a + b < c || a + c < b || b + c < a) { return NaN; } + return acos((a * a + b * b - c * c) / (2 * a * b)); +} + +double _norm(double a, double b) { + // Calculate the norm of a vector. + return sqrt(a * a + b * b); +} + +bool _inverse_kinematics(double x, double y, double z, + double *ankle, double *knee, double *hip) { + // Calculate angles for knee and ankle, and put them in those variables. + // Return true on success, and false if x and y are out of range. + double f = _norm(x, y) - COXA; + double d = min(FEMUR + TIBIA, _norm(f, z)); + *hip = atan2(y, x); + if (isnan(*hip)) { return false; } + *knee = _solve_triangle(FEMUR, d, TIBIA) - atan2(-z, f); + if (isnan(*knee)) { return false; } + *ankle = _solve_triangle(FEMUR, TIBIA, d) - PI2; + if (isnan(*ankle)) { return false; } + return true; +} + +bool move_leg(unsigned char leg, double x, double y, double z) { + // Move the tip of the leg to x, y. Return false when out of range. + double ankle = NaN; + double knee = NaN; + double hip = NaN; + if (!_inverse_kinematics(x, y, z, &ankle, &knee, &hip)) { + return false; + } + hip -= PI4; + servo_move(LEG_JOINT[leg][0], ankle); + servo_move(LEG_JOINT[leg][1], knee); + servo_move(LEG_JOINT[leg][2], hip); + leg_position[leg][0] = x; + leg_position[leg][1] = y; + leg_position[leg][2] = z; + return true; +} + +bool move_leg_by(unsigned char leg, double dx, double dy, double dz) { + // Move the tip of the leg by dx, dy. Return false when out of range. + return move_leg(leg, + leg_position[leg][0] + dx, + leg_position[leg][1] + dy, + leg_position[leg][2] + dz); +} + +bool rotate_leg_by(unsigned char leg, double angle) { + // Rotate the tip of the leg around the center of robot's body. + double x = leg_position[leg][0] + BASE; + double y = leg_position[leg][1] + BASE; + double nx = x * cos(angle) - y * sin(angle) - BASE; + double ny = x * sin(angle) + y * cos(angle) - BASE; + return move_leg(leg, nx, ny, leg_position[leg][2]); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/misc.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,10 @@ +#ifndef MISC_H +#define MISC_H + +extern const double PI2; +extern const double PI4; +extern const double TAU; +extern const double SQRT2; +extern const double NaN; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/misc.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,7 @@ +#include "misc.h" + +const double PI2 = PI / 2; +const double PI4 = PI / 4; +const double TAU = PI * 2; +const double SQRT2 = 1.4142135623730951; +const double NaN = 0 / 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/serial.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,7 @@ +#ifndef SERIAL_H +#define SERIAL_H + +void serial_setup(void); +void serial_loop(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/serial.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,81 @@ +#include "creep.h" +#include "leg.h" +#include "servos.h" +#include "misc.h" + +void serial_setup() { + int16_t prev = 0x00; + int16_t next = 0x00; + + Serial.begin(19200); + + while (prev != 'x' || next != 'x') { + if (Serial.available()) { + prev = next; + next = Serial.read(); + } + delay(100); + } +} + +void serial_loop() { + while (Serial.available()) { + unsigned char command = Serial.read(); + switch (command) { + case 'w': // Up arrow. + creep_dy = min(1.5, creep_dy + 0.25); + break; + case 's': // Down arrow. + creep_dy = max(-1.5, creep_dy - 0.25); + break; + case 'a': // Left arrow. + creep_rotation = min(PI/90, creep_rotation + PI/360); + break; + case 'd': // Right arrow. + creep_rotation = max(-PI/90, creep_rotation - PI/360); + break; + case '[': // Rewind. + creep_spread = max(-10.0, creep_spread - 1); + break; + case '\'': // Play/pause. + creep_spread = 0.0; + break; + case ']': // Fast forward. + creep_spread = min(10.0, creep_spread + 1); + break; + case ',': // Channel minus. + creep_height = max(TIBIA * 0.25, creep_height - 1); + break; + case '.': // Channel plus. + creep_height = min(TIBIA * 1.25, creep_height + 1); + break; + case '/': + creep_height = TIBIA * 0.75; + break; + case 'z': // Volume down. + creep_dx = max(-1.0, creep_dx - 0.25); + break; + case 'm': // Mute. + beep_mute = !beep_mute; + break; + case 'c': // Volume up. + creep_dx = min(1.0, creep_dx + 0.25); + break; + case 'h': // Home. + for (unsigned char leg=0; leg < 4; ++leg) { + move_leg( + leg, + ((COXA + FEMUR) / SQRT2), + ((COXA + FEMUR) / SQRT2), + TIBIA + ); + } + break; + case 'x': // OK. + creep_dx = 0.0; + creep_dy = 0.0; + creep_rotation = 0.0; + break; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/servos.h Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,11 @@ +#ifndef SERVOS_H +#define SERVOS_H + +// Number of servos. +#define SERVOS 12 + +void servo_move(unsigned char servo, double rads); +void servo_setup(void); +void servo_shutdown(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dice/servos.ino Thu Feb 26 18:18:06 2015 +0100 @@ -0,0 +1,102 @@ +#include "misc.h" +#include "servos.h" +#include <Servo.h> + +// Range of the servos. +#define SERVO_FREQ_MIN 600 +#define SERVO_FREQ_MAX 2400 + +static const unsigned char SERVO_PINS[SERVOS] = { +// ankle, knee, hip + 6, 5, 4, // front left + 7, 8, 9, // hind left + 12, 11, 10, // hind right + 14, 15, 16 // front right +}; +static const char SERVO_REVERSE[SERVOS] = { +// ankle, knee, hip + -1, 1, 1, // front left + 1, -1, -1, // hind left + -1, 1, 1, // hind right + 1, -1, -1 // front right +}; +static const char SERVO_TRIM[SERVOS] = { +// ankle, knee, hip + 0, 0, 10, // front left + 0, 0, 10, // hind left + -10, -5, -10, // hind right + 10, -5, -10 // front right +}; +static Servo servos[SERVOS]; + +static const double HOME_POSITION[SERVOS] = { +// ankle, knee, hip + 0, 0, 0, // front left + 0, 0, 0, // hind left + 0, 0, 0, // hind right + 0, 0, 0 // front right +}; + +static const double FOLD_POSITION[SERVOS] = { +// ankle, knee, hip + PI2, PI2, -PI4, // front left + -PI2, -PI2, -PI4, // hind left + PI2, PI2, -PI4, // hind right + -PI2, -PI2, -PI4 // front right +}; + +void servo_move(unsigned char servo, double rads) { + // Move a servo to a position in radians between -PI/2 and PI/2. + if (servo >= SERVOS) { + return; + } + rads = rads * SERVO_REVERSE[servo] + PI2; + while (rads > TAU) { + rads -= TAU; + } + while (rads < 0) { + rads += TAU; + } + int freq = (SERVO_FREQ_MAX - + SERVO_FREQ_MIN) * rads / PI + SERVO_FREQ_MIN; + freq = min(SERVO_FREQ_MAX, + max(SERVO_FREQ_MIN, + freq + SERVO_TRIM[servo] * 10)); + servos[servo].writeMicroseconds(freq); +} + +void servo_setup() { + // Initialize all servos. + // Start with hips. + for (unsigned char servo = 2; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } + // Then ankles. + for (unsigned char servo = 0; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } + // Finally knees. + for (unsigned char servo = 1; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } +} + +void servo_shutdown() { + // Power down all servos. + for (unsigned char servo = 0; servo < SERVOS; ++servo) { + servo_move(servo, FOLD_POSITION[servo]); + } + delay(500); + for (unsigned char servo = 0; servo < SERVOS; ++servo) { + servos[servo].detach(); + } +}
