File size: 11,358 Bytes
a440694 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Инерциальный трекер</title>
<!-- Подключаем библиотеку Leaflet.js для карт OpenStreetMap -->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<style>
body, html { margin: 0; padding: 0; height: 100%; font-family: sans-serif; }
#map { height: 60%; width: 100%; }
#info { padding: 10px; height: 35%; overflow-y: auto; background-color: #f0f0f0; }
#info p { margin: 5px 0; }
#startButton {
position: absolute;
top: 70px;
right: 10px;
z-index: 1000;
padding: 10px 15px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
#startButton.active {
background-color: #f44336;
}
</style>
</head>
<body>
<div id="map"></div>
<button id="startButton">Старт</button>
<div id="info">
<h3>Панель данных</h3>
<p><strong>Статус:</strong> Ожидание старта...</p>
<p><strong>Текущие координаты (вычисленные):</strong> <span id="lat">N/A</span>, <span id="lon">N/A</span></p>
<p><strong>Начальные GPS координаты:</strong> <span id="startLat">N/A</span>, <span id="startLon">N/A</span></p>
<p><strong>Скорость (м/с):</strong> X: <span id="velocityX">0</span>, Y: <span id="velocityY">0</span></p>
<p><strong>Ускорение (м/с²):</strong> X: <span id="accelX">0</span>, Y: <span id="accelY">0</span>, Z: <span id="accelZ">0</span></p>
<p><strong>Ориентация:</strong> α (yaw): <span id="alpha">0</span>, β (pitch): <span id="beta">0</span>, γ (roll): <span id="gamma">0</span></p>
<p style="color: red; font-weight: bold;">ВНИМАНИЕ: Позиция будет быстро накапливать ошибку и не будет соответствовать реальности!</p>
</div>
<script>
// --- Инициализация карты Leaflet ---
const map = L.map('map').setView([55.751244, 37.618423], 13); // Центр Москвы по умолчанию
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
let startMarker, currentMarker;
let isTracking = false;
// --- Переменные для инерциальной навигации ---
let lastTimestamp = null;
let velocity = { x: 0, y: 0 }; // Скорость в метрах/секунду (Восток, Север)
let currentPosition = { lat: 0, lon: 0 };
let orientation = { alpha: 0, beta: 0, gamma: 0 }; // Углы ориентации
const startButton = document.getElementById('startButton');
startButton.addEventListener('click', () => {
if (!isTracking) {
startTracking();
} else {
stopTracking();
}
});
function startTracking() {
// 1. Получаем точную начальную позицию через GPS
if (!navigator.geolocation) {
alert('Геолокация не поддерживается вашим браузером');
return;
}
document.querySelector('#info p:nth-child(1)').innerHTML = '<strong>Статус:</strong> Получение начальных GPS координат...';
navigator.geolocation.getCurrentPosition(position => {
currentPosition.lat = position.coords.latitude;
currentPosition.lon = position.coords.longitude;
document.getElementById('startLat').textContent = currentPosition.lat.toFixed(6);
document.getElementById('startLon').textContent = currentPosition.lon.toFixed(6);
// Ставим маркеры
if (startMarker) map.removeLayer(startMarker);
if (currentMarker) map.removeLayer(currentMarker);
startMarker = L.marker([currentPosition.lat, currentPosition.lon]).addTo(map)
.bindPopup('Начальная точка (GPS)').openPopup();
currentMarker = L.marker([currentPosition.lat, currentPosition.lon], {
icon: L.icon({ iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] })
}).addTo(map)
.bindPopup('Текущая точка (ИНС)');
map.setView([currentPosition.lat, currentPosition.lon], 18);
// 2. Начинаем слушать сенсоры
lastTimestamp = performance.now();
window.addEventListener('deviceorientation', handleOrientation);
window.addEventListener('devicemotion', handleMotion);
isTracking = true;
startButton.textContent = 'Стоп';
startButton.classList.add('active');
document.querySelector('#info p:nth-child(1)').innerHTML = '<strong>Статус:</strong> Отслеживание активно...';
}, error => {
alert(`Ошибка получения GPS: ${error.message}`);
document.querySelector('#info p:nth-child(1)').innerHTML = '<strong>Статус:</strong> Ошибка GPS.';
});
}
function stopTracking() {
window.removeEventListener('deviceorientation', handleOrientation);
window.removeEventListener('devicemotion', handleMotion);
isTracking = false;
startButton.textContent = 'Старт';
startButton.classList.remove('active');
document.querySelector('#info p:nth-child(1)').innerHTML = '<strong>Статус:</strong> Остановлено.';
// Сброс скорости
velocity = { x: 0, y: 0 };
}
// --- Обработчики сенсоров ---
function handleOrientation(event) {
// Сохраняем ориентацию устройства
orientation.alpha = event.alpha; // Z-ось (рыскание, yaw)
orientation.beta = event.beta; // X-ось (тангаж, pitch)
orientation.gamma = event.gamma; // Y-ось (крен, roll)
document.getElementById('alpha').textContent = orientation.alpha.toFixed(2);
document.getElementById('beta').textContent = orientation.beta.toFixed(2);
document.getElementById('gamma').textContent = orientation.gamma.toFixed(2);
}
function handleMotion(event) {
if (lastTimestamp === null) {
lastTimestamp = performance.now();
return;
}
const now = performance.now();
const dt = (now - lastTimestamp) / 1000.0; // Дельта времени в секундах
lastTimestamp = now;
// Получаем ускорение устройства, ВКЛЮЧАЯ гравитацию
const accel = event.accelerationIncludingGravity;
// В настоящей ИНС здесь был бы Фильтр Калмана (на основе метода Гаусса)
// для отделения гравитации и получения истинного ускорения.
// Мы делаем очень грубое приближение, используя данные без гравитации, если они доступны
const linearAccel = event.acceleration;
// Для простоты будем использовать линейное ускорение, если оно есть.
// Оно все равно очень неточное.
let ax = linearAccel.x || 0;
let ay = linearAccel.y || 0;
// --- Это критически важный, но очень сложный шаг ---
// Нужно повернуть вектор ускорения из системы координат устройства
// в мировую систему координат (Север, Восток, Вверх).
// Это требует сложных матричных преобразований (кватернионов).
// Для демонстрации мы пропустим этот шаг и будем считать, что
// ay - это движение на Север, а ax - на Восток, что верно, только
// если телефон лежит на столе экраном вверх и "смотрит" на север.
// В реальности это приведет к мгновенной ошибке.
let worldAccel = {
x: ax, // Движение на Восток
y: ay // Движение на Север
};
// Двойное интегрирование (причина накопления ошибки)
// 1. Обновляем скорость
velocity.x += worldAccel.x * dt;
velocity.y += worldAccel.y * dt;
// 2. Обновляем позицию (расстояние в метрах)
const deltaX = velocity.x * dt;
const deltaY = velocity.y * dt;
// Конвертируем смещение в метрах в градусы широты и долготы
const R = 6378137; // Радиус Земли в метрах
const dLat = deltaY / R;
const dLon = deltaX / (R * Math.cos(Math.PI * currentPosition.lat / 180));
currentPosition.lat += dLat * 180 / Math.PI;
currentPosition.lon += dLon * 180 / Math.PI;
// Обновляем информацию на экране
document.getElementById('accelX').textContent = (ax).toFixed(2);
document.getElementById('accelY').textContent = (ay).toFixed(2);
document.getElementById('accelZ').textContent = (linearAccel.z || 0).toFixed(2);
document.getElementById('velocityX').textContent = velocity.x.toFixed(2);
document.getElementById('velocityY').textContent = velocity.y.toFixed(2);
document.getElementById('lat').textContent = currentPosition.lat.toFixed(6);
document.getElementById('lon').textContent = currentPosition.lon.toFixed(6);
// Обновляем позицию маркера на карте
currentMarker.setLatLng([currentPosition.lat, currentPosition.lon]);
// map.panTo([currentPosition.lat, currentPosition.lon]); // Раскомментируйте, чтобы карта следовала за маркером
}
</script>
</body>
</html> |