Individual Project Blog

An exploration of ducted fan thrusters to maneuver mobile robots

Note to examiners: Make sure to click the “⇠ Older” button at the bottom of this page to see posts going back to the beginning of this module


The last two weeks have once again been a whirlwind of testing, recording results and analysing all that data, and of course writing it all up into my final report and slides!

The testing went reasonably well, some very good bits where the robot was on its best behaviour and some parts so frustrating, it felt like the universe itself was taunting me. 40 test runs were completed, recorded and analysed, 20 runs for each of the two Kalman Filters I was testing. As it turns out, the speaker which I added a few weeks back for health and safety (and the convenience of knowing Bluetooth was connecting properly) gave me an additional time-saving surprise this week: It turns out that the tones it plays (alongside the loud whirr of the thrusters spinning up) are very visible when looking at an audio waveform. This turned what I thought would be a fairly arduous task analysing the time each run took, into a fairly quick and easy glance at the audio track of each run's video in some open source audio editing software (I use Tenacity, a good fork of the (in)famous Audacity). I didn't even have to extract the audio tracks first, opening the video files just worked! Check it out:

cb8b9170-e9ea-4e8b-97f9-92f40933c2c4

The part I've highlighted in the red box at the bottom of the image shows the length of the selected audio down to the millisecond (though I opted to record this to the nearest half second to account for differences in the recorded audio and exactly where I was clicking on each track). Recording oscillations still meant watching the video all the way through though, and there's a chunk of my report explaining the methodology of how I've counted what counts as an oscillation that I won't repeat here.

The same goes for the analysis methodology and results, this blog is designed to be a record of what I've been working on rather than a detailed explanation of the work itself, again, it's all in the report. That said, I did learn about a rather neat piece of statistical analysis that I will share the results for here, because the graphs are quite nice.

98824d0e-01df-4ea0-a746-63208a1422f1

779b528b-e022-4001-b456-bb1effd2ba88

These are ECDF graphs, and they're very useful because they show what percentage of runs met what quality very intuitively. If you want to find out how fast the best 90% of runs finished, you can just trace a line on the time graph from 0.9 up to the curve and then trace it down to the value and you get ~85 seconds for the LKF and ~105 seconds for the EKF, same for the best 50% or n% and it works with any continuous metric. Since osciallations are discrete and always positive, you can use something called a Common Language Effect Score (CLES) which is less visually impressive but resulted in a useful sounding statistic that ~60% of LKF runs performed better in terms of number of oscillations than EKF runs. Obviously that's not the be-all and end-all but it is effective at getting across a message nonetheless.

Anyway that's it! Everything is submitted and in half an hour's time, the deadline that the last 12 weeks have been leading up to will have passed. I'm actually quite proud of everything I've accomplished in that time, and all I can do now is wait and hope my final grade reflects that. In the mean time, I'll be treating myself to a long nap!

Another absolute whirlwind of a week has gone by, with significant progress on getting the robot to reach its goal.

I've fully implemented a linear Kalman filter – this is the localisation algorithm that performs sensor fusion on the laser distance sensor and the IMU and estimates where the robot is along the track and its current velocity. This is only one half of the problem to solve though, just because the robot now knows where it is, that doesn't mean it knows how to move somewhere else. That's where the control algorithm comes in – I began experimenting with a cascaded P-Controller, one of the most simple control algorithms out there. This would allow me to at least verify that the Kalman filter could be used as an input to a control algorithm successfully.

