User Tools

Site Tools


amc:ss2024:irrigantion_cart_nozzle:start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
amc:ss2024:irrigantion_cart_nozzle:start [2024/07/31 00:58] – [Conclusion] amr.abdelkhalekamc:ss2024:irrigantion_cart_nozzle:start [2024/10/20 23:40] (current) – [Enhancing the If-Condition for Better Plant Recognition] amr.abdelkhalek
Line 36: Line 36:
  
 The following diagram Figure 1 shows the overall layout of our project: The following diagram Figure 1 shows the overall layout of our project:
 +the system uses LIDAR sensors for distance measurements, processes the data with an ESP32-S3-WROOM-1 microcontroller, and communicates the results to a personal computer and other components. 
 +  * **VLX53L0X (LIDAR Sensors):**
 +Horizontal Distance Input: One VLX53L0X sensor measures horizontal distance.
 +Vertical Distance Input: Another VLX53L0X sensor measures vertical distance.
 +  * **ESP32-S3-WROOM-1 (Arduino):**
 +Receives distance readings from both VLX53L0X sensors (horizontal and vertical distance).
 +Processes these readings and performs various tasks based on the data.
 +  * **LED Light/Actuator:**
 +Connected to the Arduino.
 +Can be turned on or off based on commands from the Arduino.
 +  * **Battery/Charger:**
 +Powers the ESP32-S3-WROOM-1.
 +  * **MQTT Server:**
 +Receives processed readings (distance data and actuator state) from the Arduino.
 +Sends actuator control commands (like "On" or "Off") to the Arduino.
 +  * **Personal Computer with Jupyter Notebooks:**
 +Communicates with the MQTT server to receive the distance readings and actuator state.
 +control commands to the MQTT server, which then relays them to the Arduino.
  
  
-{{amc:ss2024:irrigantion_cart_nozzle:blank_diagram.png?1000|Figure 1: Project Layout}}\\ +<imgcaption image1| Project Layout>{{amc:ss2024:irrigantion_cart_nozzle:blank_diagram.png?1000|Figure 1: Project Layout}}</imgcaption>\\
-**Figure 1: Project Layout**+
  
  
 The following Figure 2 is the implementation of the previous layout in real-life application: The following Figure 2 is the implementation of the previous layout in real-life application:
  
 +<imgcaption image2| Real-life Implementation>{{amc:ss2024:irrigantion_cart_nozzle:untitled.png?426.6x775.3|Figure 2: Real-life Implementation}}</imgcaption>\\
  
- 
-{{amc:ss2024:irrigantion_cart_nozzle:untitled.png?426.6x775.3|Figure 2: Real-life Implementation}}\\ 
-**Figure 2: Real-life Implementation** 
  
   - **Vertical VL53L0X Sensor**    - **Vertical VL53L0X Sensor** 
Line 284: Line 299:
 ===== Python Code =====  ===== Python Code ===== 
  
-For installing the necessary libraries, you can download the following text file "requirements.txt" in your working environment +==== Required Libraries ====
  
-<file txt requirements.txt>+To install the required libraries for the project, open and download the following text file "requirements.txt"
  
 +<file txt requirements.txt>
 bqplot bqplot
 traits traits
Line 294: Line 310:
 ipywidgets ipywidgets
 pandas pandas
- 
 </file> </file>
  
-and run the python code+Then run the following terminal command in the environment where you will run the project:
  
 <code python> <code python>
 pip install -r requirements.txt pip install -r requirements.txt
-</code>  +</code> 
  
 ==== MQTT Client Setup and Configuration ====  ==== MQTT Client Setup and Configuration ==== 
Line 526: Line 542:
  
 ==== Defining and Initializing System States with Enum ====  ==== Defining and Initializing System States with Enum ==== 
-<code python>+
 class State(Enum): class State(Enum):
     IDLE = 1       # State when the system is idle     IDLE = 1       # State when the system is idle
Line 1101: Line 1117:
     10001.0   296.0              185.0     12427.5     10001.0   296.0              185.0     12427.5
     10002.0   253.0              200.0     7500.5     10002.0   253.0              200.0     7500.5
-==== Enhancing the If-Condition for Better Plant Recognition ==== 
-To enhance the condition for recognizing plants more accurately, we need to implement peak detection logic and replace the older one; to ensure only true plants are detected. This approach reduces the likelihood of false positives caused by objects with heights above 120 mm even as mentioned earlier and help creating an automated system that keeps track of the plants.  
- 
  
 ==== Enhancing the If-Condition for Better Plant Recognition ==== ==== Enhancing the If-Condition for Better Plant Recognition ====
