Data logging and sensor filtering

Made a big code haul today to get data logging working on the device. I had it a while back, but it was outputting human readable data which was not efficient enough to output the data I needed at fast enough rates. Adding more data to the logging would prevent the steppers from getting fast enough signals which would cause them to sporadically skip steps. Soo I turned it off and haven’t gotten back to until now…

But alas, here we are. Data logging is all of a sudden a big priority. For one, it will help make the beta testing debug process go a lot smoother. But it will also help me tackle another thing that I’ve been meaning to implement for a while: Kalman filtering. I’ve reached a sort of bottleneck with the accuracy and reliability I am able to get with the current sensor setup. I am able to get within 0.5mm or so on small cuts, but when I go to bigger designs this widens significantly and gets more unreliable with sensor drift. Also, 0.5mm isn’t quite acceptable - I’d like to do better. My hope is that with a well tuned Kalman filtering model for the sensors, I should be able to reduce drift in the system and tighten up the machine’s accuracy. Also, this opens the door up for more complex sensor fusion implementation - such as adding a 9DoF IMU - to further improve accuracy and reliability. Since all of my rigid body position estimates start with an angular velocity calculation, which needs to be integrated to get orientation, a 9DoF IMU should in theory be able to give an additional absolute measurement to make this foundation a lot more secure. It would be nice not to have to add any more sensors, but if they can significantly increase accuracy and decrease drift, then it would make sense.

Anywho, data logging will help a lot with this since I’ll be able to easily compare what I’ve drawn/cut against what the device thinks it has drawn/cut. I can then adjust various parameters and tune the system to make these two things the same. I’ve never worked with Kalman filtering before, so this should be interesting. I’ve watched a couple YouTube videos, though, so I’m basically an expert now :smile:… it’s pretty fun learning about this stuff.

To get data logging working better, I’ve switched to binary outputs and taken the time to explicitly structure all of the data cleanly. This is how the structure looks:

Header

char firmware-version
char design-filename

Design info

int pathInfo[0] - feature type, numPoints (?), etc.
int[3] path[0].point[0] - points contain x, y, z
int[3] path[0].point[1]

int pathInfo[1]
int[3] path[1].point[0]

int[3] path[num_paths].point[num_points]

Raw sensor data

This will be sent every sensor time step
sensorPacket[4] - for each sensor
long time - micros() from start of design
int dx
int dy
byte sq - surface quality

Additional data at constant sampling rate

This will be sent at a constant sampling rate
auxPacket:
long time - micros() from start of design
pose
float x
float y
float yaw
int current_path_idx
int current_point_idx
goal
float x
float y
float toolPos
float desPos
state
int cutState

Just finished implementing all of this into the firmware. Next step is to make the decoder and a good way to visualize the data. Will update with more progress…

4 Likes

You can spend a lot of time in a kalman filter. Where it would really help is combining the encoders with the angular rate from the 9dof IMU. The encoders will already be better than anything from the accelerometer. A mentor told me once when I was trying to make a kalman filter work with wheel encoders, “A $10 encoder is better than a $5,000 accelerometer”.

As for data logging, I have used a few tricks in the past. For one thing, putting different data in different classes is a good practice. You can separate things into different rates. You can also parse some of the messages if you have the wrong version. I also like using key, length, value strings to organize the data. The version message might start with 0xBEEF. The plan data might start with 0xCAFE, etc… Then if you lose some bytes, you can regain your place and if you have the wrong version data parser, you can still read some of those messages.

An extra mile would be making a dictionary of error messages. Error message 0x01 might be “invalid file name”. Then the microcontroller code would only send 0x01 in a list of possible error messages. The debugger and the code then would need to be very synced so the debugger can print a human readable message. I wouldn’t do that for a small project though. It can be pretty fragile.

1 Like

Are you referring to the optical flow sensors as the encoders? I totally agree. It would be awesome to be able to use the absolute orientation measurement from the 9dof IMU as well, but I’m not sure how accurate that is. Might order one and start playing around with it.

Dude great stuff! I am currently using these as my starting, terminating, and header bytes:

#define PACKET_START		0xAA
#define PACKET_END			0x55
// Constants for packet types
#define PACKET_SENSORS		0x01
#define PACKET_AUX			0x02
#define PACKET_HEADER		0xA0
#define PACKET_DESIGN_INFO	0xA1
#define PACKET_PATH			0xA2
#define PACKET_PATH_POINT	0xA3

The sensor and aux messages have the start and end packets, but initial messages don’t. I just got these from my helpful LLM friend, though, so I don’t really know if I’m doing something stupid here. Is there a good reason to making these strings 2 bytes instead of just one?

1 Like

Yes.

That’s a magnetometer, so probably not terribly accurate. If the IMU is doing its own kalman filtering, it might be useful sometimes. But they are really noisy. Not good enough for engraving. Unless they have gotten a lot better since I used them.

No. Those look fine. I code in C++ and strings are what I think of when I think of bytes. If you have an analog to digital conversion, at some point, you will get an 0xAA. But with the second byte for key and the fact that you’ll probably mostly be on track, I wouldn’t worry about it.

Hmm ok gotcha. I’ll keep that in mind. I saw this one on my brief search which seems to have some sort of filtering built in:

Pretty neato but also not especially cheap.

1 Like