Lab 8: Stunts

April 9, 2024 | 754 words

In Lab 8, I used distance measurements and orientation control to have my car perform a drift in the form of a 180 degree turn after running at a wall.

Orientation Control

I chose an orientation-based stunt, mostly because I think the mechanism involved is more interesting, but also because I know orientation control will continue to matter significantly in upcoming labs, so this was a good chance to improve upon my implementation from Lab 6.

Straight and Narrow

To prepare for a drift maneuver, I first wanted to combine linear movement with orientation control, thereby allowing my car to drive in a straight line while approaching a wall. This only required a minor addition to my orientation PID controller: a base speed.

void pidOriControl() {
    ...

    // The total magnitude of motor input should exceed the threshold
    if (abs(ori_base_speed) + abs(input_speed) < ORIENT_THRESHOLD) {
        idleMotors();
        return;
    }

    // Drive the motors relative to the base speed
    driveMotors(ori_base_speed + input_speed, ori_base_speed - input_speed);
}

The base speed generates the car's linear movement, while the PID controller's input speed acts as a correction term. This way, setting a constant angular heading and driving the car forward should propel it perfectly straight, or at least more so than I was able to achieve with motor calibration alone in Lab 4.

A Wrinkle In Time

While testing the straight-line mechanism and trying to prepare for a 180 degree turn by measuring the distance ahead of the car, I ran into a significant problem with my IMU.

While working on Lab 6, I had starting using the DMP feature of the IMU breakout board. This had worked really well for orientation control only two weeks earlier. However, now that I had a base speed and multiple sensors going at the same time, the DMP starting acting up in a particularly nasty way. Its readings would slow down and lag behind physical reality over the course of thirty seconds or so, before finally giving out entirely, sending my orientation PID controller into a tailspin.

I tried to fix the issue for several days but made no real progress in identifying the root cause. At first, it seemed hardware might be to blame. I tried re-soldering several of my connections. I tried replacing wires, Qwiic connections, and battery cables. I even tried replacing the IMU and testing with a different Artemis. What was most strange is that nothing ever seemed wrong when running the IMU in isolation or with one or two other systems active. It was only when all cylinders were firing at once, so to speak, that the DMP would go absolutely bananas.

At some point, while reading through documentation and examples, I noticed that the DMP materials make a few references to functions with "FIFO" in their name. I had seen this some weeks earlier but not given it much thought. It suddenly hit me that "FIFO" here meant "first in, first out", the directionality moniker for queues.

The problem that had, at this point, plagued me for nearly a week became clear in an instant. Somehow, the DMP must be storing its readings in an internal queue on the chip, waiting to be read off over I2C. The trouble with that is, because the DMP measures orientation so quickly and I only ever read one value at a time after performing a readiness check, I had been inadvertently letting the chip's buffer fill up. This is what caused both the lagging behavior and the crashes I had been observing. And the reason this clogging of the DMP happened just when all my sensors and systems were running is because only the sum total of all those processes slowed the main control loop down enough to dip below the sampling rate of the DMP.

The fix for all this wasn't anything to do with my hardware, or my PID controller, or the BLE connection, or any of the several other things I tried; it was simply to slow the DMP down so that my Artemis could catch up with it. Once I did that, orientation control was back in business.

// Configuring DMP to output data at multiple ODRs: DMP is capable
// of outputting multiple sensor data at different rates to FIFO.
// Setting value can be calculated as follows:
// Value = (DMP running rate / ODR ) - 1
IMU.setDMPODRrate(DMP_ODR_Reg_Quat6, 1); // Set to half speed

Drifting Away

With a now working orientation controller, I added a DRIFT Python command and proceeded to run my car across my kitchen in spectacular Hollywood fashion. The results of these stunts are shown as videos and plots below.

Drift trial 1 data

Drift trial 2 data

Drift trial 3 data

Conclusion

This was supposed to be a quick and fun lab to show off some of my car's new capabilities. It turned into a multi-hour debugging ordeal that all stemmed from my stubborn insistence on using the DMP. That said, I think it was worth it. The DMP is so much better than integrating the raw gyroscope readings that it was good to improve my understanding of it, albeit painfully.