Speed Max einstellbar
This commit is contained in:
@@ -36,6 +36,8 @@ then
|
|||||||
rosparam set /controller logitech-F710
|
rosparam set /controller logitech-F710
|
||||||
rosparam set /delay_seconds 0.0
|
rosparam set /delay_seconds 0.0
|
||||||
echo 0.0 > /tmp/exomy_delay.txt
|
echo 0.0 > /tmp/exomy_delay.txt
|
||||||
|
rosparam set /speed_limit_percent 100
|
||||||
|
echo 100 > /tmp/exomy_speed_limit.txt
|
||||||
|
|
||||||
/opt/ros/melodic/lib/rosbridge_server/rosbridge_websocket > /tmp/rosbridge.log 2>&1 &
|
/opt/ros/melodic/lib/rosbridge_server/rosbridge_websocket > /tmp/rosbridge.log 2>&1 &
|
||||||
ROSBRIDGE_PID=$!
|
ROSBRIDGE_PID=$!
|
||||||
|
|||||||
@@ -101,6 +101,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="admin_section">
|
||||||
|
<div class="admin_section_header">
|
||||||
|
<p class="eyebrow">Geschwindigkeit</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin_quick_link_card">
|
||||||
|
<div>
|
||||||
|
<strong>Maximale Geschwindigkeit</strong>
|
||||||
|
<p>Begrenzt die Höchstgeschwindigkeit des Rovers für Controller und Web-GUI.</p>
|
||||||
|
</div>
|
||||||
|
<div class="delay_control">
|
||||||
|
<select id="speed_limit_select" class="delay_select" onchange="setSpeedLimit(parseInt(this.value))">
|
||||||
|
<option value="10">10 %</option>
|
||||||
|
<option value="20">20 %</option>
|
||||||
|
<option value="30">30 %</option>
|
||||||
|
<option value="40">40 %</option>
|
||||||
|
<option value="50">50 %</option>
|
||||||
|
<option value="60">60 %</option>
|
||||||
|
<option value="70">70 %</option>
|
||||||
|
<option value="80">80 %</option>
|
||||||
|
<option value="90">90 %</option>
|
||||||
|
<option value="100" selected>100 % – keine Begrenzung</option>
|
||||||
|
</select>
|
||||||
|
<span id="speed_limit_status" class="delay_status_text"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="admin_section">
|
<div class="admin_section">
|
||||||
<div class="admin_section_header">
|
<div class="admin_section_header">
|
||||||
<p class="eyebrow">Systemstatus</p>
|
<p class="eyebrow">Systemstatus</p>
|
||||||
@@ -304,6 +331,33 @@
|
|||||||
if (sel) sel.value = String(seconds);
|
if (sel) sel.value = String(seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchSpeedLimit() {
|
||||||
|
try {
|
||||||
|
var response = await fetch(adminApiBase + '/api/speed-limit');
|
||||||
|
if (!response.ok) return;
|
||||||
|
var data = await response.json();
|
||||||
|
var sel = document.getElementById('speed_limit_select');
|
||||||
|
if (sel) sel.value = String(data.speed_limit_percent);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setSpeedLimit(percent) {
|
||||||
|
var statusEl = document.getElementById('speed_limit_status');
|
||||||
|
if (statusEl) statusEl.textContent = 'Setze…';
|
||||||
|
try {
|
||||||
|
var response = await fetch(adminApiBase + '/api/speed-limit', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({speed_limit_percent: percent})
|
||||||
|
});
|
||||||
|
var data = await response.json();
|
||||||
|
if (statusEl) statusEl.textContent = data.status || 'Gesetzt';
|
||||||
|
window.setTimeout(function() { if (statusEl) statusEl.textContent = ''; }, 2000);
|
||||||
|
} catch (e) {
|
||||||
|
if (statusEl) statusEl.textContent = 'Fehler';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchDelay() {
|
async function fetchDelay() {
|
||||||
try {
|
try {
|
||||||
var response = await fetch(adminApiBase + '/api/delay');
|
var response = await fetch(adminApiBase + '/api/delay');
|
||||||
@@ -351,8 +405,10 @@
|
|||||||
|
|
||||||
fetchStatus();
|
fetchStatus();
|
||||||
fetchDelay();
|
fetchDelay();
|
||||||
|
fetchSpeedLimit();
|
||||||
window.setInterval(fetchStatus, 10000);
|
window.setInterval(fetchStatus, 10000);
|
||||||
window.setInterval(fetchDelay, 5000);
|
window.setInterval(fetchDelay, 5000);
|
||||||
|
window.setInterval(fetchSpeedLimit, 5000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import time
|
|||||||
PORT = 8082
|
PORT = 8082
|
||||||
EXOMY_CONTAINER = 'exomy_autostart'
|
EXOMY_CONTAINER = 'exomy_autostart'
|
||||||
DELAY_FILE = '/tmp/exomy_delay.txt'
|
DELAY_FILE = '/tmp/exomy_delay.txt'
|
||||||
|
SPEED_LIMIT_FILE = '/tmp/exomy_speed_limit.txt'
|
||||||
MOTOR_TEST_LOCK = threading.Lock()
|
MOTOR_TEST_LOCK = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
@@ -251,6 +252,33 @@ def set_delay(seconds):
|
|||||||
return seconds
|
return seconds
|
||||||
|
|
||||||
|
|
||||||
|
def get_speed_limit():
|
||||||
|
try:
|
||||||
|
with open(SPEED_LIMIT_FILE) as f:
|
||||||
|
return max(10, min(100, int(f.read().strip())))
|
||||||
|
except Exception:
|
||||||
|
return 100
|
||||||
|
|
||||||
|
|
||||||
|
def set_speed_limit(percent):
|
||||||
|
percent = max(10, min(100, int(percent)))
|
||||||
|
data = str(percent).encode()
|
||||||
|
try:
|
||||||
|
fd = os.open(SPEED_LIMIT_FILE, os.O_WRONLY | os.O_TRUNC)
|
||||||
|
except FileNotFoundError:
|
||||||
|
fd = os.open(SPEED_LIMIT_FILE, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o666)
|
||||||
|
try:
|
||||||
|
os.write(fd, data)
|
||||||
|
finally:
|
||||||
|
os.close(fd)
|
||||||
|
subprocess.run(
|
||||||
|
['docker', 'exec', EXOMY_CONTAINER, 'bash', '-c',
|
||||||
|
f'source /opt/ros/melodic/setup.bash && rosparam set /speed_limit_percent {percent}'],
|
||||||
|
capture_output=True, check=False
|
||||||
|
)
|
||||||
|
return percent
|
||||||
|
|
||||||
|
|
||||||
def rumble_controller(duration_ms=400):
|
def rumble_controller(duration_ms=400):
|
||||||
# Linux FF constants
|
# Linux FF constants
|
||||||
EV_FF = 0x15
|
EV_FF = 0x15
|
||||||
@@ -539,6 +567,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
if self.path == '/api/delay':
|
if self.path == '/api/delay':
|
||||||
self.write_json(200, {'delay_seconds': get_delay()})
|
self.write_json(200, {'delay_seconds': get_delay()})
|
||||||
return
|
return
|
||||||
|
if self.path == '/api/speed-limit':
|
||||||
|
self.write_json(200, {'speed_limit_percent': get_speed_limit()})
|
||||||
|
return
|
||||||
if self.path == '/api/status':
|
if self.path == '/api/status':
|
||||||
self.write_json(200, collect_status())
|
self.write_json(200, collect_status())
|
||||||
return
|
return
|
||||||
@@ -577,6 +608,24 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
self.write_json(500, {'status': str(exc)})
|
self.write_json(500, {'status': str(exc)})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.path == '/api/speed-limit':
|
||||||
|
try:
|
||||||
|
content_length = int(self.headers.get('Content-Length', '0'))
|
||||||
|
except ValueError:
|
||||||
|
content_length = 0
|
||||||
|
raw_body = self.rfile.read(content_length) if content_length > 0 else b'{}'
|
||||||
|
try:
|
||||||
|
payload = json.loads(raw_body.decode('utf-8'))
|
||||||
|
except (UnicodeDecodeError, json.JSONDecodeError):
|
||||||
|
self.write_json(400, {'status': 'Ungültige Anfrage'})
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
percent = set_speed_limit(payload.get('speed_limit_percent', 100))
|
||||||
|
self.write_json(200, {'status': 'Geschwindigkeitslimit gesetzt', 'speed_limit_percent': percent})
|
||||||
|
except Exception as exc:
|
||||||
|
self.write_json(500, {'status': str(exc)})
|
||||||
|
return
|
||||||
|
|
||||||
if self.path == '/api/motor-test/drive-neutral/preview':
|
if self.path == '/api/motor-test/drive-neutral/preview':
|
||||||
try:
|
try:
|
||||||
content_length = int(self.headers.get('Content-Length', '0'))
|
content_length = int(self.headers.get('Content-Length', '0'))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import math
|
|||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
import rospy
|
import rospy
|
||||||
from sensor_msgs.msg import Joy
|
from sensor_msgs.msg import Joy
|
||||||
@@ -21,6 +22,8 @@ global last_locomotion_mode
|
|||||||
locomotion_mode = LocomotionMode.ACKERMANN.value
|
locomotion_mode = LocomotionMode.ACKERMANN.value
|
||||||
last_locomotion_mode = locomotion_mode
|
last_locomotion_mode = locomotion_mode
|
||||||
motors_enabled = True
|
motors_enabled = True
|
||||||
|
_speed_limit = 100
|
||||||
|
_speed_limit_last_read = 0.0
|
||||||
AXIS_DEADZONE = 0.1
|
AXIS_DEADZONE = 0.1
|
||||||
last_start_button_pressed = False
|
last_start_button_pressed = False
|
||||||
last_webgui_time = None
|
last_webgui_time = None
|
||||||
@@ -72,6 +75,18 @@ CONTROLLER_FUNCTION_MAPS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_speed_limit():
|
||||||
|
global _speed_limit, _speed_limit_last_read
|
||||||
|
now = time.time()
|
||||||
|
if now - _speed_limit_last_read > 2.0:
|
||||||
|
try:
|
||||||
|
_speed_limit = int(rospy.get_param('/speed_limit_percent', 100))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_speed_limit_last_read = now
|
||||||
|
return _speed_limit
|
||||||
|
|
||||||
|
|
||||||
def _rumble_thread(duration_ms):
|
def _rumble_thread(duration_ms):
|
||||||
EV_FF = 0x15
|
EV_FF = 0x15
|
||||||
FF_RUMBLE = 0x50
|
FF_RUMBLE = 0x50
|
||||||
@@ -229,9 +244,9 @@ def callback(data):
|
|||||||
|
|
||||||
rover_cmd.motors_enabled = motors_enabled
|
rover_cmd.motors_enabled = motors_enabled
|
||||||
|
|
||||||
# The velocity is decoded as value between 0...100
|
# The velocity is decoded as value between 0...100, capped by speed limit
|
||||||
stick_length = min(math.sqrt(x*x + y*y), 1.0)
|
stick_length = min(math.sqrt(x*x + y*y), 1.0)
|
||||||
rover_cmd.vel = int(100 * math.pow(stick_length, VELOCITY_EXPO))
|
rover_cmd.vel = int(get_speed_limit() * math.pow(stick_length, VELOCITY_EXPO))
|
||||||
|
|
||||||
# The steering is described as an angle between -180...180
|
# The steering is described as an angle between -180...180
|
||||||
# Which describe the joystick position as follows:
|
# Which describe the joystick position as follows:
|
||||||
|
|||||||
Reference in New Issue
Block a user