Ball launcher

My friends studying Industrial Design are currently doing a “smart product” project where they have to make an active 2-player game out of LEGO using the NXT and a launcher of some sort.

If I had to make such a game, I’d probably make a Twister robot that carefully calculates impossible positions and shoots at you while you’re struggling. Sounds fun, right?

But I don’t have to make such a game, so I can just have some fun making a launcher.

Theremin

I made another silly thing. I came across a musical instrument called a Theremin. The real deal uses a really cool concept where your body and 2 antennas act as a capacitor in an LC circuit. By changing the distance between your body and the antennas, the capacitance changes, which changes the resonance frequency and modifies the pitch and amplitude of the output signal.

This thing is much more silly. It uses the IR sensor to measure the distance to your hand and generates a tone based on that. It works nothing like the real thing.

The sound quality of the EV3 is terrible, because it’s just a small speaker attached to a PWM port with a low-pass filter.

The IR sensor is slow, inaccurate and discrete. So you can do none of the slides and vibrato you can do with a Theremin. At first I tried to smooth the input to get a more natural sound, but that made it even slower and impossible to tune. So in the end I mapped the discrete input steps to discrete notes, so that it at least sounds in tune. You still can’t play anything on it though.

I wrote the code for this in C on ev3dev. I use ev3c to talk to the sensors and libasound to generate the sound. This took a while to get working.

#include "ev3c.h"
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include "alsa/asoundlib.h"

static char *device = "default";                        /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[800];                          /* some random data */

int main(void)
{
  int err;
  unsigned int i;
  snd_pcm_t *handle;
  snd_pcm_sframes_t frames;
  if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
          printf("Playback open error: %s\n", snd_strerror(err));
          exit(EXIT_FAILURE);
  }
  if ((err = snd_pcm_set_params(handle,
                                SND_PCM_FORMAT_U8,
                                SND_PCM_ACCESS_RW_INTERLEAVED,
                                1,
                                8000,
                                1,
                                500000)) < 0) {   /* 0.5sec */
          printf("Playback open error: %s\n", snd_strerror(err));
          exit(EXIT_FAILURE);
  }

  const double interval = pow(2, 1.0/12.0);
  const double R=8000; // sample rate (samples per second)
  double F=440; // frequency of middle-C (hertz)
  double Fp = F;
  double V=127; // a volume constant
  double t; // doudle counter, yeaaaa


  //Loading all sensors
  ev3_sensor_ptr sensors = ev3_load_sensors();
  ev3_sensor_ptr prox1 = sensors;
  ev3_mode_sensor(prox1,0);
  ev3_open_sensor(prox1);
  while(1)
  {
    ev3_update_sensor_val(prox1);
    Fp = F;
    F=220*pow(interval, prox1->val_data[0].s32/4);
    t*=Fp/F; // scale time with frequency change
    // this is to maintain a continuous sine

    fprintf(stderr, "%d, %f\n", prox1->val_data[0].s32, F);
    
    for ( i=0; i<800; i++ ) {
      t+=1;
      buffer[i] = (sin(t*2*M_PI*F/R)+1)*V;
    }

    //printf("%d\n", snd_pcm_avail(handle));
    frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
    if (frames < 0)
            frames = snd_pcm_recover(handle, frames, 0);
    if (frames < 0) {
            printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
            break;
    }
    if (frames > 0 && frames < (long)sizeof(buffer))
            printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
  }
  //Let's delete the list in the very end. It will also close the
  //sensors
  ev3_delete_sensors(sensors);
  snd_pcm_close(handle);
  return 0;
}

Chandelier bot

I wanted to invite someone over to watch a movie, but realised that my energy saving light bulb isn’t the most cozy environment ever. The light they give of always feels a bit cold.

To remedy this I went to the store to buy some candles (and forgot to buy matches). But just plain candles seemed so boring, so I wanted to do something special with them.

I remembered from years back this Tai Chi practice where you put glasses of water or candles on your hands and rotate them over and under your shoulder. It creates this beautiful spiral movement. So I thought it would be nice to make a robot that moves my candles around.

The robot is done. Now to wait for the movie.

Improved Mars Rover

I noticed one particular gear causing a lot of slipping, the large one driving the bogie. It misbehaved in two ways:

The large gear was pushed sideways by the force of the motor, pushing apart the frame and causing the top gear to slip.

The large gear exerted force on the bogie, pushing one wheel off the ground under force.

To solve these issues, I used a smaller gear and reinforced the frame. This helps keep all the gears together and all the wheels on the ground. The robot can now climb much bigger obstacles.

