NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » Nerdkit learns to drive

June 24, 2012
by esoderberg
esoderberg's Avatar

Link below has video of couple test runs of my drive by wire 3 wheeler. Consists of Nerdkit mcu reading from an mpu-6050 gyro/accel combo and a hall switch pickup for velocity measurement. Driver input comes from a joystick. I started to design my own H-bridge, but I kept burning them out, so in order to get on the road, for now I'm using a Pololu motor controller to take my steering inputs from nerdkit to drive a linear actuator for steering control. I could go much faster but I'm still at the point where I go no faster than I'm comfortable crashing at(made lots of mistakes along the way so far, so more are expected). I'm still tuning the handling qualities but it's pretty tame so far. Most of my runs are with driver input controlling tilt rate, but also a few with driver input taken as desired tilt angle. Probably higher performance with former but easier to drive with latter.

http://youtu.be/q8inBsAz1ng

June 25, 2012
by Ralphxyz
Ralphxyz's Avatar

Oh wow, I want one :-)

Ralph

June 26, 2012
by HansRoaming
HansRoaming's Avatar

Very impressive!

June 27, 2012
by esoderberg
esoderberg's Avatar

If anyone is familiar with the MPU-6050 gyro/accel and knows how to init the DMP portion, it would be great to see this in the forums. The data sheets from Invensense are really weak on this topic; there's some info on the web for using the DMP function, but from what I've found it's almost all in Arduino wiring code which I'm not familiar with (although it looks not too far from C code). It would let me simplify my code for the three wheeler and off load some of the processing being done on the nerdkit MCU if I could crack the code on this feature.

Eric

July 08, 2012
by esoderberg
esoderberg's Avatar

I hooked up a wireless link (via 2 XBee Pro, which worked great with minimal setup/learning curve) to record some of the data for the three wheeler. It should make refining the handling a little more rigorous than making adjustments based just on feel (although thanks to USNTPS I've made it work so far with this method). I'd like to be able to push more parameters on each run but as it is, I've gone from running at 400 Hz with no data link to about 200 Hz when sending 3 parameters, so for now I'm limiting what I send. Attached pic shows data for a series of S turns - green is tilt angle, blue is tilt rate, and red is steering input from joystick. My first impression is that the tilt rate signal is noisy as hell; I guess I shouldn't be surprised as the little 150cc engine gives the bike a pretty good rattle. I'm going to be doing a little more filtering to clean it up, but every time I adjust the filter I need to rework my PID parameters. At least now I'll be able to better see just what each adjustment is doing.

Eric

datarun

July 17, 2012
by esoderberg
esoderberg's Avatar

I'm doing a little clean up on my 3 wheel code and have been stumped: The function as written below works just fine - it reads multiple high and low bytes from a gyro FIFO buffer (in two's complement form) and returns a signed 16 bit value with the average of data from the FIFO buffer. Here's the part that I'm stuck on: if the MSB of the high byte is 1 than the converted 2's complement data should be negative and requires conversion to standard form; by my understanding this is equivalent to saying the shifted high and low bytes are > 32767. Yet when I use "if (((fifo_buffer_in[i2]<<8)+fifo_buffer_in[1+(i2)])>32767)" I get good data, but when I use the "if (fifo_buffer_in[i*2]>>7)", ie the statement that is commented out instead, dtilt_long always returns as a negative (around -65,000 when at rest). I could be content that it works as coded, but as I'm relying on this to keep myself from crashing I'd like to actually understand why what looks to be a simpler yet equivalent "if" statement is resulting in different results. Any thoughts welcome.

Eric

    int16_t dtiltfifo_read(int16_t dtiltinit){

    uint8_t fifo_count_hi, fifo_count_low, fifo_buffer_in[36];
    int32_t dtilt_long=0;

            //read gyro fifo count  
            TWI_buffer_out[0] = 0x72;//x axis-tilt stored in FIFO
            TWI_master_start_write_then_read(MPU6000gyro_ADR, 1,2);
            while(TWI_busy);
            fifo_count_hi= TWI_buffer_in[0];
            fifo_count_low= TWI_buffer_in[1];

            //Read data
            TWI_buffer_out[0] = 0x74;           
            for(i=0;i<((fifo_count_hi<<8) + fifo_count_low); i++){
            TWI_master_start_write_then_read(MPU6000gyro_ADR, 1, 1);
            while(TWI_busy);
            fifo_buffer_in[i]=TWI_buffer_in[0];}

            //Put results from gyro into program variables  data comes in "two's complement" form  if MSB is 1, data is negative
            //x axis (tilt)  average all of the x gyro readings from the fifo buffer
            for(i=0;i<(fifo_count_low/2); i++){

            //if (fifo_buffer_in[i*2]>>7);
                    //fifo_buffer_in [i*2] stores sequential high bytes, [1 + (2*i)] stores sequential low bytes --- normal cycle time typically gives around 8 bytes so only fifo_count_low required to count to bytes in fifo.

            if (((fifo_buffer_in[i*2]<<8)+fifo_buffer_in[1+(i*2)])>32767) dtilt_long+=((fifo_buffer_in[i*2]<<8)+fifo_buffer_in[1+(i*2)])-65536-dtiltinit;
            else dtilt_long+=((fifo_buffer_in[i*2]<<8)+fifo_buffer_in[1+(i*2)])-dtiltinit;
                    }

            //convert output to mradians per second = .133 *(2/fifo_count_low) to take average of multiple readings from FIFO above
            return(-(dtilt_long/(3.7*fifo_count_low)));}
