Raykarr's picture
Update app.py
b2ad3fc verified
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
# --------------------------------------
# Utility functions
# --------------------------------------
def generate_random_pile(num_points=50, max_height=10.0, noise=0.5):
"""
Generates a random 'pile shape' with a certain maximum height.
shape it as a bell-curve-like mound plus random noise.
Args:
num_points (int): Number of discrete x-points to model the silo cross-section.
max_height (float): Approximate max possible height.
noise (float): Random fluctuations around the main shape.
Returns:
x_array (np.array): X-coordinates from 0..1 (normalized).
height_array (np.array): Random mound shape values at each x.
"""
x_array = np.linspace(0, 1, num_points)
center = 0.5
sigma = 0.15 # controls how wide the mound is
base_curve = max_height * np.exp(-0.5 * ((x_array - center) / sigma)**2)
random_variation = noise * np.random.randn(num_points)
height_array = np.clip(base_curve + random_variation, 0, None)
return x_array, height_array
def simulate_sensor_readings(x_array, height_array, sensor_type="LiDAR", num_sensors=5):
"""
Simulates sensor readings from the height array.
randomly pick a few positions along x_array,
then record the height + some noise depending on sensor_type.
Args:
x_array (np.array): X-coordinates for the silo cross-section
height_array (np.array): True pile height at each x
sensor_type (str): "LiDAR" or "Ultrasonic" - determines noise level
num_sensors (int): number of sensors/points to read
Returns:
sensor_positions (np.array): Chosen positions
sensor_heights (np.array): Measured heights (with noise)
"""
sensor_positions = np.random.choice(x_array, size=num_sensors, replace=False)
sensor_positions = np.sort(sensor_positions)
sensor_heights = []
for pos in sensor_positions:
idx = np.argmin(np.abs(x_array - pos))
true_height = height_array[idx]
if sensor_type.lower() == "lidar":
noise_std = 0.1
else:
noise_std = 0.2
measured = true_height + np.random.randn() * noise_std
sensor_heights.append(max(measured, 0.0))
return sensor_positions, np.array(sensor_heights)
def estimate_peak_height(sensor_positions, sensor_heights):
"""
Simple approach: take the max sensor reading as approximate peak.
"""
return np.max(sensor_heights)
def estimate_volume(x_array, height_array, silo_width=5.0, silo_depth=5.0):
"""
Approximates volume of the pile using trapezoidal integration
across the 2D cross-section, then multiplying by depth.
Args:
x_array: normalized x from 0..1
height_array: height at each x
silo_width (float): actual width of silo in meters
silo_depth (float): depth into the page in meters
Returns:
volume (float): approximate volume in cubic meters
"""
x_meters = x_array * silo_width
area_2d = np.trapz(height_array, x_meters) # m^2
volume = area_2d * silo_depth # m^3
return volume
def plot_pile_shape(x_array, height_array, sensor_positions=None, sensor_heights=None):
"""
Creates and returns a matplotlib figure showing
the random pile shape and optionally sensor readings.
"""
fig, ax = plt.subplots(figsize=(5,3))
ax.plot(x_array, height_array, label='Pile Shape', linewidth=2, color='blue')
if sensor_positions is not None and sensor_heights is not None:
ax.scatter(sensor_positions, sensor_heights, color='red',
label='Sensor Readings', s=50, zorder=5)
ax.set_title("Silo Cross-Section (2D) - Wood Chip Pile")
ax.set_xlabel("Normalized Silo Width (0..1)")
ax.set_ylabel("Height (m)")
ax.legend()
fig.tight_layout()
return fig
def plot_sensor_bar(sensor_positions, sensor_heights):
"""
Creates and returns a bar chart showing sensor heights
vs. sensor ID or position.
"""
fig, ax = plt.subplots(figsize=(4,3))
# Let's use sensor index as x-ticks
indices = np.arange(len(sensor_positions))
ax.bar(indices, sensor_heights, color='green')
ax.set_title("Sensor Readings Bar Chart")
ax.set_xlabel("Sensor Index")
ax.set_ylabel("Measured Height (m)")
ax.set_xticks(indices)
ax.set_xticklabels([f"{pos:.2f}" for pos in sensor_positions], rotation=45)
fig.tight_layout()
return fig
# --------------------------------------
# Streamlit App
# --------------------------------------
st.set_page_config(page_title="ITC PSPD - Wood Chip Silo Demo", layout="centered")
st.title("Height Detection Of Wood Chips in Closed Silos - ITC PSPD Demo")
st.markdown("""
Welcome to a simple demonstration of *Wood-chip silo height profile Detection and Volume Estimation*.
This is an **Industry 4.0** inspired solution to **digitally** track the *height* and *volume*
of wood chips in a silo, supporting **paperboard and specialty paper** operations at **ITC PSPD**.
---
""")
# Sidebar controls
st.sidebar.header("Simulation Controls")
sensor_type = st.sidebar.selectbox("Select Sensor Type:", ["LiDAR", "Ultrasonic"])
max_height = st.sidebar.slider("Max Potential Height (m):", 5.0, 30.0, 10.0, 1.0)
noise_level = st.sidebar.slider("Random Noise Level:", 0.0, 3.0, 0.5, 0.1)
num_points = st.sidebar.slider("Pile Resolution (Points):", 20, 200, 50, 10)
num_sensors = st.sidebar.slider("Number of Sensors:", 1, 12, 5)
silo_width = st.sidebar.slider("Silo Width (m):", 1.0, 20.0, 5.0, 1.0)
silo_depth = st.sidebar.slider("Silo Depth (m):", 1.0, 20.0, 5.0, 1.0)
# Button to generate new random pile
if "x_array" not in st.session_state or "height_array" not in st.session_state:
st.session_state["x_array"] = None
st.session_state["height_array"] = None
if st.sidebar.button("Generate New Random Pile"):
x_array, height_array = generate_random_pile(
num_points=num_points,
max_height=max_height,
noise=noise_level
)
st.session_state["x_array"] = x_array
st.session_state["height_array"] = height_array
# If we have data, proceed
if st.session_state["x_array"] is not None and st.session_state["height_array"] is not None:
x_array = st.session_state["x_array"]
height_array = st.session_state["height_array"]
# Simulate sensor readings
sensor_positions, sensor_heights = simulate_sensor_readings(
x_array, height_array, sensor_type, num_sensors
)
# Estimate peak height from sensor
approx_peak = estimate_peak_height(sensor_positions, sensor_heights)
# Estimate volume
volume_est = estimate_volume(x_array, height_array, silo_width, silo_depth)
st.subheader("1) Silo Cross-Section Visualization")
fig_pile = plot_pile_shape(x_array, height_array, sensor_positions, sensor_heights)
st.pyplot(fig_pile)
# Show measured peak height
col1, col2 = st.columns(2)
with col1:
st.markdown(f"**Approx Peak Height (from sensors):** `{approx_peak:.2f} m`")
st.progress(min(approx_peak/max_height, 1.0))
with col2:
st.markdown(f"**Estimated Volume:** `{volume_est:.2f} m³`")
st.progress(min(volume_est/((max_height*silo_width*silo_depth)), 1.0))
st.subheader("2) Sensor Data Overview")
col3, col4 = st.columns(2)
with col3:
# Show bar chart of sensor readings
fig_bars = plot_sensor_bar(sensor_positions, sensor_heights)
st.pyplot(fig_bars)
with col4:
st.write("### Sensor Data Table")
table_data = {
"Position (norm)": [f"{pos:.2f}" for pos in sensor_positions],
"Height (m)": [f"{h:.2f}" for h in sensor_heights]
}
st.table(table_data)
st.markdown("---")
st.markdown("""
### Interpretation & Industry 4.0 Connection
- **Digital Twins**: This simulation mimics how a *digital twin* of the silo tracks
wood-chip heights. The real system could have *LiDAR* sensors scanning
or an array of *ultrasonic* sensors measuring the pile at intervals.
- **Data-Driven Insights**: Automated volume estimation helps production planning,
ensuring continuous supply for **paperboard** manufacturing while minimizing
waste or silo overflow.
- **ITC PSPD**: As a leading division in paper & packaging, adopting *Industry 4.0*
solutions supports sustainability and operational efficiency—aligned with ITC’s
triple bottom line.
- **Big Data & AI**: If multiple silos or sites feed data into a cloud platform,
advanced analytics/predictive models can optimize inventory or detect anomalies.
---
""")
else:
st.info("Use the **sidebar** to generate a new random pile and view the results.")
st.markdown("""
## Made By:
- Name: Kaustubh Raykar
- PRN: 21070126048
- Btech AIML 2021-25
- Contact: +91 7020524609
- Symbiosis Institute Of Technology, Pune
- [email protected]
- [email protected]
**Thank you** for exploring this Wood Chip Silo Demo for **ITC PSPD**.
""")