IMG_20150326_153433

Excavator

Today I spent some time building an excavator. I had to choose between controlling all parts of the arm and letting some parts move together mechanically.

I chose to do the former, which means I don’t have any motors left for the chassis, but can freely move all parts of the arm.

Maybe another NXT or RCX kit could serve as the base, which also needs 3 motors: 2 to drive and 1 for the rotating platform.

All NXT building instructions now free

I thought long and hard about this, as a lot of effort went into making these building instructions, but I concluded that I feel better about sharing them with as many people as possible than sharing them with a handful and making a few bucks.

So I’m happy to announce that all the instructions I made so far will be available for free. Go check them out.

I haven’t been writing a whole lot in the past months, but I feel some things breeding and I’m also super excited about the new EV3 coming out this year.

Mindsensors Glidewheel

I just got two Glidewheels from Mindsensors for testing. The Glidewheel allows you to integrate other motors into your NXT models, providing precise control over even the oldest 9V motors.

The Glidewheel is designed for Power Functions motors, but works just as well for my RCX motors.

The hard part of using them with RCX LEGO is that they don’t fit directly or close to the RCX motors. You’ll have to mount them elsewhere and stick an axle through them. This is further complicated by the stud-based RCX LEGO.

The first thing I made with them is this small car that uses a drive and steer motor. Even with the rotation sensor that you could buy for the RCX, it was incredibly hard to steer a robot like this.

With the Glidewheel it is incredibly easy. Well, almost. My first attempt looked much like my past attempts with the RCX.

The RCX motors are a lot faster than the NXT motors, so what happens is that the PID controller in the NXT starts overreacting.

To stop this, I used an algorithm called gradual descent(or twiddle, as prof Thrun calls it), which basically modifies P, I or D a little, and sees if it gets better or worse.

float pid[];
float delta[];
int err;
int newerr;
mutex running;

task record() {
	newerr = 0;
	int tacho;
	Acquire(running);
	for(int i=0; i<200; i++) {
		tacho = MotorRotationCount(OUT_A);
		newerr += abs(tacho - 365);
		Wait(10);
	}
	Release(running);
}

inline void run() {
	ResetRotationCount(OUT_A);
	start record;
	RotateMotorPID(OUT_A, 100, 365, pid[0], pid[1], pid[2]);
	Acquire(running);
	printf("%d", newerr)
	Release(running);
}


task main() {
	ArrayInit(pid, 32, 3);
	ArrayInit(delta, 5, 3);
	err = INT_MAX;
	run();
	while(ArraySum(delta, NA, NA) > 0.1) {
		NumOut(0, LCD_LINE2, pid[0]);
		NumOut(0, LCD_LINE3, pid[1]);
		NumOut(0, LCD_LINE4, pid[2]);
		for(int i=0; i<3; i++) {
			pid[i] += delta[i];
			run();
			if(newerr < err) {
				err = newerr;
				delta[i] *= 1.1;
			} else {
				pid[i] -= 2*delta[i];
				run();
				if(newerr < err) {
					err = newerr;
					delta[i] *= 1.1;
				} else {
					pid[i] += delta[i];
					delta *= 0.9;
				}
			}
		}
	}
	PlayTone(432,1000);
	Wait(10000);
}

The result of this code for my little car was

P I D
Default 96 32 32
Free 40 40 32
Load 40 32 40

Inserting these values in my code, I get this smooth motion.

Code:

mutex inControl;

task avoid() {
	while(true) {
		while(SensorUS(IN_4)>30);
		Acquire(inControl);
		PosRegSetAngle(OUT_C, 90);
		OnRevRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
		Wait(2000);
		OnFwdRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
		Release(inControl);
	}
}

task turn() {
        while(true) {
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, 90);
		Release(inControl);
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, -90);
		Release(inControl);
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, 0);
		Release(inControl);
        }       

}

task main() {
	SetSensorLowspeed(IN_4);
        OnFwdRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
        PosRegEnable(OUT_C, 40, 40, 32);
	Precedes(turn, avoid);
}

Face Tracking Laptop Crawler

First post from the US!

I can’t really do nice photography, video and building instructions here, but I did make a robot.

This thing sits under your laptop, and can drive it around and tilt the screen. The idea is that it will always face you, and keep a fixed distance too.

It uses the webcam of my laptop to track my face using OpenCV from Python.

It could also keep a fixed distance using the ultrasonic sensor, but this is not implemented.