July 17, 2012
by esoderberg
esoderberg's Avatar

One last item to note for anyone looking at the question above: when I just read the gyro register directly for one pair of bytes, without using the FIFO buffer, just looking at the MSB of the high byte in the "if" statement seems to work just fine using the code below.

int16_t dtiltreg_read(int16_t dtiltinit){

    int16_t dtilt;

            TWI_buffer_out[0] = 0x43;//gyro_x
            TWI_master_start_write_then_read(MPU6000gyro_ADR, 1,2);
            while(TWI_busy);

            //Put results from gyro into program variables  data comes in "two's complement" form  if MSB is 1, data is negative
                            if (TWI_buffer_in[0]>>7) dtilt = (TWI_buffer_in[0]<<8) + TWI_buffer_in[1]-dtiltinit -65536;
            else dtilt = (TWI_buffer_in[0]<<8) + TWI_buffer_in[1]-dtiltinit;

            dtilt*=-.133;   //output in mradians per second
            return (dtilt);}
July 17, 2012
by pcbolt
pcbolt's Avatar

Eric -

That is strange. I'd be curious to see if...

if (fifo_buffer_in[i*2] >= 0x80)

would work. BTW, hope you didn't leave that semicolon there in line 24 above. The one thing that puzzles me is that you're returning a signed integer from the function, yet you're using a float number in the return statement calculation. That would worry me if I was handing over controls to the obedient little MCU brain.

July 17, 2012
by esoderberg
esoderberg's Avatar

PCBolt,

Tried your suggestion. That does not work either, gives same result as

if (TWI_buffer_in[0]>>7)

As for returning a signed integer from a float, I have plenty of precision rounding to the nearest integer (the noise level is well above the lsb, which is down to milliradians/per second). Are there potential "bad" things that could happen, or does assigning a float to an integer reliably round down to the next integer? I could avoid it easy enough if there is a reason beyond the level of precision.

Eric

July 17, 2012
by esoderberg
esoderberg's Avatar

Drat!! Meant to say your suggestion gives same result as

if (fifo_buffer_in[i*2]>>7)
July 17, 2012
by pcbolt
pcbolt's Avatar

Eric -

I tested the float variables and you're right...it will simply round the result down with correct precision. I also tested the "if" statements and both worked for me. There was one problem though and it would make sense as to what you are seeing. When I computed the answer to this:

result = (test[0]<<8) + test[1];

I got a negative number. Since "result" was declared an "int16_t", it did all the sign conversions correctly. In other words I did not need to add the "-65536" term. If this is the way your program is working, maybe the

if (fifo_buffer_in[i*2]>>7)

is correct after all and since it is subtracting 65536 for each summing step, the average would be close to -65536. Still brings up the question as to why this works...

if(fifo_buffer_in[i*2]<<8)+fifo_buffer_in[1+(i*2)])>32767)...

Two things might be happening. First, if the MCU doesn't know to extend the shift from an 8-bit variable to a 16-bit, the "fifo_buffer_in[i*2]<<8" would just get shifted away to 0. Second, if it did know to extend to 16-bits, but used signed integer logic, the test would never pass since it would be either less than 0 or less than 32767. If it jumps to the "else" statement..it never subtracts 65536.

Just a theory.

July 17, 2012
by pcbolt
pcbolt's Avatar

Eric -

Almost forgot...if the MCU is computing the sign correctly, you would not need any "if" statements at all, you just need:

dtilt_long+=((fifo_buffer_in[i*2]<<8)+fifo_buffer_in[1+(i*2)])-dtiltinit;

If the compiler warns about "signedness" you could try:

dtilt_long+=((((int16_t)fifo_buffer_in[i*2])<<8)+((int8_t)fifo_buffer_in[1+(i*2)]))-dtiltinit;

Good luck.

July 18, 2012
by esoderberg
esoderberg's Avatar

PCBolt,

Holy Cow! Your suggestion works, no if statements are necessary. It's amazing to me that my understanding of what was going on was so wrong, yet still managed to have it work. I definitely have a weakness when it comes to variable types; seems a majority of the time that I really get stuck on a piece of code, it's the consequence of variable types that I fail to fully account for in my logic. Thanks for helping me out.

Eric

July 18, 2012
by pcbolt
pcbolt's Avatar

Eric -

Glad to help. I still don't have a complete grasp of the subject. I just know that I don't know and code defensively. Looking forward to seeing a video of the finished project.

Post a Reply

Please log in to post a reply.

Did you know that a piezoelectric buzzer can be used to play music with your microcontroller? Learn more...