{"id":11935,"date":"2025-10-22T00:21:26","date_gmt":"2025-10-21T23:21:26","guid":{"rendered":"https:\/\/dronesonen.usn.no\/?p=11935"},"modified":"2025-10-30T12:49:58","modified_gmt":"2025-10-30T11:49:58","slug":"aroweek-week-9","status":"publish","type":"post","link":"https:\/\/dronesonen.usn.no\/?p=11935","title":{"rendered":"AROWEEK &#8211; WEEK 9"},"content":{"rendered":"\n<p><strong>\u00c5smund Wigen Thygesen<\/strong>&nbsp;<\/p>\n\n\n\n<p>This week was going to implement the &#8220;set motor speed&#8221; functions. With the motor controllers we are going to use we can use PWM to control the motor speed. However, when looking into using PWM with the raspberry pi I have found little official specifications on the PWM capabilities of the raspberry pi 5, outside of official channels I have seen varying and sometimes conflicting information.&nbsp;<\/p>\n\n\n\n<p>From what I did manage to find it seems like by default the gpiozero library uses software PWM, which would potentially be inaccurate under load, and we need the PWM to be pretty precise since we want to control motors and servos with it. You can change the pin factory, and the pigpio factory, from what I understood, is supposed to utilize hardware PWM, but I failed to find information on whether applies to all pins, just some pins, or any pins but a limited number, so to be sure I decided to do some testing before implementing anything.&nbsp;&nbsp;<\/p>\n\n\n\n<p>As far as I&#8217;m aware, we need 4 PWM signals, tho not all are required at once, so for testing I decided on running 5 simultaneous PWM signals, where 2 of the pins are marked as PWM in some pinouts and the other 3 are not. The duty cycles were set to 0.8, 0.65, 0.5, 0.35, and 0.2 all different in case there&#8217;s an optimisation for identical PWM signals.&nbsp;&nbsp;<\/p>\n\n\n\n<p>To measure the signals I would normally have used an oscilloscope, but didn&#8217;t have one at hand. Instead I utilized hardware interrupts on an arduino nano every(newer version of the nano, which is interrupt capable on every pin). Tho due to memory limitations I ended up having to sample just one signal at a time anyway.&nbsp;&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"708\" height=\"565\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-208.png\" alt=\"\" class=\"wp-image-11941\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-208.png 708w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-208-300x239.png 300w\" sizes=\"auto, (max-width: 708px) 100vw, 708px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"738\" height=\"348\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-207.png\" alt=\"\" class=\"wp-image-11938\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-207.png 738w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-207-300x141.png 300w\" sizes=\"auto, (max-width: 738px) 100vw, 738px\" \/><\/figure>\n\n\n\n<p>This is the sampling function, it is called from each interrupt service routine, with the associated pin index.&nbsp;&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"571\" height=\"207\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-203.png\" alt=\"\" class=\"wp-image-11936\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-203.png 571w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-203-300x109.png 300w\" sizes=\"auto, (max-width: 571px) 100vw, 571px\" \/><\/figure>\n\n\n\n<p>This is the loop, pretty simple, just prints the stats, resets the sample index and increments the pin currently being sampled. The print stats function calculates the average and standard deviation of the duty cycle, pulse width and period.&nbsp;&nbsp;<\/p>\n\n\n\n<p>With that in place I first tested the default PWM without anything else running on the pi, already this was pretty inconsistent, the screenshot below is one where the signal was pretty good, but I had lots of variation even at low load probably due to background programs.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"423\" height=\"354\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-204.png\" alt=\"\" class=\"wp-image-11939\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-204.png 423w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-204-300x251.png 300w\" sizes=\"auto, (max-width: 423px) 100vw, 423px\" \/><\/figure>\n\n\n\n<p>When testing under high load the PWM was consistently very bad, to the point it at times broke my sampling method, as you can see for the pin 3 readings below, where the standard deviation for the duty cycle is 3.32, which seems a bit off considering you can&#8217;t have a duty cycle greater than 1 or less than 0. I have some suspicions of how that happened, but it&#8217;s hard to say for sure without an oscilloscope.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"424\" height=\"345\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-205.png\" alt=\"\" class=\"wp-image-11937\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-205.png 424w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-205-300x244.png 300w\" sizes=\"auto, (max-width: 424px) 100vw, 424px\" \/><\/figure>\n\n\n\n<p>When switching to the pigpio pin factory things immediately look at lot better, for all pins. The image below is the data under low load.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"498\" height=\"426\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-206.png\" alt=\"\" class=\"wp-image-11940\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-206.png 498w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-206-300x257.png 300w\" sizes=\"auto, (max-width: 498px) 100vw, 498px\" \/><\/figure>\n\n\n\n<p>Then under high load the average and standard deviation is essentially the same, so this at least shows we can use the raspberrys PWM, we just have to change the pin factory.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"496\" height=\"426\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-209.png\" alt=\"\" class=\"wp-image-11942\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-209.png 496w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-209-300x258.png 300w\" sizes=\"auto, (max-width: 496px) 100vw, 496px\" \/><\/figure>\n\n\n\n<p>In conclusion, it my testing does indicate that the default PWM for the gpiozero library is indeed software PWM, but we can use the pigpio factory for what seems like hardware PWM on at least 5 pins, it&#8217;s at least consistent enough for our purposes.&nbsp;&nbsp;<\/p>\n\n\n\n<p>I found the testing very interesting, but it did take a lot more time than anticipated, so I didn&#8217;t get time to do anything else, but at least a potential pain point has been eliminated.&nbsp;<\/p>\n\n\n\n<p><strong>Sulaf: Obstacle Avoidance<\/strong><\/p>\n\n\n\n<p>This week, I continued developing my obstacle detection module and began implementing basic obstacle avoidance behavior. Last week, I focused on setting up the detection system \u2014 using threading and shared flags to monitor sensor input independently of the navigation system. This week, I expanded the module to include a simple avoidance maneuver that can be triggered when an obstacle is detected.<\/p>\n\n\n\n<p>The goal remains the same: to create a safety layer that runs in parallel with the navigation system. While the Raspberry Pi only has one core, threading allows the detection and avoidance logic to run in a way that appears concurrent with navigation. This means the robot can continue searching for weeds while also reacting to nearby obstacles.<\/p>\n\n\n\n<p><strong>I<\/strong><strong>ntegration Strategy<\/strong><\/p>\n\n\n\n<p>Since I still don\u2019t have access to the navigation code, I designed the module to be fully independent. It runs in its own thread, continuously checking sensor input. When an obstacle is detected, the module:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sets a shared flag (_is_blocked = True)<\/li>\n\n\n\n<li>Interrupts motion by stopping the motors<\/li>\n\n\n\n<li>Executes a basic avoidance maneuver (e.g., turning slightly)<\/li>\n\n\n\n<li>Resets the flag once the path is clear<\/li>\n<\/ul>\n\n\n\n<p>This behavior is testable without needing the navigation system. Later, the navigation module can read the shared flag and decide whether to pause, reroute, or resume movement.<\/p>\n\n\n\n<p><strong>Software &amp; Hardware Used<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ultrasonic Sensors (for front, left and right detection)<\/li>\n\n\n\n<li>Digital GPIO pins for the Raspberry Pi for sensor input<\/li>\n\n\n\n<li>Motor Control Interface (shared with the navigation system)<\/li>\n\n\n\n<li>Compass and Wheel encoders (used indirectly for orientation and position)<\/li>\n<\/ul>\n\n\n\n<p><strong>Testing Setup<\/strong><\/p>\n\n\n\n<p>Because I still don\u2019t have access to the physical robot or sensors, I continued using simulated sensor readings. These are randomly generated values that mimic ultrasonic distance measurements. This allows me to test the logic and structure of the code without hardware.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"484\" height=\"282\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223.png\" alt=\"\" class=\"wp-image-11955\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223.png 484w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223-300x175.png 300w\" sizes=\"auto, (max-width: 484px) 100vw, 484px\" \/><\/figure>\n\n\n\n<p>I also added simulated motor control functions that print actions to the console. These will be replaced later with real motor commands from the navigation system.<\/p>\n\n\n\n<p>I continued to use the python class that will declare\/define the shared flag, the distance threshold and obstacle detection in parallel (threading):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"761\" height=\"471\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-221.png\" alt=\"\" class=\"wp-image-11957\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-221.png 761w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-221-300x186.png 300w\" sizes=\"auto, (max-width: 761px) 100vw, 761px\" \/><\/figure>\n\n\n\n<p>And the obstacle detection will also be continued to be used:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"765\" height=\"456\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-224.png\" alt=\"\" class=\"wp-image-11959\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-224.png 765w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-224-300x179.png 300w\" sizes=\"auto, (max-width: 765px) 100vw, 765px\" \/><\/figure>\n\n\n\n<p><strong>Obstacle Avoidance (New)<\/strong><\/p>\n\n\n\n<p>Since this week focused on obstacle avoidance, that is what this new code tackles. Obstacle avoidance is essentially navigation when obstacles are detected, ergo navigating until all sensors are giving the clear. That is why the sensors are always being read as before. To make this possible, I have to move the car by manipulating the motor speed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"598\" height=\"169\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-220.png\" alt=\"\" class=\"wp-image-11956\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-220.png 598w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-220-300x85.png 300w\" sizes=\"auto, (max-width: 598px) 100vw, 598px\" \/><\/figure>\n\n\n\n<p>&nbsp;Again, reminder that this is the navigational part, and since I do not want to infringe on \u00c5smund\u2019s part, and since I do not have a physical car model in my possession to even test this, I opted for printing out the navigation\/turns. This is easy to change out anyway, and does not affect the obstacle avoidance at all. This is efficient for testing. &nbsp;<\/p>\n\n\n\n<p><strong>Emergency Stop and Obstacle Avoidance (Integrated with Obstacle Detection)<\/strong><\/p>\n\n\n\n<p>As a reminder, with the obstacle detection I made last week (the threading, the detection loop and the constat checking for obstacles): <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"945\" height=\"574\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-243.png\" alt=\"\" class=\"wp-image-11992\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-243.png 945w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-243-300x182.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-243-768x466.png 768w\" sizes=\"auto, (max-width: 945px) 100vw, 945px\" \/><\/figure>\n\n\n\n<p>And thorught the updated flag I made:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"484\" height=\"282\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223.png\" alt=\"\" class=\"wp-image-11958\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223.png 484w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-223-300x175.png 300w\" sizes=\"auto, (max-width: 484px) 100vw, 484px\" \/><\/figure>\n\n\n\n<p>I can now monitor the obstacles with sensors and check if not self._is_blocked happened or not, if no obstacles are detected through the obstacle detection system, then it will move forward, or else it will \u2018emergency stop\u2019 and \u2018avoid obstacle\u2019 (a new feature, obstacle avoidance part). So now I have to make the emergency stop and avoidance code, which turned out:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"564\" height=\"229\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-222.png\" alt=\"\" class=\"wp-image-11954\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-222.png 564w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-222-300x122.png 300w\" sizes=\"auto, (max-width: 564px) 100vw, 564px\" \/><\/figure>\n\n\n\n<p>This block is the part that stops all navigation when an obstacle is detected by setting the motors to 0. The function below it, the _avoid_obstacle, is simply included to showcase in the terminal better what the system is doing for clarification and testing.<\/p>\n\n\n\n<p>Now that I have the stopping functionality, I still need the navigation method to maneuver around the obstacle. Last week I only had an indicator in my _avoid_obstacle function, I expand this week to include actual navigation. This navigation would include backing up:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"655\" height=\"253\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-225.png\" alt=\"\" class=\"wp-image-11960\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-225.png 655w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-225-300x116.png 300w\" sizes=\"auto, (max-width: 655px) 100vw, 655px\" \/><\/figure>\n\n\n\n<p>Then the sensors check which side does not have any obstacle:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"118\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-226.png\" alt=\"\" class=\"wp-image-11961\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-226.png 481w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-226-300x74.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/figure>\n\n\n\n<p>Following, it maneuvers out the way:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"777\" height=\"366\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-227.png\" alt=\"\" class=\"wp-image-11963\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-227.png 777w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-227-300x141.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-227-768x362.png 768w\" sizes=\"auto, (max-width: 777px) 100vw, 777px\" \/><\/figure>\n\n\n\n<p>Then lastly it does a final sensor check to update the shared flag, to prevent blind movement (and collision), and to confirm the maneuver was a success. All of these functions are inside of the _avoid_obstacle() function.<\/p>\n\n\n\n<p><strong>Testing<\/strong><\/p>\n\n\n\n<p>I stimulate this in the code by changing the navigation code from last week, since I noticed that no matter if there was an obstacle detected or not, the avoid_obstacle function was not triggered properly. The issue is likely due to how fast and randomly the simulation runs. The sensors generate random values every 0.2 seconds, so the flag flips to false and back before I can even notice. I realized that due to the mismatched timing and the many loops that I have, it\u2019s hard to get the avoidance function to trigger. Thus I changed the navigational cycle: <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"945\" height=\"362\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-245.png\" alt=\"\" class=\"wp-image-11997\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-245.png 945w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-245-300x115.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-245-768x294.png 768w\" sizes=\"auto, (max-width: 945px) 100vw, 945px\" \/><\/figure>\n\n\n\n<p><strong>Output<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"613\" height=\"196\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-228.png\" alt=\"\" class=\"wp-image-11962\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-228.png 613w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-228-300x96.png 300w\" sizes=\"auto, (max-width: 613px) 100vw, 613px\" \/><\/figure>\n\n\n\n<p>Here it is possible to see the navigation when an obstacle is detected, by manipulating the motors, either turning them right or left. &nbsp;The cycle in which it detected an obstacle &nbsp;with the sensors is also included.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"613\" height=\"201\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-229.png\" alt=\"\" class=\"wp-image-11964\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-229.png 613w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-229-300x98.png 300w\" sizes=\"auto, (max-width: 613px) 100vw, 613px\" \/><\/figure>\n\n\n\n<p>As you can see, the obstacle avoidance code works, and as it the obstacle detection did, this should also be able to run in parallel with the navigational module in with thread I used. &nbsp;<\/p>\n\n\n\n<p><strong>Integration with Navigation Module<\/strong><\/p>\n\n\n\n<p>I still have to keep this part in mind, however, seeing the update will not affect the navigational part, thus the only thing he needs to do is import my class and check the flag before letting the robot move. In the testing part I used:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"339\" height=\"157\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-244.png\" alt=\"\" class=\"wp-image-11994\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-244.png 339w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-244-300x139.png 300w\" sizes=\"auto, (max-width: 339px) 100vw, 339px\" \/><\/figure>\n\n\n\n<p>Which runs only if the file is executed directly. It prevents the code from running in when the file is imported as a module, so he can reuse my Robot class without triggering navigation automatically.<\/p>\n\n\n\n<p><strong>Rick Embregts<\/strong>&nbsp;<\/p>\n\n\n\n<p>In the following text the main design of the Motor Control Unit and the Battery Management System is explained. At the end of this&nbsp;segment&nbsp;you will find the pictures with the full schematic.&nbsp;<\/p>\n\n\n\n<p><strong>MCU<\/strong>&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Protection\u00a0<\/li>\n<\/ul>\n\n\n\n<p>The limits of the motors are&nbsp;mainly determined&nbsp;by the thermal energy the motor can dissipate. Since the biggest factor in the thermal energy is the&nbsp;current&nbsp;the motors can be current controlled by the jumpers from connector J5&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Current measuring\u00a0<\/li>\n<\/ul>\n\n\n\n<p>For allowing the DRV8840 to&nbsp;mesure&nbsp;the&nbsp;current flowing through the&nbsp;motor&nbsp;the schematic includes a resistor of 82mOhm connected between Isen and ground.&nbsp;<\/p>\n\n\n\n<p>Calculation of current measuring resistor:&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"585\" height=\"81\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-285.png\" alt=\"\" class=\"wp-image-12271\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-285.png 585w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-285-300x42.png 300w\" sizes=\"auto, (max-width: 585px) 100vw, 585px\" \/><\/figure>\n\n\n\n<p>With two 2&nbsp;resisters&nbsp;Vref&nbsp;is set to be 1V.&nbsp;Ichop&nbsp;is calculated by with the max amperage the motors can handle times 2 so we can drive 2 motors with one driver.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"741\" height=\"84\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png\" alt=\"\" class=\"wp-image-12275\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png 741w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287-300x34.png 300w\" sizes=\"auto, (max-width: 741px) 100vw, 741px\" \/><\/figure>\n\n\n\n<p>According to this&nbsp;equation&nbsp;the resistor&nbsp;Rsense&nbsp;must be able to dissipate 0,472W.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Other functions\u00a0<\/li>\n<\/ul>\n\n\n\n<p>The DRV8840 Also has sleep mode, Fault detection, reset, Drive, Direction and decay inputs and outputs. The pins of sleep mode, decay, fault&nbsp;detection&nbsp;and reset are bridged between the 2 controllers for the left and right motors to take up&nbsp;les&nbsp;pins in the raspberry. The other pins have a&nbsp;sepret&nbsp;connection to the Raspberry. All pins can be connected directly to the raspberry due to the internal logic of the MCU functioning from 2VDC.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Spray motor\u00a0<\/li>\n<\/ul>\n\n\n\n<p>To control the motor that sprays&nbsp;liquid&nbsp;a relay is used in combination with a diode to protect the raspberry from the induction of the coil in the relay.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Heat dissipation\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Since the current is flowing through 2&nbsp;mosfets&nbsp;at&nbsp;almost every&nbsp;time the calculation for the generated heat is as follows:&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"741\" height=\"84\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png\" alt=\"\" class=\"wp-image-12273\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png 741w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287-300x34.png 300w\" sizes=\"auto, (max-width: 741px) 100vw, 741px\" \/><\/figure>\n\n\n\n<p>Even with the highest thermal resistance of 31.6 C\/W the&nbsp;chip will stay below the specified operating temperature of 86 C up to&nbsp;a&nbsp;ambient temperature of 37,75 C. The&nbsp;real thermal&nbsp;resistance will be far below the&nbsp;above mentioned&nbsp;resistance. Therefor I comfortably conclude that the chip is adequately cooled and there is no need for&nbsp;some sort of heatsink.&nbsp;<\/p>\n\n\n\n<p><strong>BMS<\/strong>&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Battery connections\u00a0<\/li>\n<\/ul>\n\n\n\n<p>For the battery connections I copied the design used in the datasheet for 4&nbsp;cell&nbsp;with internal cell balancing. This reduces the cost and costs less time. The downside is the lower balancing current.&nbsp;However&nbsp;this is not a problem since the imbalance between the cells should not take too long to&nbsp;discharging&nbsp;the cell with 50&nbsp;mAh.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Battery protection\u00a0<\/li>\n<\/ul>\n\n\n\n<p>The battery is protected by a sense resistor. Pins SRN and SNP are connected to both ends of the&nbsp;resister&nbsp;to&nbsp;mesure&nbsp;a voltage. If this voltage is above 70mV&nbsp;than&nbsp;the protection kicks in. The protection time is set by resistor R13 to the maximum time of 1,4 seconds. For short&nbsp;circuit&nbsp;the time is lower.&nbsp;<\/p>\n\n\n\n<p>See the maximum allowed current and corresponding resistor R13 (Rsense) calculated in the following equation:&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"132\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-288.png\" alt=\"\" class=\"wp-image-12276\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-288.png 481w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-288-300x82.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Temperature sensor\u00a0<\/li>\n<\/ul>\n\n\n\n<p>The specified temperature sensor is the 103AC NTC and is a cheap widely used temperature sensor&nbsp;perficly&nbsp;suitable for this project. The&nbsp;circuit\u2019s&nbsp;described in the datasheet also&nbsp;operate&nbsp;with this&nbsp;sensor&nbsp;which means I can copy and paste this part as well. The only modification is that there will be a connector for the NTC on the board instead of a soldered NTC.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Battery connection\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Due to the limited current&nbsp;capebilities&nbsp;of the battery connector the decision&nbsp;is&nbsp;made to double the ground and positive&nbsp;whire&nbsp;by making the connector 2 pins larger. This saves&nbsp;cost&nbsp;for a new connector&nbsp;an&nbsp;simplifies the PCB.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Low power mode\u00a0<\/li>\n<\/ul>\n\n\n\n<p>The BQ77915 from&nbsp;texus&nbsp;instruments has a hibernate mode. In this&nbsp;mode&nbsp;the&nbsp;current&nbsp; draw&nbsp;of the chip is reduced to 2&nbsp;micor&nbsp;amps. To wake the system&nbsp;up&nbsp;a power switch is connected to the press pin with a 10Kohm resistor. When this is&nbsp;pressed&nbsp;the raspberry supplies the 5V needed to keep the BMS from entering hibernate mode. To enter hibernate&nbsp;mode&nbsp;the entire battery&nbsp;has to&nbsp;be disconnected to make sure the raspberry is not supplying the press pin.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mosfets\u00a0current paths\u00a0<\/li>\n<\/ul>\n\n\n\n<p>There are 2&nbsp;mosfets&nbsp;in the recommended schematic. This is to make sure that the battery can be charged when depleted and discharged when full of charge. Both&nbsp;mosfets&nbsp;are connected through the&nbsp;pcb&nbsp;by drain like the recommended schematic. To calculate the gate&nbsp;resistor&nbsp;see the following equation:&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"420\" height=\"115\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-286.png\" alt=\"\" class=\"wp-image-12272\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-286.png 420w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-286-300x82.png 300w\" sizes=\"auto, (max-width: 420px) 100vw, 420px\" \/><\/figure>\n\n\n\n<p>According to the&nbsp;datasheets&nbsp;the input capacitance of the&nbsp;mosfet&nbsp;is around 565nC and the supply voltage to the gate around 14V. Since the&nbsp;minimum&nbsp;gate voltage is 2,8V, we can conclude that at&nbsp;<img loading=\"lazy\" decoding=\"async\" width=\"8\" height=\"22\" src=\"https:\/\/dronesonen.usn.no\/266aaa31-e9d1-4b93-bc42-d6dd097ce2a7\">&nbsp;is 1 the voltage at the gate is already around 8.8V so above the&nbsp;minimum&nbsp;gate voltage. Therefor we can calculate the resistance by changing&nbsp;the&nbsp;<img loading=\"lazy\" decoding=\"async\" width=\"8\" height=\"22\" src=\"https:\/\/dronesonen.usn.no\/4cc3ab6d-2833-415f-bd9e-c9f4e9d38242\">&nbsp;so we come to the decided 1mS of turn on time. This time is not based on anything and is just a random picked time since the&nbsp;mosfet&nbsp;will turn on and off at way above 1Hz&nbsp;frequency\u2019s. Therefor the switching losses are not calculated.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mosfet\u00a0pull down resistor\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Since the supply voltage of the gate driver of the BQ77915 supply\u2019s 14V the&nbsp;pull down&nbsp;resistance can be calculated by the following formula.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"741\" height=\"84\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png\" alt=\"\" class=\"wp-image-12274\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287.png 741w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-287-300x34.png 300w\" sizes=\"auto, (max-width: 741px) 100vw, 741px\" \/><\/figure>\n\n\n\n<p>To discharge the gate as fast as possible and keep the gate voltage at a minimum of 3V the&nbsp;lowest possible resistor value is 4Kohm. Choosing a higher resistance will result in slower turn off time. Choosing a higher resistance will result in a lower gate voltage and will therefore not allow enough current flow or generate higher losses.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Grounds\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Since the current path is broken at the negative\u00a0therminal\u00a0it is\u00a0neccicery\u00a0to not connect the BMS chip and the raspberry to the same ground.\u00a0Otherwise\u00a0the\u00a0mosfets\u00a0will be bridged and\u00a0useless\u00a0so the BMS\u00a0can\u2019t\u00a0cut the current flow through the battery. The input of the raspberry\u2019s however\u00a0are\u00a0still connected to the BQ77915. In lots of\u00a0cases\u00a0this is\u00a0absolutely not\u00a0ideal. But since the voltage across the\u00a0mosfets\u00a0will be verry\u00a0low\u00a0it poses no risk to the inputs in the raspberry.\u00a0<\/p>\n\n\n\n<p><strong>MCU<\/strong>&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"520\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-290.png\" alt=\"\" class=\"wp-image-12280\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-290.png 901w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-290-300x173.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-290-768x443.png 768w\" sizes=\"auto, (max-width: 901px) 100vw, 901px\" \/><\/figure>\n\n\n\n<p><strong>BMS<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"492\" src=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-296.png\" alt=\"\" class=\"wp-image-12285\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-296.png 901w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-296-300x164.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2025\/10\/image-296-768x419.png 768w\" sizes=\"auto, (max-width: 901px) 100vw, 901px\" \/><\/figure>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00c5smund Wigen Thygesen&nbsp; This week was going to implement the &#8220;set motor speed&#8221; functions. With the motor controllers we are going to use we can use PWM to control the motor speed. However, when looking into using PWM with the raspberry pi I have found little official specifications on the PWM capabilities of the raspberry [&hellip;]<\/p>\n","protected":false},"author":114,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11935","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/11935","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/users\/114"}],"replies":[{"embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11935"}],"version-history":[{"count":7,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/11935\/revisions"}],"predecessor-version":[{"id":12286,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/11935\/revisions\/12286"}],"wp:attachment":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11935"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11935"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11935"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}