soumalya-iitj's picture
Update index.html
e0dc152 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Embodied AI - Autonomous Vehicle Simulator</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
color: white;
}
.header {
background: rgba(0, 0, 0, 0.3);
padding: 20px;
text-align: center;
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.main-container {
display: flex;
flex: 1;
gap: 20px;
padding: 20px;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.simulation-area {
flex: 1;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.canvas-container {
position: relative;
width: 100%;
height: 600px;
background: #1a1a2e;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
#simulationCanvas {
width: 100%;
height: 100%;
display: block;
}
.controls-panel {
width: 300px;
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
height: fit-content;
}
.control-section {
margin-bottom: 25px;
}
.control-section h3 {
font-size: 1.2rem;
margin-bottom: 15px;
color: #fff;
border-bottom: 2px solid rgba(255, 255, 255, 0.3);
padding-bottom: 8px;
}
.control-group {
margin-bottom: 15px;
}
.control-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #e0e0e0;
}
.control-group input, .control-group select {
width: 100%;
padding: 8px 12px;
border: none;
border-radius: 8px;
background: rgba(255, 255, 255, 0.2);
color: white;
font-size: 14px;
}
.control-group input::placeholder {
color: rgba(255, 255, 255, 0.6);
}
.btn {
width: 100%;
padding: 12px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 10px;
}
.btn-primary {
background: linear-gradient(45deg, #4CAF50, #45a049);
color: white;
}
.btn-secondary {
background: linear-gradient(45deg, #2196F3, #1976D2);
color: white;
}
.btn-danger {
background: linear-gradient(45deg, #f44336, #d32f2f);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.stats {
background: rgba(0, 0, 0, 0.3);
padding: 15px;
border-radius: 8px;
margin-top: 15px;
}
.stats h4 {
margin-bottom: 10px;
color: #fff;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.stat-label {
color: #ccc;
}
.stat-value {
color: #4CAF50;
font-weight: 600;
}
.ai-info {
background: rgba(0, 0, 0, 0.3);
padding: 15px;
border-radius: 8px;
margin-top: 15px;
font-size: 13px;
line-height: 1.4;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 15px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
}
.legend-color {
width: 16px;
height: 16px;
border-radius: 50%;
}
@media (max-width: 768px) {
.main-container {
flex-direction: column;
}
.controls-panel {
width: 100%;
}
}
</style>
</head>
<body>
<div class="header">
<h1>🚗 Embodied AI Vehicle Simulator</h1>
<p>Interactive autonomous vehicle navigation with real-time AI decision making</p>
</div>
<div class="main-container">
<div class="simulation-area">
<div class="canvas-container">
<canvas id="simulationCanvas"></canvas>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background: #4CAF50;"></div>
<span>Autonomous Vehicle</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f44336;"></div>
<span>Obstacles</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #2196F3;"></div>
<span>Target Destinations</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #FFC107;"></div>
<span>Sensors (LiDAR)</span>
</div>
</div>
</div>
<div class="controls-panel">
<div class="control-section">
<h3>🎮 Simulation Controls</h3>
<button class="btn btn-primary" id="startBtn">Start Simulation</button>
<button class="btn btn-secondary" id="pauseBtn">Pause</button>
<button class="btn btn-danger" id="resetBtn">Reset</button>
</div>
<div class="control-section">
<h3>⚙️ Parameters</h3>
<div class="control-group">
<label>Vehicle Speed</label>
<input type="range" id="speedSlider" min="0" max="5" value="2" step="0.5">
<span id="speedValue">2.0</span>
</div>
<div class="control-group">
<label>Sensor Range</label>
<input type="range" id="sensorRange" min="50" max="200" value="100" step="10">
<span id="sensorValue">100</span>
</div>
<div class="control-group">
<label>AI Behavior</label>
<select id="behaviorSelect">
<option value="cautious">Cautious</option>
<option value="normal" selected>Normal</option>
<option value="aggressive">Aggressive</option>
</select>
</div>
</div>
<div class="control-section">
<h3>🏭 Environment</h3>
<button class="btn btn-secondary" id="addObstacleBtn">Add Obstacle</button>
<button class="btn btn-secondary" id="addTargetBtn">Add Target</button>
<button class="btn btn-danger" id="clearBtn">Clear All</button>
</div>
<div class="stats">
<h4>📊 Real-time Stats</h4>
<div class="stat-item">
<span class="stat-label">Distance Traveled:</span>
<span class="stat-value" id="distanceTraveled">0m</span>
</div>
<div class="stat-item">
<span class="stat-label">Targets Reached:</span>
<span class="stat-value" id="targetsReached">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Obstacles Avoided:</span>
<span class="stat-value" id="obstaclesAvoided">0</span>
</div>
<div class="stat-item">
<span class="stat-label">AI Decisions/sec:</span>
<span class="stat-value" id="decisionsPerSec">0</span>
</div>
</div>
<div class="ai-info">
<h4>🧠 AI Decision Making</h4>
<p id="currentDecision">System initializing...</p>
<br>
<p><strong>Embodied AI Features:</strong></p>
<ul style="margin-left: 15px; margin-top: 5px;">
<li>Real-time sensor processing</li>
<li>Dynamic path planning</li>
<li>Obstacle avoidance</li>
<li>Goal-oriented behavior</li>
<li>Environmental adaptation</li>
</ul>
</div>
</div>
</div>
<script>
// Canvas setup
const canvas = document.getElementById('simulationCanvas');
const ctx = canvas.getContext('2d');
// Set canvas size
function resizeCanvas() {
const container = canvas.parentElement;
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Simulation state
let isRunning = false;
let animationId = null;
let lastTime = 0;
let decisionCount = 0;
let lastDecisionTime = 0;
// Vehicle class
class AutonomousVehicle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.angle = 0;
this.speed = 2;
this.maxSpeed = 5;
this.size = 15;
this.sensorRange = 100;
this.sensors = [];
this.path = [];
this.currentTarget = null;
this.distanceTraveled = 0;
this.lastX = x;
this.lastY = y;
this.behavior = 'normal';
this.avoidanceVector = { x: 0, y: 0 };
}
updateSensors() {
this.sensors = [];
const numSensors = 8;
for (let i = 0; i < numSensors; i++) {
const angle = (i / numSensors) * 2 * Math.PI;
const endX = this.x + Math.cos(angle) * this.sensorRange;
const endY = this.y + Math.sin(angle) * this.sensorRange;
let distance = this.sensorRange;
// Check collision with obstacles
for (let obstacle of obstacles) {
const dx = obstacle.x - this.x;
const dy = obstacle.y - this.y;
const distToObstacle = Math.sqrt(dx * dx + dy * dy);
if (distToObstacle < distance && distToObstacle > 0) {
const angleToObstacle = Math.atan2(dy, dx);
const angleDiff = Math.abs(angle - angleToObstacle);
if (angleDiff < 0.5 || angleDiff > 2 * Math.PI - 0.5) {
distance = Math.max(0, distToObstacle - obstacle.size);
}
}
}
this.sensors.push({
angle: angle,
distance: distance,
x: this.x + Math.cos(angle) * distance,
y: this.y + Math.sin(angle) * distance
});
}
}
makeDecision() {
decisionCount++;
// Find closest target
let closestTarget = null;
let minDistance = Infinity;
for (let target of targets) {
const dx = target.x - this.x;
const dy = target.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
closestTarget = target;
}
}
this.currentTarget = closestTarget;
// Calculate avoidance vector
this.avoidanceVector = { x: 0, y: 0 };
for (let sensor of this.sensors) {
if (sensor.distance < this.sensorRange * 0.7) {
const avoidanceStrength = (this.sensorRange * 0.7 - sensor.distance) / (this.sensorRange * 0.7);
this.avoidanceVector.x -= Math.cos(sensor.angle) * avoidanceStrength;
this.avoidanceVector.y -= Math.sin(sensor.angle) * avoidanceStrength;
}
}
// Normalize avoidance vector
const avoidanceMag = Math.sqrt(this.avoidanceVector.x ** 2 + this.avoidanceVector.y ** 2);
if (avoidanceMag > 0) {
this.avoidanceVector.x /= avoidanceMag;
this.avoidanceVector.y /= avoidanceMag;
}
// Update decision display
const decisionElement = document.getElementById('currentDecision');
if (this.currentTarget) {
if (avoidanceMag > 0.1) {
decisionElement.textContent = `Avoiding obstacles while navigating to target (${Math.round(minDistance)}m away)`;
} else {
decisionElement.textContent = `Direct path to target (${Math.round(minDistance)}m away)`;
}
} else {
decisionElement.textContent = 'Searching for targets...';
}
}
update(deltaTime) {
this.updateSensors();
this.makeDecision();
// Calculate target vector
let targetVector = { x: 0, y: 0 };
if (this.currentTarget) {
const dx = this.currentTarget.x - this.x;
const dy = this.currentTarget.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
targetVector.x = dx / distance;
targetVector.y = dy / distance;
}
// Check if reached target
if (distance < 20) {
const index = targets.indexOf(this.currentTarget);
if (index > -1) {
targets.splice(index, 1);
stats.targetsReached++;
document.getElementById('targetsReached').textContent = stats.targetsReached;
}
}
}
// Combine vectors based on behavior
let finalVector = { x: 0, y: 0 };
const behaviorSettings = {
cautious: { avoidance: 0.8, target: 0.2 },
normal: { avoidance: 0.6, target: 0.4 },
aggressive: { avoidance: 0.3, target: 0.7 }
};
const settings = behaviorSettings[this.behavior];
finalVector.x = targetVector.x * settings.target + this.avoidanceVector.x * settings.avoidance;
finalVector.y = targetVector.y * settings.target + this.avoidanceVector.y * settings.avoidance;
// Normalize final vector
const finalMag = Math.sqrt(finalVector.x ** 2 + finalVector.y ** 2);
if (finalMag > 0) {
finalVector.x /= finalMag;
finalVector.y /= finalMag;
}
// Update velocity
this.vx = finalVector.x * this.speed;
this.vy = finalVector.y * this.speed;
// Update position
this.x += this.vx * deltaTime;
this.y += this.vy * deltaTime;
// Keep within bounds
this.x = Math.max(this.size, Math.min(canvas.width - this.size, this.x));
this.y = Math.max(this.size, Math.min(canvas.height - this.size, this.y));
// Update angle for rendering
if (this.vx !== 0 || this.vy !== 0) {
this.angle = Math.atan2(this.vy, this.vx);
}
// Track distance traveled
const dx = this.x - this.lastX;
const dy = this.y - this.lastY;
this.distanceTraveled += Math.sqrt(dx * dx + dy * dy);
this.lastX = this.x;
this.lastY = this.y;
}
draw() {
// Draw sensor lines
ctx.strokeStyle = 'rgba(255, 193, 7, 0.3)';
ctx.lineWidth = 1;
for (let sensor of this.sensors) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(sensor.x, sensor.y);
ctx.stroke();
}
// Draw vehicle
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
// Vehicle body
ctx.fillStyle = '#4CAF50';
ctx.fillRect(-this.size, -this.size/2, this.size * 2, this.size);
// Vehicle direction indicator
ctx.fillStyle = '#fff';
ctx.fillRect(this.size/2, -this.size/4, this.size/2, this.size/2);
ctx.restore();
// Draw path
if (this.path.length > 1) {
ctx.strokeStyle = 'rgba(76, 175, 80, 0.5)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(this.path[0].x, this.path[0].y);
for (let i = 1; i < this.path.length; i++) {
ctx.lineTo(this.path[i].x, this.path[i].y);
}
ctx.stroke();
}
}
}
// Obstacle class
class Obstacle {
constructor(x, y, size = 20) {
this.x = x;
this.y = y;
this.size = size;
}
draw() {
ctx.fillStyle = '#f44336';
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
// Add warning stripes
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(this.x - this.size/2, this.y - this.size/2);
ctx.lineTo(this.x + this.size/2, this.y + this.size/2);
ctx.moveTo(this.x + this.size/2, this.y - this.size/2);
ctx.lineTo(this.x - this.size/2, this.y + this.size/2);
ctx.stroke();
}
}
// Target class
class Target {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 15;
this.pulsePhase = Math.random() * Math.PI * 2;
}
update(deltaTime) {
this.pulsePhase += deltaTime * 0.003;
}
draw() {
const pulse = Math.sin(this.pulsePhase) * 0.3 + 1;
const size = this.size * pulse;
ctx.fillStyle = '#2196F3';
ctx.beginPath();
ctx.arc(this.x, this.y, size, 0, Math.PI * 2);
ctx.fill();
// Target rings
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(this.x, this.y, size * 0.7, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.arc(this.x, this.y, size * 0.4, 0, Math.PI * 2);
ctx.stroke();
}
}
// Game objects
const vehicle = new AutonomousVehicle(100, 100);
const obstacles = [];
const targets = [];
// Stats
const stats = {
distanceTraveled: 0,
targetsReached: 0,
obstaclesAvoided: 0,
decisionsPerSec: 0
};
// Initialize with some default objects
function initializeEnvironment() {
obstacles.length = 0;
targets.length = 0;
// Add some initial obstacles
obstacles.push(new Obstacle(300, 200, 25));
obstacles.push(new Obstacle(500, 350, 30));
obstacles.push(new Obstacle(200, 400, 20));
obstacles.push(new Obstacle(600, 150, 35));
// Add some initial targets
targets.push(new Target(400, 100));
targets.push(new Target(600, 400));
targets.push(new Target(150, 300));
}
// Event handlers
document.getElementById('startBtn').addEventListener('click', () => {
isRunning = true;
animate();
});
document.getElementById('pauseBtn').addEventListener('click', () => {
isRunning = false;
if (animationId) {
cancelAnimationFrame(animationId);
}
});
document.getElementById('resetBtn').addEventListener('click', () => {
isRunning = false;
if (animationId) {
cancelAnimationFrame(animationId);
}
// Reset vehicle
vehicle.x = 100;
vehicle.y = 100;
vehicle.vx = 0;
vehicle.vy = 0;
vehicle.distanceTraveled = 0;
vehicle.path = [];
// Reset stats
stats.targetsReached = 0;
stats.obstaclesAvoided = 0;
decisionCount = 0;
// Reinitialize environment
initializeEnvironment();
// Update display
updateStatsDisplay();
});
document.getElementById('speedSlider').addEventListener('input', (e) => {
vehicle.speed = parseFloat(e.target.value);
document.getElementById('speedValue').textContent = vehicle.speed.toFixed(1);
});
document.getElementById('sensorRange').addEventListener('input', (e) => {
vehicle.sensorRange = parseInt(e.target.value);
document.getElementById('sensorValue').textContent = vehicle.sensorRange;
});
document.getElementById('behaviorSelect').addEventListener('change', (e) => {
vehicle.behavior = e.target.value;
});
document.getElementById('addObstacleBtn').addEventListener('click', () => {
const x = Math.random() * (canvas.width - 100) + 50;
const y = Math.random() * (canvas.height - 100) + 50;
const size = Math.random() * 20 + 15;
obstacles.push(new Obstacle(x, y, size));
});
document.getElementById('addTargetBtn').addEventListener('click', () => {
const x = Math.random() * (canvas.width - 100) + 50;
const y = Math.random() * (canvas.height - 100) + 50;
targets.push(new Target(x, y));
});
document.getElementById('clearBtn').addEventListener('click', () => {
obstacles.length = 0;
targets.length = 0;
});
// Canvas click handler for adding objects
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (e.shiftKey) {
obstacles.push(new Obstacle(x, y, 20));
} else {
targets.push(new Target(x, y));
}
});
// Update stats display
function updateStatsDisplay() {
document.getElementById('distanceTraveled').textContent = Math.round(vehicle.distanceTraveled) + 'm';
document.getElementById('targetsReached').textContent = stats.targetsReached;
document.getElementById('obstaclesAvoided').textContent = stats.obstaclesAvoided;
document.getElementById('decisionsPerSec').textContent = stats.decisionsPerSec;
}
// Main animation loop
function animate(currentTime = 0) {
if (!isRunning) return;
const deltaTime = currentTime - lastTime;
lastTime = currentTime;
// Calculate decisions per second
if (currentTime - lastDecisionTime > 1000) {
stats.decisionsPerSec = Math.round((decisionCount / (currentTime - lastDecisionTime)) * 1000);
decisionCount = 0;
lastDecisionTime = currentTime;
}
// Clear canvas
ctx.fillStyle = '#1a1a2e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update and draw targets
for (let target of targets) {
target.update(deltaTime);
target.draw();
}
// Draw obstacles
for (let obstacle of obstacles) {
obstacle.draw();
}
// Update and draw vehicle
vehicle.update(deltaTime);
vehicle.draw();
// Update stats display
updateStatsDisplay();
animationId = requestAnimationFrame(animate);
}
// Initialize
initializeEnvironment();
animate();
// Add instructions
const instructions = document.createElement('div');
instructions.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 15px;
border-radius: 10px;
font-size: 12px;
max-width: 200px;
z-index: 1000;
`;
instructions.innerHTML = `
<strong>Controls:</strong><br>
• Click to add targets<br>
• Shift+Click to add obstacles<br>
• Use panel controls for settings<br>
• Watch AI make real-time decisions!
`;
document.body.appendChild(instructions);
// Hide instructions after 5 seconds
setTimeout(() => {
instructions.style.opacity = '0';
instructions.style.transition = 'opacity 1s';
setTimeout(() => instructions.remove(), 1000);
}, 5000);
</script>
</body>
</html>