Everyone is now obsessed with drones, but I've never been too into them. They are noisy I guess. In any case, I like RC cars more, so I figured I would give a go to creating one from scratch!

Internals

I need WiFi and a camera for the FPV, so the ESP32 was the cheap and well documented option. I thought it wolud be overpowered for this, as I'm used to fiddling with Attinys and ATmegas, but I was very wrong!

The gist is that I'm using an ESP32s3 dev board, motor drivers and a cheap OV cam module.

Grab the code here if you are interested. I actually started with Platformio/Arduino and ended up moving to ESP-IDF to get full control of my task cores, after messing a bit with binary patching to force that in the precompiled arduino libs, but to be honest the arduino code was fine and I could have stayed there.

Light

A minor point, light. As simple as it gets, an LED toggled on/off by a transistor.

I went through various logic level FET's, but I had issues fully turning the gate on/off. In retrospect I should have reached for a BJT and saved myself a lot of trouble. In any case, I ended up lifting the pad on the umpteenth rework of the PCB and got rid of the LED.

Front wheels

Initially I wanted to use Ackerman steering because it's so cool, but I ended up doing skid steering because it's much simpler mechanically (2 wheels, two DC motors, that's it), and I was a bit space-constrained.

Skid steer also comes with a nice bonus. I can rotate in place, which means I can drive in small spaces like my desk much more easily!

As soon as I started testing the skid steering though I saw compensation issues. Both DC motors are not the same, both gearboxes are not the same (greasing alone gives you a 20% boost in RPMs, so even the greasing needs to be the same!), etc. I solved this in software, implementing a compensation variable to reduce power to one of the motors.

Traction

Grip is hard. I can't 3D print soft rubber (95 shore is too hard and doesn't grip at all), so I went with rubber bands! They work great!

The back wheels are a whole different world though. They need to skid.

Rear "wheels"

This was much harden than the front one, surprisingly. I had two big issues that made the car fully unable to steer.

Traction (or absence of)

There are three options for the wheels: You can make them slippery (felt), you can make them grippy (rubber), or you can do something in between (bare plastic).

wheel_comparison.webp
Figure 1: Slippery back train (just a felt pad on the bottom), naked plastic tires, and grippy (rubber band) rear wheels.

Initially I went with rubber, but that meant the car could not steer. This was great to nudge it towards not leaning towards any side and make it go straight, but the turning radius was huge.

Bare wheels work much better, but it's a waste compared to not having them at all. I ended up embracing the skid and just adding felt (not great for carpet driving but works perfectly on hard floors).

Stability

The front wheels are essential because it's not an all wheel drive, and that means that all the wheels need to touch the floor. Or at least the front ones!

Normal cars have suspension, and that means that the weight gets distributed equally. I played a bit with some basic "suspension" systems (give a bit of play to the axle), and that worked for keeping the all the wheels in contact with the floor.

Nevertheless, I found easier to just embrace the tripod concept and have a single point of contact on the rear.

felt_composite.webp
Figure 2: EMBRACE THE TRIPOD

The lean forward helps add more weight into the front wheels to increase their grip.

FPV

This was, together with the actual mechanical trouble with the wheels, the trickiest part. The ESP32s3 is definitely struggling to pull frames from the cam and do something else at the same time (in this case stream them via TCP).

I started using Arduino + Platformio, and the performance was terrible. I was quick to blame the core the core of the camera being shared with the WiFi, but after I migrated the whole proyect to the ESP IDF to have full control on core assignments, I discovered that the camera broke the WiFi even when in a totally different core.

The interesting part is that at low XCLK's for the camera, everything works perfectly and the stream is smooth (even if the frames take a bit long to actually capture). And abruptly, when bumping over 8MHz, it starts malfunctioning. This points to the issue being interference of some sorts, not simple interrupts competing for resources or task starvation.

And the smoking gun: if you put the finger on the WiFi PCB antenna, the stream gets smoother!

Eventually, it would be cool to get a spectrum analyzer and delve deeper into the exact strength of these 2.4GHz interferences. It makes no sense that a 10MHz camera can cause issues 2 orders of magnitude above its frequency band.

Optimizing quality

We have two knobs here: how big do we want the image to be (VGA, Full HD, QQVGA…) and the quality of the jpeg encoding. The quality is inverted for this specific camera; 0 is the best quality and 100 is the worst.

quality_qvga_jpeg.png
Figure 3: Quality, 0 is better. Below q = 20 we get decent results
timings.png
Figure 4: Frame sizes and capture times. It's not worth it to go below q = 10 or higher than 20. 15 is a sweet spot with almost no penalty. The capture time is lagely independent on the quality, dominated by actually getting the frame. Bumping the image size is not too expensive.

I ended up going with VGA @ q=12. I can lower the quality as needed via a menu in the controller but usually you don't notice the difference in frame rate, and the image gets much worse.

--EOF