First though, I had to make sure to properly calibrate the laser distance sensor. Since “forward” in the the robot's coordinate system is towards a laser distance reading of zero, that doesn't match up with the positive value of the IMU meaning forwards acceleration, or the positive values used for forwards thrust. To account for this, I called the end of the track with the laser reflector (which is just just a white 3d-printed panel mounted to the end of the track) a somewhat arbitrary positive value of 40,000 (4m in the laser's 1/10th of a mm readings) and subtract the reported laser value from that, always resulting in a positive value that gets larger the closer to the reflector the robot. I also added a static offset to the measurement to account for the distance between the front of the laser and the alignment arrow in the middle of the robot. I calculated this by averaging about 350 laser readings with the alignment arrow positioned exactly 1m from the reflector.

1d121b53-32c6-416d-8c1e-c0b58ee3ae98 Aligning the robot's alignment arrow with the 1m track marker

The cascaded P-Controller loop definitely made the robot move, but the effect was a little on the intense side, so much so, the robot actually damaged itself by slamming into the end of the track and I had to re-print and replace the base the fans sit on, which cost me valuable time. I let that be a lesson to myself – there's no such thing as too much health and safety, and I'm just glad it was an easily replaceable part and not any of the expensive electronics which were damaged.

That's not to say the algorithm I used was bad though, after I had debugged it and placed very strict limits on the raw PWM output that controls the fans to avoid any more damage, the robot definitely looked like it was trying to approach its target, but it was oscillating past it over and over, and no amount of tuning seemed to be fixing it. I could have expanded this to a full PID style control algorithm, but that would have been a bit of a distraction. Remember, the purpose of this system is to sort of emulate how a satellite moves in orbit with reaction control system thrusters that fire in short bursts, that kind of movement can't really be emulated with a classic PID style control algorithm, instead I needed to use a the kind of algorithm actually used on those satellites. This algorithm is called Bang-Bang with hysteresis, and treats the fans being on or off as a binary state, rather than a continuous range of power. The ESCs still expect the PWM signals to be smooth though or they will shut off the fans for safety, so this requires a separate control loop running at its own intervals, independent of the main control loop, to control the smooth ramp up and down of the fans.

This definitely looks like it works better (and closer to how I imagined), but it still requires a little tuning before it's completely ready. The robot does approach it's goal but will still often overshoot slightly and then gets stuck trying to correct as static friction inhibits it's ability to move the short distance back to the goal. This is something I believe I can code around early next week.

Let's Talk Telemetry

All this work on the control algorithms required extensive debugging, which obviously needs data from the robot. To aid myself with this process, I added a new characteristic to the bluetooth stack, to enable streaming of telemetry data back from the robot to the control script in real time. Currently the robot reports the following values which are logged by the control script to a CSV: – Laser distance reading – IMU reading – Front thrust value – Rear thrust value – The algorithm's estimated position – The algorithm's estimated velocity

These have been really helpful for debugging and tuning and should hopefully allow me to make some nice graphs for the final report once everything is working as intended!

Other Stuff

As for other features I've implemented this week, I've added an error tone to the speaker class which plays when the robot encounters an error, or in situations like when the controller disconnects unexpectedly. Speaking of, that exact scenario now also results in an emergency stop triggering to stop the robot going out of control. I've also made it dump error traces to a file on the board itself so in case of errors cascading and not being able to report over bluetooth, I should still have a file I can pull off the board to help debug whatever just happened.

I might add a couple of videos to this post in an update or maybe they'll come next week, we'll see.

Gantt Chart Check In

Thankfully there's been an extension to the final report deadline, that's good news because who knows how I was going to get this done before!

fd5f72f1-0166-4407-969e-d14425bee7b7

This week started with the arrival of the linear bearing block which finally meant I could get my robot completely assembled for the final time and fixed to the track which will be its home from now on. While it did take a couple of hours of de-greasing the rail (thanks again Alex for your help with that!), the robot is now fully integrated and can slide along very smoothly with much less friction than a gunky greased track would provide.

It's always immediately following a major milestone though that you tend to notice new problems. During the limited testing I've been able to do so far, I've been using the Pimoroni board's integrated button to start the program running. Now everything's fully integrated though, the button on the board is now rather inaccessible – and no, poking it with an Alan key as I had begun doing was never going to be a viable long-term solution. Doing so brought to mind a comparison with nuclear scientists testing the so called “demon core” with a screwdriver, and while my robot probably wasn't going to kill me, my actions could certainly result in damage and/or minor injury – those thrusters are no laughing matter.

62b3f17b-4696-4274-9974-b838afc7f073 A recreation of Louis Slotlin testing the Demon Core moments before disaster

Health and Safety Gone Sensible

While it technically would have been possible to design and print some kind of mechanism to enable pressing the button on the board or just wired a new switch to it that was more accessible, the more desirable solution would be to not have to touch the robot at all. Luckily the Pi Zero W has both wifi and Bluetooth capability built in. Since wifi would rely on more external components like a router or access point, and would involve hard-coding an SSID and password (bad practice security-wise, even if it technically doesn't matter here, and also makes the robot less portable) I instead decided to look into the Bluetooth side of things.

I've never developed anything with Bluetooth before and I wanted to learn about it as well as feeling it was probably the most appropriate solution for the situation. I began looking into the docs for Bluetooth from both Pimoroni and the raspberry pi foundation themselves and quickly found references to the aioble library (that stands for All In One Bluetooth Low Energy). Documentation for this library is... okay I guess, there are a few official examples, but in my opinion, they don't make good practices or patterns for more complex applications super clear, and while it was relatively easy to program a way to send a single packet of data from my laptop to the robot to start the program, replacing my start button, once I tried to get telemetry streamed from the robot back to my laptop I began running into problems and cryptic error messages (Seriously guys, how am I supposed to know that [errorno 22] EINVAL means “You tried to publish a notification but the central device wasn't subscribed to the notification channel yet”??!)

In addition to the Bluetooth control, I also added another important safety feature to the robot this week – a small (but very loud at max volume) speaker. This allows the robot to make sounds when it does, or is about to do something so everyone knows to get their fingers out of the way before it accelerates. I also added some sounds which are helpful for development too, giving me vital information about the robot's state without having to have a USB cable attached. Currently I've implemented sounds for the following events: – Power up – Bluetooth connected – Bluetooth disconnected – About to move – Stopped

Here's a video demonstrating the wireless Bluetooth control and the speaker sounds

So What About the Control Algorithm?

So the algorithm that sits at the center of everything still hasn't really been implemented or tested. The events of this week have definitely been needed to get me there though, getting remote telemetry from the robot is especially important for helping to tune the algorithm once it's actually making the robot move.

As for the algorithm, or should I say algorithms, themselves though, I've locked in on a class of algorithm called a Kalman Filter. This is a sensor fusion/localisation algorithm that should tell the robot where it is along the track. How I translate that into thrust values for the fans though, is definitely still TBD and requires more research, but I do feel confident I'll figure it out soon, especially when looking back at how much progress this project has made in the last 2 weeks.

Weekly Gantt Chart Check In

5938dbc4-57ae-4f5d-9c18-66fba491e66b

The last 7 days have been so busy it almost feels like a month has passed since my last post. The MDF plates were laser cut the day after my last post and since then I've been hard at work, modelling mounts for everything. I know I said in my last post I planned to just zip-tie everything down, but sensors need a fixed reference point without the risk of them moving around within the carriage, and the grid system I designed with zip-ties in mind makes aligning 3D printed mounts for everything relatively simple.

50c14e20-d80f-4ca4-a1c8-3be46e730f6f

With every change I make, I keep the CAD model up to date and as close as I reasonably can to the physical robot. This help me keep everything well-planned and organised and helps me spot any issues before I try printing/assembling anything.

I'm using M3 heat-set inserts to allow my printed parts to be cleanly and securely bolted to the MDF plate through the grid holes.

Another big milestone reached in the last few days is that the track has now been brought up to the lab, a big thanks to Nick, Alex and the other faculty staff who helped move that enormous thing across campus! The next step is integrating the robot with the track!

It's Sensor Time

Now all the hardware is (mostly) attached together, it's time to turn back to the software side of this project. As I began trying to integrate the sensors, I noticed my development environment I had set up a few weeks ago wasn't particularly dev-friendly; intellisense and syntax completion were nowhere to be found. I got to work researching and made some significant improvements to the development experience by introducing Meta's PyreFly language server into the mix and finding sets of Micropython and Pimoroni firmware stubs to get syntax completion and proper import checking for the built-in libraries I'm using (i.e. machine and Inventor. All this is managed as part of the Nix flake, making the whole setup declarative and completely reproducible with a single command on any machine with Nix installed.

With my environment now set up to my liking, I began programming some modules to read the sensors individually (I'll work on combining the data later). The first was the IMU which required a library to be installed. The official bno08x library from AdaFruit is compatible only with Circuitpython, so to use it in Micropython, I found this community-developed library instead. I added it to the nix flake and made it so Nix regenerates the lib folder every time nix develop is run to keep things reproducible. The tutorial documentation is easy to follow and before long I was getting linear acceleration readings from the IMU.

The DFRobot distance sensor is quite a bit more difficult to work with though, it's accessible only over UART by sending raw byte values directly with no library to abstract away the process. On top of that, the documentation leaves a lot to be desired and it heavily implies DFRobot have reverse-engineered this sensor and don't fully know how it works. After a few hours of staring at the documentation and whipping up a quick script to generate the checksums required at the end every command, I eventually got to the point where I could get readings from it and adjust other settings like activating/deactivating the visible targeting laser.

Gantt chart update for this week

e497f046-aadc-4fd5-ab31-7d5a8bc84156

This week was spent designing and assembling the finalised hardware. I made the realisation that placing the fans back to back as in my original quick mock-up would not be the most intelligent way to mount them as the fans would both be obstructing each other. Instead, I have designed the final hardware with this in mind and stacked the fans vertically instead, allowing clear intake and exhaust for both EDFs.

Using my previous static test stand model as a base, I designed the final carriage parts to hold the fans in place. I also added cable management loops to make sure the wires for the upper fan are safely out of the way during operation.

37392516-36d5-4321-b018-39ddadc972bc

The upper part and both fans are held securely in place using an m3 x 100mm bolt, screwed into some brass heat-set inserts in the base of the stand.

44ea6a8f-4a83-4153-a365-0cdc1a541807 The heat-set inserts after just being inserted with a soldering iron

ff7cfc64-146d-41e0-8489-ae04ecc810a5

As with the previous mini test track, I plan to use laser-cut MDF to interface with the rail slider, but the final version of the carriage also needs a place to mount the batteries, sensors and microcontroller. I have designed a system that will use two MDF plates, separated by standoffs with the other components mounted between them.

After modelling the lower plate at a size I thought was sensible, I drew a scale diagram of it on paper and tried arranging the components in different layouts to make sure everything would fit correctly.

02619df9-2a7b-4015-9f70-efb4292adf01

64cd3007-50d4-4ac5-81ed-06b7a2f9a7cd Note the “front” is at the bottom of this image

My initial plan was to 3d print mounts for each individual component and fix them to the MDF plate with screws, but this would mean I would need to decide on a layout in advance without being able to easily switch the positions of components around. To combat this, I instead added a grid of holes to the model of the lower plate so I can zip-tie each of the components in place, along with any wires to keep them secure.

e7cd0253-d959-4a4d-bf9b-7bcee01ed51e

Regarding sensors, I have finalised my decision on the sensors I will be using too. Of course the feedback signal from each fan will be used and alongside it, I have decided on an Adafruit BNO08x 9-DoF IMU which I will use to measure linear acceleration and a DFRobot SEN0366 IR laser distance sensor (ordered, and should arrive early next week). This distance sensor is impressively accurate, at just a 2mm standard deviation between 1 and 5 meters.

I've been in the lab for a good while over the last week, making a lot of progress (although still not as much as I would have ideally liked).

The first big decision I had to make this week was the choice of programming language I was going to use for the microcontroller. The Pimoroni board I'm using has specific libraries and abstractions available through a customised MicroPython firmware, so that looked like the most suitable choice. This is my first time using MicroPython and I'm still a little unsure about the idea of running an interpreted language on a microcontroller. I'm somewhat tempted to try rebuilding what I have so far in Rust to take advantage of its compile-time error checking, but I've only used Rust a tiny amount compared to Python so switching languages may turn out to be shooting myself in the foot and I can't really afford to waste time at this point in the project.

After setting up the development environment, I was able to start programming some tests. The first step was just to get the fan to boot up, which requires sending a PWM signal of 1000 microseconds. I then began translating some of the jerk simulation code to MicroPython to get a smooth but fast ramp-up curve for the thruster. The full git repo can be found here and will continually be updated as the project progresses.

At this point I hit a little bit of choice paralysis, I was unsure whether I should start integrating sensors, work on getting the second thruster working, or start designing the carriage for the track. Alex thankfully broke me out of this and suggested building a small-scale temporary test track so I could begin testing different thrust durations and ramp-up timings to start seeing how these impacted the thruster's movement. Using some laser-cut MDF and a small (approx. 1m long) rail/linear bearing system, I (we! Alex provided the design and laser-cutting expertise) constructed a small-scale test track/carriage system.

ecd00555-37c3-48a7-adaa-d8afd5cad2a1

I mounted the thruster to the carriage plate and checked my code's output without the fan attached to make sure the PWM values seemed sane, despite having done this, the thruster's movement was far too violent, slamming from one end of the track to the other and unplugging itself from the microcontroller (at which point the speed controller's safety cutoff kicked in). I ended up reducing the PWM signal to near minimum and raising it up to about 15% of what I'd tested it at to find a movement that seemed sane.

The next step now is to try figuring out how jerk values actually impact physical movent and begin looking at integrating sensors into the design.

I am writing this blog post from a hotel room so I'll update with pictures as soon as I can.

Over the course of the last week, I have been working on two sub-components of the project; the first is modelling a test stand for the EDF so I can do experiments with the fan safely and securely in place to avoid damage and injury, the second is creating a simulation of the effect of jerk on the theoretical linear position, building on what was explained in my last blog post.

The test stand

I first decided the best way to design the stand would be to model the EDF itself. The Amazon listing for the fan had a diagram giving the physical measurements for parts of the casing which I used to guide the process:

3455b38f-4bb1-4951-bb26-36e0929c2841

I used digital calipers to measure the casing of my own fan as a sanity check and found that some measurements had to be adjusted, and there were parts of the case that did not have measurements shown on the diagram at all, namely the ridge around the middle of the ducting. The final result has all the detail necessary for going on to model the test stand, but I didn't bother modelling unnecessary details like the fan blades.

ed236218-cd8d-46c1-9192-6df3a7af9ec4

Note that I'm using OnShape to do this modelling as it's the tool I'm used to, and having it run in the browser is very convenient for me (especially given that native Linux CAD tools range from fiddly, incomplete mess to non-existent).

Once I had the fan modeled, It was relatively simple to create the main body of the test stand using the fan model as a template and building around it. I used a boolean difference to make the test stand cradle the exact shape of the fan body, including the central ridge, and added two pins to go through the mounting holes to ensure the fan stays in place. The stand also has a large flat portion with a circular indentation so it can be clamped to a table or desk to keep it in place.

ec6c5cf4-a5fd-44b5-9701-320dd366e949

937838ae-f41d-47a6-8066-983778996d34

I printed a test fit to make sure the ridge indent and pins properly fit the real fan by chopping the model down to the bare minimum (a slice around the middle of the stand about 9mm wide) to make sure everything would fit correctly, and then printed the full stand in white PLA+ (it was the spool that happened to be on my printer at the time and I don't have any reason to believe the stand needs any special properties that might require other filaments) at a 0.24mm layer height with 15% cross-hatch pattern infill.

a3332638-0e48-4068-819f-fa10adda7bd6 This is a photo of the fan on the test stand ready to start experimenting.

I did consider adding a way to manage the wires to keep them out of the way, if this is needed, I'll add it on a future revision of the stand, but given it's only temporary, I'm not convinced it will be needed.

Simulation

This simulation is an extension of the spreadsheet I used in my last blog post. I've written it in typescript using the Fresh framework. This is a framework I've used before and I find it allows me to get up and running with less boilerplate than React or Vue, the other popular frameworks I've used and know.

The simulation allows the editing of jerk “keyframes” which function similarly to keyframes in any video editor/animation software, the jerk value changes only when a keyframe is encountered, with each keyframe being assigned a specific “tick” or timestep to which it belongs.

The simulation can then be started, and the green slider at the top will move accordingly, following the position calculated in real time. At the same time graphs for jerk, acceleration, velocity and position are plotted in real time.

3fec24d8-2fb4-4d22-a00d-e538b9bd53f2 It's not pretty but it is certainly functional.

The git repo for this simulation can be found on my labcode here

This simulation helps me to visualise the kind of movements I'll be having to deal with on the actual test track, but the important next step is to be able to find or create an algorithm that can do this process in reverse. Currently, jerk keyframes are the input to the simulation and the final position is the output (with acceleration and velocity being intermediary stages). To accomplish the main goal of this project though, I need to be able to give the algorithm a position along the track and have it calculate the jerk required and when to apply it in order to reach that position.

Gantt Chart

There's not much to add to the Gantt chart this week beyond marking what I've done as complete, although I have added an entry for preparing for the Viva, which should have been on there from the beginning. I'll be conducting experiments this coming week and I'm sure that will lead to “unknown unknowns” becoming “known unknowns” I can add to the chart.

1d9a6180-cc6d-4de9-871f-aa0c92cc9b98

After discussing with Nick and Alex, I came to realise last week's calculations were based on completely flawed logic. I was for some reason under the impression that the thrust of the EDF needed to out-weigh the overall weight of the carriage, which is not true when moving horizontally (no force of gravity to overcome).

This means that the EDFs I ordered are definitely overkill for what I need them for, but they have already arrived (several days earlier than expected) and returning them and waiting for less powerful fans would just be a waste of time at this point.

To make everything work, I need to calculate the force required to move the entire carriage using Newton's second law of motion, F=ma.

  • m is the mass of the carriage, which will be static once it's built
  • a is the acceleration which will be variable depending on the position the carriage needs to reach
  • F is the thrust required (in Newtons) to make the carriage move at the specified acceleration

The forces involved with EDFs can be very high, high enough to damage or break the motor in some cases. It is important to make sure the acceleration does jump from 0 to 100% too quickly. This means I need to be looking at the next differential up from acceleration, jerk.

Jerk is the rate of change of acceleration, in a similar way to how acceleration is the rate of change of velocity. By making small adjustments to jerk, even just between discrete values of 1, 0 and -1, it's possible to finely and smoothly adjust acceleration in a way which ensures the forces applied to the motor shaft aren't going to do any damage.

This graph shows a simulated change in jerk, acceleration, velocity and absolute position over a span of 200 discrete time steps:

2b3b2a14-b94c-40d9-bee2-ebc439214ef5

Note that velocity and distance have been scaled to 1/10 and 1/100 of their true values respectively, in order to make the other elements of the graph properly visible.

Over the next couple of days, I will work on creating a dynamic visual simulation. This will help me visualise the equations I need to implement for the real hardware.

My updated Gantt chart for this week: 8e5755e6-83e6-43b3-b3ce-92eaeaac4c67

I have spent some time over the last week proving beyond a reasonable doubt that the parts are the correct ones for the job.

Using the specs for ducted fans shown on the Amazon listing, I have created a spreadsheet to calculate the costs and capabilities for each of several ducted fans available in the range of a supplier.

fa0874da-8ec8-4d58-bebd-2d4518536e3c

The above table shows the specs of the various EDFs in the range, and calculated remaining thrust capability (thrust remaining after the entire carriage weight is taken into account) and the unit thrust per £GBP.

The total carriage weight is calculated as:

2*(EDF weight+Recommended ESC Weight) + Recommended Battery Pack Weight + 500

With the final added 500g to account for the microcontroller, sensors, 3d printed chassis etc.

The table determines that the 3S 50mm and 3S 64mm EDFs are not adequate as the thrust produced by these models is less than the total carriage weight would be if these fans were used, meaning the carriage would be unable to move, even theoretically. The 4S 50mm does rise above this threshold, but only very slightly (by 40g) and these calculations do not account for air resistance, track friction or inefficiencies in the motor. It is therefore unlikely that this EDF will be fit for purpose.

The sensible options seem to be to either stick with the 4S 70mm for a 90% higher remaining capability and a 63% higher remaining thrust capability per £GBP, or downgrade to the 4S 64mm to save £15 total and potentially increase battery life by up to 44% (assuming constant peak power draw).

You will see I have also added the next highest model, the 6S 70mm to the table for completeness, but this model's power consumption is far greater than that of any of the “lower tier” models. Because of this it requires a much heaver and more expensive battery pack and does not provide a clear warranted use case.

I have decided that the higher remaining capability of the 4S 70mm will provide a greater thrust window with which to experiment and is therefore worth the price. I have ordered one with which to begin testing.

Unfortunately, the delivery time from this supplier is 8-14 days and I could not find any other suppliers selling EDFs with similar specifications in a more suitable timeframe. I may have to order a smaller and less capable EDF and ESC if I can find one with a quick delivery window in order to get some experience writing code to interface with the ESCs and control the EDFs.

The same ducted fan model I mentioned in my post on 1st July is also sold in a bundle with an appropriate 80A ESC (Electronic Speed Controller). As far as I can tell, this makes it around £2-10 cheaper than buying an equivalent ESC separately (just going by amazon listings at time of writing) and ensures correct compatibility with the EDF.

The product listing shows a wiring diagram, including connecting the ESC to wired and wireless control systems. I'll need to look into how to connect the ESC to an Arduino or similar microcontroller so I can control the thrust output using my own code.

bea268e6-4a9b-4d14-8437-d8bac5d89b61

I will also need a battery, helpfully the listing for the fan and ESC lists an appropriate compatible LiPo battery model: “4S 3500MAH 35C”.

Some preliminary research has revealed that this means the appropriate battery pack will need to have 4 cells in series (to produce the appropriate voltage) and a safe continuous discharge rate of at least 35 times its theoretical capacity of 3500mAh (around 122.5A).

If the battery in question was to be discharged at that rate continuously, it would only last around 1 minute and 45 secoands before running flat. It is probably sensible to purchase at least two battery packs so I can continue working while one charges.

Searching that model on amazon shows battery packs with voltages listed as 14.8 volts. This is what is considered the nominal voltage for the battery pack, but when fully charged, the pack can output as much as 16.8 volts, the amount listed as the maximum for the EDF.

The principle is shown in the following graph from a dataset collected by Matteo Galeotti et al. and published in science direct. It shows the voltage curve for a similar single lithium polymer cell while discharging:

It shows the voltage output starting high at around 4 volts before sharply dropping off and then following a shallower curve (around the nominal voltage) before dropping off steeply again as it finishes discharging. This graph also shows the change in that voltage curve over a number of charge-discharge cycles, but i don't expect that to be of significant concern over the short period of time I will be conducting my experiments.

Of the listings shown on Amazon, almost all of them come in bundles of 2 packs. All these listings range from around £50-60. This listing has appropriate battery packs available for £52.43 for a bundle of 2.

This should be enough parts to build an initial test assembly and begin experimenting with writing code to control the EDF's thrust output. I'll need to purchase a second EDF and ESC in order to construct the carriage for the 1D linear test track setup I proposed in my previous post on 4th July.

I have put together an initial BOM which I will send via email to request the uni purchase the required parts: 4817a283-77cb-4bed-868e-ce1753be18a6

I have also updated my Gantt chart, and I'll start including the most recent version at the end of my blog posts going forward: b4eff7ae-67a6-42ce-9dde-dc527d6a2858