-To enhance the condition for recognizing plants more accurately, we need to implement peak detection logic and replace the older one; to ensure only true plants are detected. This approach reduces the likelihood of false positives caused by objects with heights above 120 mm even as mentioned earlier and help creating an automated system that keeps track of the plants. +To enhance the condition for recognizing plants more accurately, we need to implement peak detection logic and replace the older one; to ensure only true plants are detected. This approach reduces the likelihood of false positives caused by objects with heights above 120 mm even as mentioned earlier and helps create an automated system that keeps track of the plants. 
  
-In order to implement this, testing the time required to detect to plants' peaks through a full array by find_peaks is cruical.+In order to implement this, testing the time required to detect plants' peaks through a full array by find_peaks is crucial.
  
 This was tested on our array of 870 elements detecting the two peaks by the following line of command in the peak detecting step: This was tested on our array of 870 elements detecting the two peaks by the following line of command in the peak detecting step:
  
 <code python> <code python>
-%%time #outputs the processing time+# Measure the time taken to find peaks in the array 
 +%%time  #outputs the processing time 
 + 
 +# Find peaks in the array 'array_z' with a specified width and distance between peaks
 peaks5, wid_dis = find_peaks(array_z, width=9,distance=50) peaks5, wid_dis = find_peaks(array_z, width=9,distance=50)
 </code>   </code>  
Line 1120: Line 1136:
   Wall time: 0 ns   Wall time: 0 ns
  
 +# Note: The processing time changes if we have to plot every time, which in our application, we used the faster bqplot modules, but regardless:
  
-this of course changes if we have to plot everytime which of course in our application we used the faster bqplot modules but regardless: 
 <code python> <code python>
 +# Measure the time taken to find peaks in the array
 %%time #outputs the processing time %%time #outputs the processing time
 +
 +# Find peaks in the array 'array_z' with a specified width and distance between peaks
 peaks5, wid_dis = find_peaks(array_z, width=9,distance=50) peaks5, wid_dis = find_peaks(array_z, width=9,distance=50)
-plt.plot(peaks5, array_z[peaks5], "vg")plt.plot(array_z)plt.legend(['width + distance'])+ 
 +# Plot the original data with the detected peaks 
 + plt.plot(peaks5, array_z[peaks5], "vg") 
 + plt.plot(array_z) 
 + plt.legend(['width + distance']) 
 </code>   </code>  
  
Line 1135: Line 1159:
  
 <code python> <code python>
 +# Measure the time taken to plot the results
 %%time #outputs the processing time %%time #outputs the processing time
-plt.plot(peaks5, array_z[peaks5], "vg"); plt.plot(array_z); plt.legend(['width + distance'])+ 
 +# Plot the original data with the detected peaks 
 +plt.plot(peaks5, array_z[peaks5], "vg");  
 +plt.plot(array_z); 
 +plt.legend(['width + distance'])
 </code> </code>
      
Line 1142: Line 1171:
   Wall time: 54.4 ms   Wall time: 54.4 ms
      
-confirming that all the time spent is in plotting and not searching for the peaks ... this suggests that we can use find_peaks every reading or almost as often without worrying about spamming our hardware.+Confirming that all the time spent is in plotting and not searching for the peaksthis suggests that we can use find_peaks every reading or almost as often without worrying about spamming our hardware.
  
  
Line 1148: Line 1177:
  
 <code python> <code python>
-N1 = 870 +# Initialize the arrays and variables for testing with our older real data 
-z_data = np.array(np.zeros(N1))  # Initialize with empty arrays (to resemble the variable we have in our on_message function) + 
-peaks_total_xposition = np.array([])  # Initialize with empty arrays +N1 = 870                              # Number of elements in the array 
-peaks_last = np.array([]) # Initialize with empty arrays +z_data = np.array(np.zeros(N1))       # Initialize with empty arrays (to resemble the variable we have in our on_message function) 
-counter = 0 +peaks_total_xposition = np.array([])  # Initialize with empty array  
-counter_2 = 0 +peaks_last = np.array([])             # Initialize with empty array  
-checker = N1/10 #is set to check 10 times ever 1 time the z_data array is completely filled +counter = 0                           # Counter to keep track of the number of readings 
 +counter_2 = 0                         # Secondary counter to keep track of the number of readings  
 +checker = N1 / 10                     Set to check 10 times every time the z_data array is completely filled 
 </code> </code>
      
-and now to check the output and the time of using this in a for-loop that activates for every reading to again model our on_message function+# Now check the output and the time of using this in a for-loop that activates for every reading to model our on_message function
  
 <code python> <code python>
-%%time+ 
 +%%time # Outputs the processing time 
 +# Iterate through each element in array_z
 for i in  array_z: for i in  array_z:
-    z_data = np.roll(z_data, -1); z_data[-1] = i +    # Shift the array to the left by one position 
-    counter += 1 +    z_data = np.roll(z_data, -1); z_data[-1] = i # Update the last element with the current reading 
-    counter_2 += 1+    counter += 1   # Increment the counter 
 +    counter_2 += 1 # Increment the counter
     if counter == checker:     if counter == checker:
-        peaks_wd, wid_dis = find_peaks(z_data, width=9,distance=50) +        # Find peaks in the z_data array with a specified width and distance between peaks  
-        if   len(peaks_wd) > len(peaks_last): +        peaks_wd, wid_dis = find_peaks(z_data, width=9,distance=50)  
-            peaks_total_xposition = np.sort(np.unique(np.append(peaks_total_xposition,  array_x[peaks_wd]))) +        if   len(peaks_wd) > len(peaks_last):  
-            peaks_last = peaks_wd +        # If the number of detected peaks is greater than the last detected peaks which indicates a new peak ... 
-        counter = 0+            # Update the total x positions of the peaks  
 +            # np.append(peaks_total_xposition, array_x[peaks_wd]): Append the current peak positions (array_x[peaks_wd]) to the existing peak positions (peaks_total_xposition)  
 +            # np.unique(...): Remove duplicate peak positions from the appended array 
 +            # np.sort(...): Sort the peak positions in ascending order 
 +            peaks_total_xposition = np.sort(np.unique(np.append(peaks_total_xposition, array_x[peaks_wd])))  
 +            peaks_last = peaks_wd # Update the last detected peaks 
 +            counter = 0 # Reset the counter
     if counter_2 == N1:     if counter_2 == N1:
-       z_data = np.array(np.zeros(N1))     +        z_data = np.array(np.zeros(N1))    # Reset the z_data array 
 + 
 + 
 </code>    </code>   
  
Line 1178: Line 1221:
   Wall time: 35.8 ms       Wall time: 35.8 ms    
  
-It may be a bit of unnecessary to do the sorting unique check every time/reading for our peak detection array but again it worked. However, future optimization may be done here.+It may be a bit unnecessary to do the sorting unique check every time/reading for our peak detection array but again it worked. However, future optimization may be done here.
  
 and the output was the same: and the output was the same:
 <code python> <code python>
 +# Print the total x positions of the peaks
 peaks_total_xposition peaks_total_xposition
 </code> </code>
  
-  array([253., 296.])+  array([253., 296.]) # Output
      
 Feel encouraged to compare with our last trial on the full array of array_z... it produces the same output Feel encouraged to compare with our last trial on the full array of array_z... it produces the same output
Line 1204: Line 1248:
         peaks_wd, wid_dis = find_peaks(z_data, width=9,distance=50)         peaks_wd, wid_dis = find_peaks(z_data, width=9,distance=50)
         if   len(peaks_wd) > len(peaks_last):         if   len(peaks_wd) > len(peaks_last):
-            peak_positions = np.add(np.full(len(array_x[peaks_wd]), arm_sens_lead), array_x[peaks_wd])+            # Calculate the new peak positions by adding the sensor lead distance to the current peak positions 
 +            # np.full(len(array_x[peaks_wd]), arm_sens_lead): Creates an array of the same length as array_x[peaks_wd],  
 +            # where every element is equal to arm_sens_lead (300 mm). This represents the lead distance for the sensor. 
 +            # array_x[peaks_wd]: Retrieves the x positions corresponding to the detected peaks.  
 +            # np.add(...): Adds the sensor lead distance to each of the x positions of the detected peaks.peak_positions =  
 +            np.add(np.full(len(array_x[peaks_wd]), arm_sens_lead), array_x[peaks_wd])
             peaks_total_xposition = np.sort(np.unique(np.append(peaks_total_xposition,peak_positions)))             peaks_total_xposition = np.sort(np.unique(np.append(peaks_total_xposition,peak_positions)))
             peaks_last = peaks_wd             peaks_last = peaks_wd
Line 1213: Line 1262:
  
 <code python> <code python>
- peaks_total_xposition+peaks_total_xposition
 </code> </code>
  
   array([553., 596.])   array([553., 596.])
      
-assuming the cart is 30 cm behind then the values-add and this stage are also successful+Assuming the cart is 30 cm behindthen the values-added at this stage are also successful 
 ==== Enhancing the If-Condition for Better Plant Recognition - Implementation ==== ==== Enhancing the If-Condition for Better Plant Recognition - Implementation ====
  
-We move to our on_message function and apply the new findings:+Now we will move to our on_message function and apply the new findings:
  
  
Line 1249: Line 1299:
          
     s = f"{i:4d}  {t:12.2f}  {x:6.2f}  {z:6.2f}"     s = f"{i:4d}  {t:12.2f}  {x:6.2f}  {z:6.2f}"
 +    # Check if the system is idle and the current x position is at a peak position
     if (state == state.IDLE) & np.isin(x,peaks_total_xposition):# & (z > 120) if a threshold is needed     if (state == state.IDLE) & np.isin(x,peaks_total_xposition):# & (z > 120) if a threshold is needed
         client.publish("gw/duese002-licht", "on")         client.publish("gw/duese002-licht", "on")
         state = state.WATERING         state = state.WATERING
 +    # Check if the system is watering and the current x position is not at a peak position
     if (state == state.WATERING) & np.isin(x,peaks_total_xposition,invert=True):     if (state == state.WATERING) & np.isin(x,peaks_total_xposition,invert=True):
         client.publish("gw/duese002-licht", "off")         client.publish("gw/duese002-licht", "off")
         state = state.IDLE         state = state.IDLE
-     #check if the water is off and the cart position is on a plant location .. turns it on +         
-     #check if the water is on and the cart position is not on a plant location (inverts the logic).. turns it off+     #checks if the water is off and the cart position is on a plant location .. turns it on 
 +     #checks if the water is on and the cart position is not on a plant location (inverts the logic).. turns it off
  
                      
Line 1298: Line 1350:
     plotter.update_data2(data2)     plotter.update_data2(data2)
          
-#    out.append_stdout(s + "\n")+#    out.append_stdout(s + "\n")def on_message(client, userdata, message): 
 +    
 </code> </code>
 ====== VI. Conclusion ====== ====== VI. Conclusion ======
  
-Further development and testing will continue to refine the detection algorithms and explore alternative communication protocols to enhance system performance and reliability as well as creating proper backups using PostgreSQL Dashboards.+This project addresses water scarcity by developing an automated irrigation cart that conserves water through precise plant detection and targeted watering.  
 + 
 +Utilizing VL53L0X sensors and an Arduino, the system accurately monitors plant presence and cart movement, ensuring efficient water use.  
 + 
 +The integration of MQTT for communication and Python for data processing lays the groundwork for future enhancements, including improved sensors and expanded scalability. 
 + 
 +Future enhancements will focus on refining detection algorithms and exploring alternative communication protocols, including PostgreSQL Dashboards for reliable backups 
  
 Sensors may be replaced in the future with faster detecting ones so that their algorithms may be easier or more straight forward to accommodate a much larger number of sensors.  Sensors may be replaced in the future with faster detecting ones so that their algorithms may be easier or more straight forward to accommodate a much larger number of sensors. 
Line 1308: Line 1368:
 Multiplexers most probably will be used for connecting with the multiple number of nozzles for our system e.g. irrigating 30 rows at the same time. Each of which will have at least a single sensor, and a water nozzle. 30 controlling MOSFETS hence may be connected to our microcontroller plus the 30 sensors.  Multiplexers most probably will be used for connecting with the multiple number of nozzles for our system e.g. irrigating 30 rows at the same time. Each of which will have at least a single sensor, and a water nozzle. 30 controlling MOSFETS hence may be connected to our microcontroller plus the 30 sensors. 
  
-Further studies are going to be done on the rush in current in case large nozzles will be used .. and 30 of those will mean multiple of that rush in current, in case a smooth start is needed to be done to ease that rush in current PWM may be required.+Further studies are going to be done on the rush in current in case large nozzles will be used .. and 30 of those will mean multiple of that rush in current. Additionallythis further research will be conducted on managing current surges when using large nozzles might introduce the possibility of implementing PWM to ensure a smooth startup to mitigate the current spikes.
  
 https://wiki.eolab.de/doku.php?id=eolab:projects:giesswagen:start https://wiki.eolab.de/doku.php?id=eolab:projects:giesswagen:start
  
 ====== VII. References ====== ====== VII. References ======
 +
 +  * Pololu, “VL53L0X-arduino/examples/single/single.ino at master · Pololu/VL53L0X-arduino,” GitHub, https://github.com/pololu/vl53l0x-arduino/blob/master/examples/Single/Single.ino (accessed Jun. 17, 2024). 
 +  *  “Find_peaks#,” find_peaks - SciPy v1.14.0 Manual, https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html#scipy.signal.find_peaks (accessed Jul. 09, 2024).
 +
 +
  
amc/ss2024/irrigantion_cart_nozzle/start.1722380287.txt.gz · Last modified: 2024/07/31 00:58 by amr.abdelkhalek