Initial Version
This commit is contained in:
commit
0ee065f899
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
build/
|
||||||
|
log/
|
||||||
|
install/
|
||||||
9
README.md
Normal file
9
README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
sudo apt-get update && sudo apt-get install python3-pip -y && pip3 install websockets
|
||||||
|
|
||||||
|
colcon build
|
||||||
|
ros2 run simulator sim
|
||||||
|
|
||||||
|
firefox src/simulator/simulator/avai.html
|
||||||
|
|
||||||
|
-> Distance published as Float64 on /distance
|
||||||
|
-> inputPower subscribed as Float64 on /inputPower (0-100)
|
||||||
18
src/simulator/package.xml
Normal file
18
src/simulator/package.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>simulator</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>Basic simulator for testing a controller</description>
|
||||||
|
<maintainer email="nicolas.kuhl@rub.de">Nicolas Kuhl</maintainer>
|
||||||
|
<license>Apache License 2.0</license>
|
||||||
|
|
||||||
|
<test_depend>ament_copyright</test_depend>
|
||||||
|
<test_depend>ament_flake8</test_depend>
|
||||||
|
<test_depend>ament_pep257</test_depend>
|
||||||
|
<test_depend>python3-pytest</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_python</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
0
src/simulator/resource/simulator
Normal file
0
src/simulator/resource/simulator
Normal file
4
src/simulator/setup.cfg
Normal file
4
src/simulator/setup.cfg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[develop]
|
||||||
|
script-dir=$base/lib/simulator
|
||||||
|
[install]
|
||||||
|
install-scripts=$base/lib/simulator
|
||||||
26
src/simulator/setup.py
Normal file
26
src/simulator/setup.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
package_name = 'simulator'
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name=package_name,
|
||||||
|
version='0.0.0',
|
||||||
|
packages=[package_name],
|
||||||
|
data_files=[
|
||||||
|
('share/ament_index/resource_index/packages',
|
||||||
|
['resource/' + package_name]),
|
||||||
|
('share/' + package_name, ['package.xml']),
|
||||||
|
],
|
||||||
|
install_requires=['setuptools'],
|
||||||
|
zip_safe=True,
|
||||||
|
maintainer='ubuntu',
|
||||||
|
maintainer_email='avai@unconfigured.de',
|
||||||
|
description='TODO: Package description',
|
||||||
|
license='TODO: License declaration',
|
||||||
|
tests_require=['pytest'],
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'sim = simulator.sim:main'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
0
src/simulator/simulator/__init__.py
Normal file
0
src/simulator/simulator/__init__.py
Normal file
260
src/simulator/simulator/avai.html
Normal file
260
src/simulator/simulator/avai.html
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<html>
|
||||||
|
<title>AVAI-Controller-Game</title>
|
||||||
|
<body>
|
||||||
|
<canvas id="main" width="960px" height="540px"></canvas>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="vp">ViewPortWidth: <span id="vpv">960</span></label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="vp" name="vp" min="960" max="3000">
|
||||||
|
<br/>
|
||||||
|
<label for="ppm">PixelsPerMeter: <span id="ppmv">10</span></label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="ppm" name="ppm" min="1" max="25">
|
||||||
|
<br/>
|
||||||
|
<label for="off">Offset: <span id="offv">75</span>m</label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="off" name="off" min="0" max="200">
|
||||||
|
<br/>
|
||||||
|
<label for="leadAcc">Leading acceleration: <span id="leadAccv">0</span></label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="leadAcc" name="leadAcc" min="0" max="100">
|
||||||
|
<br/>
|
||||||
|
<label for="meanDist">Ideal distance: <span id="meanDistv">-10</span>m</label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="meanDist" name="meanDist" min="-250" max="0">
|
||||||
|
<br/>
|
||||||
|
<label for="variation">Allowed Variation: <span id="variationv">10</span>m</label>
|
||||||
|
<br/>
|
||||||
|
<input type="range" id="variation" name="variation" min="0" max="50">
|
||||||
|
<br/>
|
||||||
|
<input type="checkbox" id="autoct" name="autoct">
|
||||||
|
<label for="autoct">Ideal distance based on speed of follower</label>
|
||||||
|
<br/>
|
||||||
|
<input type="checkbox" id="hardfail" name="hardfail">
|
||||||
|
<label for="hardfail">Enable hardfail mode</label>
|
||||||
|
<br/>
|
||||||
|
<input type="button" value="START" id="start"/>
|
||||||
|
<input type="button" value="STOP" id="stop"/>
|
||||||
|
<input type="button" value="RESET" id="reset"/>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var vp = document.getElementById("vp");
|
||||||
|
var vpv = document.getElementById("vpv");
|
||||||
|
|
||||||
|
vp.value = 960;
|
||||||
|
vp.addEventListener("input", (event) => {
|
||||||
|
vpv.innerHTML = vp.value;
|
||||||
|
canv.width = vp.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
var ppm = document.getElementById("ppm");
|
||||||
|
var ppmv = document.getElementById("ppmv");
|
||||||
|
|
||||||
|
ppm.value = 10;
|
||||||
|
ppm.addEventListener("input", (event) => {
|
||||||
|
ppmv.innerHTML = ppm.value;
|
||||||
|
|
||||||
|
renderOffset = renderOffset/oneMeter * ppm.value;
|
||||||
|
clearZoneCenter = clearZoneCenter/oneMeter * ppm.value;
|
||||||
|
clearZoneVariation = clearZoneVariation/oneMeter * ppm.value;
|
||||||
|
followX = followX/oneMeter * ppm.value;
|
||||||
|
|
||||||
|
oneMeter = ppm.value;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var off = document.getElementById("off");
|
||||||
|
var offv = document.getElementById("offv");
|
||||||
|
|
||||||
|
off.value = 75;
|
||||||
|
off.addEventListener("input", (event) => {
|
||||||
|
offv.innerHTML = off.value;
|
||||||
|
renderOffset = off.value * oneMeter;
|
||||||
|
});
|
||||||
|
|
||||||
|
var leadAcc = document.getElementById("leadAcc");
|
||||||
|
var leadAccv = document.getElementById("leadAccv");
|
||||||
|
|
||||||
|
leadAcc.value = 0;
|
||||||
|
leadAcc.addEventListener("input", (event) => {
|
||||||
|
leadAccv.innerHTML = leadAcc.value;
|
||||||
|
leadAccSlid = leadAcc.value;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var meanDist = document.getElementById("meanDist");
|
||||||
|
var meanDistv = document.getElementById("meanDistv");
|
||||||
|
|
||||||
|
meanDist.value = -10;
|
||||||
|
meanDist.addEventListener("input", (event) => {
|
||||||
|
meanDistv.innerHTML = meanDist.value;
|
||||||
|
if(!autocenter) clearZoneCenter = meanDist.value * oneMeter;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var variation = document.getElementById("variation");
|
||||||
|
var variationv = document.getElementById("variationv");
|
||||||
|
|
||||||
|
variation.value = 10;
|
||||||
|
variation.addEventListener("input", (event) => {
|
||||||
|
variationv.innerHTML = variation.value;
|
||||||
|
clearZoneVariation = variation.value * oneMeter;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var autoct = document.getElementById("autoct");
|
||||||
|
|
||||||
|
autoct.addEventListener("input", (event) => {
|
||||||
|
autocenter = autoct.checked;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var hardf = document.getElementById("hardfail");
|
||||||
|
|
||||||
|
hardf.addEventListener("input", (event) => {
|
||||||
|
hardfail = hardf.checked;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var start = document.getElementById("start");
|
||||||
|
|
||||||
|
start.addEventListener("click", (event) => {
|
||||||
|
runS = true;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
var stop = document.getElementById("stop");
|
||||||
|
|
||||||
|
stop.addEventListener("click", (event) => {
|
||||||
|
runS = false;
|
||||||
|
postSettings(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var reset = document.getElementById("reset");
|
||||||
|
|
||||||
|
reset.addEventListener("click", (event) => {
|
||||||
|
postSettings(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var sock = new WebSocket("ws://127.0.0.1:6123");
|
||||||
|
sock.addEventListener('open', (event) => {
|
||||||
|
congood = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.addEventListener('message', (event) => {
|
||||||
|
console.log(event.data);
|
||||||
|
var js = JSON.parse(event.data);
|
||||||
|
spdLD = js.leadingVelocityMpS;
|
||||||
|
spdFL = js.followingVelocityMpS;
|
||||||
|
leadX = js.leadingPosMeter * oneMeter;
|
||||||
|
followX = js.followingPosMeter * oneMeter;
|
||||||
|
if(autocenter){
|
||||||
|
clearZoneCenter = js.clearZoneCenter * oneMeter;
|
||||||
|
meanDist.value = js.clearZoneCenter;
|
||||||
|
meanDistv.value = js.clearZoneCenter;
|
||||||
|
}
|
||||||
|
inBounds = js.inBounds;
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.addEventListener('close', (event) => {
|
||||||
|
congood = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var autocenter = autoct.checked;
|
||||||
|
var hardfail = hardf.checked;
|
||||||
|
var runS = false;
|
||||||
|
var congood = false;
|
||||||
|
var inBounds = true;
|
||||||
|
var leadAccSlid = 0;
|
||||||
|
|
||||||
|
var canv = document.getElementById("main")
|
||||||
|
var ctx = canv.getContext("2d")
|
||||||
|
|
||||||
|
var oneMeter = 10;
|
||||||
|
|
||||||
|
var leadX = 0;
|
||||||
|
var spdLD = 2.21;
|
||||||
|
var renderOffset = 75 * oneMeter;
|
||||||
|
|
||||||
|
var followX = -10 * oneMeter;
|
||||||
|
var spdFL = 0;
|
||||||
|
|
||||||
|
var clearZoneCenter = -10 * oneMeter;
|
||||||
|
var clearZoneVariation = 10 * oneMeter;
|
||||||
|
|
||||||
|
function sleep(milliseconds) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
function postSettings(reset){
|
||||||
|
var json = {autocenter: autocenter, hardfail: hardfail, runS: runS, reset: reset, clearZoneCenter: meanDist.value, clearZoneVariation: variation.value, leadAccSlid: leadAccSlid};
|
||||||
|
sock.send(JSON.stringify(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTrack(){
|
||||||
|
ctx.fillStyle = "#073642"
|
||||||
|
ctx.fillRect(0,0,canv.width,canv.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = inBounds ? "#859900A0":"#cb4b16A0"
|
||||||
|
zoneCut = 0;
|
||||||
|
if(renderOffset + clearZoneCenter + clearZoneVariation > renderOffset) zoneCut = clearZoneCenter + clearZoneVariation;
|
||||||
|
ctx.fillRect(renderOffset + clearZoneCenter - clearZoneVariation, (canv.height-(96 - 48 + 32 + 48))/2,clearZoneVariation*2 - zoneCut,64);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#000"
|
||||||
|
ctx.fillRect(renderOffset,(canv.height-(96 - 48 + 32 + 16))/2,32,32);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#000"
|
||||||
|
ctx.fillRect(renderOffset - (leadX-followX)-32,(canv.height-(96 - 48 + 32 + 16))/2,32,32);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#b58900"
|
||||||
|
ctx.fillRect(renderOffset - (leadX-followX),(canv.height-(96 - 48 + 4 + 16))/2,renderOffset - (renderOffset - (leadX-followX)),4);
|
||||||
|
ctx.font = "20px sans-serif";
|
||||||
|
|
||||||
|
var dstLen = (renderOffset - (renderOffset - (leadX-followX)))/oneMeter;
|
||||||
|
var dstStr = (Math.round(dstLen * 100) / 100).toFixed(2) + "m";
|
||||||
|
|
||||||
|
ctx.fillText(dstStr,Math.max(0,(leadX-followX)/2 + renderOffset - (leadX-followX) - ctx.measureText(dstStr).width/2), (canv.height-(96 - 48 + 16 + 16))/2);
|
||||||
|
|
||||||
|
for(var i = 0; i < Math.ceil(canv.width/100.0) + 1; i++){
|
||||||
|
ctx.fillStyle = "#dc322f";
|
||||||
|
ctx.fillRect((i-1)*100 + (100 - (leadX % 100)),16,50,48);
|
||||||
|
ctx.fillRect((i-1)*100 + (100 - (leadX % 100)),canv.height-(48 + 96),50,48);
|
||||||
|
ctx.fillStyle = "#fdf6e3";
|
||||||
|
ctx.fillRect((i-1)*100 + 50 + (100 - (leadX % 100)),16,50,48);
|
||||||
|
ctx.fillRect((i-1)*100 + 50 + (100 - (leadX % 100)),canv.height-(48 + 96),50,48);
|
||||||
|
}
|
||||||
|
var inval = 25 * oneMeter;
|
||||||
|
for(var i = 0; i < Math.ceil(canv.width/inval) + 1; i++){
|
||||||
|
ctx.font = "32px sans-serif";
|
||||||
|
ctx.fillStyle = "#657b83";
|
||||||
|
ctx.fillRect(i*inval - inval + (inval - (leadX % inval)) - 1,canv.height-(48+96),2,85);
|
||||||
|
ctx.fillText(Math.round((((leadX - renderOffset + i*inval - inval + (inval - (leadX % inval)))/oneMeter))) + "m", i*inval - inval + (inval - (leadX % inval)) + 4,canv.height-64);
|
||||||
|
}
|
||||||
|
ctx.font = "20px sans-serif";
|
||||||
|
|
||||||
|
ctx.fillText("Velocity Follow: " + (Math.round(spdFL * 100) / 100).toFixed(2) + "m/s",8,canv.height-8);
|
||||||
|
ctx.fillText("Velocity Leading: " + (Math.round(spdLD * 100) / 100).toFixed(2) + "m/s",8,canv.height-32);
|
||||||
|
ctx.fillText("RUN: " + runS,300,canv.height-8);
|
||||||
|
ctx.fillText("CONGOOD: " + congood,300,canv.height-32);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tickGame(){
|
||||||
|
while(true) {
|
||||||
|
if(hardfail && !inBounds) runS = false;
|
||||||
|
drawTrack();
|
||||||
|
await sleep(17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tickGame();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
273
src/simulator/simulator/sim.py
Normal file
273
src/simulator/simulator/sim.py
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
#!/bin/python3
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from std_msgs.msg import Float64
|
||||||
|
|
||||||
|
import threading
|
||||||
|
from websockets.sync import server
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import json
|
||||||
|
|
||||||
|
controllerInput = 0
|
||||||
|
speedSlider = 0
|
||||||
|
|
||||||
|
clearZoneCenter = 0.0
|
||||||
|
clearZoneVariation = 0.0
|
||||||
|
autoCenter = False
|
||||||
|
|
||||||
|
enableSim = False
|
||||||
|
resetSim = False
|
||||||
|
inBounds = True
|
||||||
|
hardFail = False
|
||||||
|
|
||||||
|
leadingPosMeter = 0.0
|
||||||
|
followingPosMeter = -10.0
|
||||||
|
|
||||||
|
leadingVelocityMpS = 0.0
|
||||||
|
followingVelocityMpS = 0.0
|
||||||
|
|
||||||
|
leadingTimeToMaxSpeedMS = 8000
|
||||||
|
leadingSlowDownFactor = 1.50
|
||||||
|
leadingMaxSpeedMpS = 30
|
||||||
|
|
||||||
|
followingTimeToMaxSpeedMS = 8000
|
||||||
|
followingSlowDownFactor = 1.50
|
||||||
|
followingMaxSpeedMpS = 30
|
||||||
|
|
||||||
|
|
||||||
|
webSock = None
|
||||||
|
def handler(websocket):
|
||||||
|
global webSock
|
||||||
|
while True:
|
||||||
|
data = websocket.recv()
|
||||||
|
handleInboundData(data)
|
||||||
|
webSock = websocket
|
||||||
|
# import pdb; pdb.set_trace()
|
||||||
|
|
||||||
|
def runWebSock():
|
||||||
|
serv = server.serve(handler, "localhost", 6123)
|
||||||
|
serv.serve_forever()
|
||||||
|
|
||||||
|
def handleInboundData(data):
|
||||||
|
js = json.loads(data)
|
||||||
|
# print(js)
|
||||||
|
global speedSlider
|
||||||
|
speedSlider = int(js["leadAccSlid"])
|
||||||
|
global clearZoneCenter
|
||||||
|
if not js["autocenter"]:
|
||||||
|
clearZoneCenter = int(js["clearZoneCenter"])
|
||||||
|
global clearZoneVariation
|
||||||
|
clearZoneVariation = int(js["clearZoneVariation"])
|
||||||
|
global autoCenter
|
||||||
|
autoCenter = js["autocenter"]
|
||||||
|
global enableSim
|
||||||
|
enableSim = js["runS"]
|
||||||
|
global resetSim
|
||||||
|
resetSim = js["reset"]
|
||||||
|
global hardFail
|
||||||
|
hardFail = js["hardfail"]
|
||||||
|
|
||||||
|
def sendUpdate():
|
||||||
|
if webSock is None:
|
||||||
|
return
|
||||||
|
js = {"leadingVelocityMpS": leadingVelocityMpS, "followingVelocityMpS": followingVelocityMpS, "leadingPosMeter": leadingPosMeter, "followingPosMeter": followingPosMeter, "clearZoneCenter": clearZoneCenter, "inBounds": inBounds}
|
||||||
|
res = json.dumps(js)
|
||||||
|
webSock.send(res)
|
||||||
|
|
||||||
|
|
||||||
|
leaderlastLinearComponent = 0
|
||||||
|
leaderlastLinearComponentTS = -1
|
||||||
|
|
||||||
|
def processLeaderCurve():
|
||||||
|
global leaderlastLinearComponent
|
||||||
|
global leaderlastLinearComponentTS
|
||||||
|
# using standard sigmoid curve limited to range 0 - 1
|
||||||
|
# https://www.wolframalpha.com/input?i=1%2F%281%2Be%5E-%28%28x*12%29-6%29%29+from+0+to+1
|
||||||
|
max = leadingTimeToMaxSpeedMS * speedSlider/100
|
||||||
|
timedelta = (time.time()*1000) - leaderlastLinearComponentTS
|
||||||
|
leaderlastLinearComponentTS = time.time()*1000
|
||||||
|
|
||||||
|
if timedelta > leadingTimeToMaxSpeedMS:
|
||||||
|
leaderlastLinearComponent = 0
|
||||||
|
return 0
|
||||||
|
if leaderlastLinearComponent < max:
|
||||||
|
leaderlastLinearComponent += timedelta
|
||||||
|
if leaderlastLinearComponent > max:
|
||||||
|
leaderlastLinearComponent = max
|
||||||
|
|
||||||
|
if leaderlastLinearComponent > max:
|
||||||
|
leaderlastLinearComponent -= timedelta * leadingSlowDownFactor + (leaderlastLinearComponent/leadingTimeToMaxSpeedMS)
|
||||||
|
if leaderlastLinearComponent < 0:
|
||||||
|
leaderlastLinearComponent = 0
|
||||||
|
|
||||||
|
x = leaderlastLinearComponent / leadingTimeToMaxSpeedMS
|
||||||
|
exponent = -((x*12)-6)
|
||||||
|
base = 1 + math.exp(exponent)
|
||||||
|
res = (1 / base) * leadingMaxSpeedMpS
|
||||||
|
|
||||||
|
return 0 if res < 0.08 else res
|
||||||
|
|
||||||
|
followerlastLinearComponent = 0
|
||||||
|
followerlastLinearComponentTS = -1
|
||||||
|
|
||||||
|
def processFollowerCurve():
|
||||||
|
global followerlastLinearComponent
|
||||||
|
global followerlastLinearComponentTS
|
||||||
|
# using standard sigmoid curve limited to range 0 - 1
|
||||||
|
# https://www.wolframalpha.com/input?i=1%2F%281%2Be%5E-%28%28x*12%29-6%29%29+from+0+to+1
|
||||||
|
max = followingTimeToMaxSpeedMS * controllerInput/100
|
||||||
|
timedelta = (time.time()*1000) - followerlastLinearComponentTS
|
||||||
|
followerlastLinearComponentTS = time.time()*1000
|
||||||
|
|
||||||
|
if timedelta > followingTimeToMaxSpeedMS:
|
||||||
|
followerlastLinearComponent = 0
|
||||||
|
return 0
|
||||||
|
if followerlastLinearComponent < max:
|
||||||
|
followerlastLinearComponent += timedelta
|
||||||
|
if followerlastLinearComponent > max:
|
||||||
|
followerlastLinearComponent = max
|
||||||
|
|
||||||
|
if followerlastLinearComponent > max:
|
||||||
|
followerlastLinearComponent -= timedelta * followingSlowDownFactor + (followerlastLinearComponent/followingTimeToMaxSpeedMS)
|
||||||
|
if followerlastLinearComponent < 0:
|
||||||
|
followerlastLinearComponent = 0
|
||||||
|
|
||||||
|
x = followerlastLinearComponent / followingTimeToMaxSpeedMS
|
||||||
|
exponent = -((x*12)-6)
|
||||||
|
base = 1 + math.exp(exponent)
|
||||||
|
res = (1 / base) * followingMaxSpeedMpS
|
||||||
|
|
||||||
|
return 0 if res < 0.08 else res
|
||||||
|
|
||||||
|
def tickDistance():
|
||||||
|
global inBounds
|
||||||
|
global enableSim
|
||||||
|
if leadingPosMeter + clearZoneCenter - clearZoneVariation > followingPosMeter:
|
||||||
|
inBounds = False
|
||||||
|
if hardFail:
|
||||||
|
enableSim = False
|
||||||
|
# print("FAIL")
|
||||||
|
return
|
||||||
|
if leadingPosMeter + clearZoneCenter + clearZoneVariation < followingPosMeter:
|
||||||
|
inBounds = False
|
||||||
|
if hardFail:
|
||||||
|
enableSim = False
|
||||||
|
# print("FAIL")
|
||||||
|
return
|
||||||
|
if leadingPosMeter < followingPosMeter:
|
||||||
|
inBounds = False
|
||||||
|
if hardFail:
|
||||||
|
enableSim = False
|
||||||
|
# print("FAIL")
|
||||||
|
return
|
||||||
|
inBounds = True
|
||||||
|
|
||||||
|
|
||||||
|
lastRunTimeStamp = time.time() * 1000
|
||||||
|
|
||||||
|
def tickPhysics():
|
||||||
|
print("Starting physics tick")
|
||||||
|
|
||||||
|
runCtr = 0
|
||||||
|
speedArray = []
|
||||||
|
global leadingPosMeter
|
||||||
|
global followingPosMeter
|
||||||
|
global resetSim
|
||||||
|
while True:
|
||||||
|
if resetSim:
|
||||||
|
global enableSim
|
||||||
|
enableSim = False
|
||||||
|
global inBounds
|
||||||
|
inBounds = True
|
||||||
|
global leadingPosMeter
|
||||||
|
leadingPosMeter = 0
|
||||||
|
global followingPosMeter
|
||||||
|
followingPosMeter = -10
|
||||||
|
global leaderlastLinearComponent
|
||||||
|
leaderlastLinearComponent = 0
|
||||||
|
global leaderlastLinearComponentTS
|
||||||
|
leaderlastLinearComponent = -1
|
||||||
|
global followerlastLinearComponent
|
||||||
|
followerlastLinearComponent = 0
|
||||||
|
global followerlastLinearComponentTS
|
||||||
|
followerlastLinearComponent = -1
|
||||||
|
global leadingVelocityMpS
|
||||||
|
leadingVelocityMpS = 0
|
||||||
|
global followingVelocityMpS
|
||||||
|
followingVelocityMpS = 0
|
||||||
|
speedArray.clear()
|
||||||
|
runCtr = 0
|
||||||
|
resetSim = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if autoCenter:
|
||||||
|
global clearZoneCenter
|
||||||
|
clearZoneCenter = -1 * followingVelocityMpS * 3.6 / 2
|
||||||
|
|
||||||
|
if not enableSim and len(speedArray) > 0:
|
||||||
|
print(speedArray)
|
||||||
|
speedArray.clear()
|
||||||
|
|
||||||
|
if not enableSim:
|
||||||
|
time.sleep(.100)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
time.sleep(.01)
|
||||||
|
runCtr += 1
|
||||||
|
|
||||||
|
if runCtr % 20 == 0:
|
||||||
|
speedArray.append(speedSlider)
|
||||||
|
|
||||||
|
leadingVelocityMpS = processLeaderCurve()
|
||||||
|
|
||||||
|
followingVelocityMpS = processFollowerCurve()
|
||||||
|
|
||||||
|
global lastRunTimeStamp
|
||||||
|
timedelta = (time.time()*1000) - lastRunTimeStamp
|
||||||
|
lastRunTimeStamp = time.time() * 1000
|
||||||
|
|
||||||
|
portion = timedelta * leadingVelocityMpS/1000
|
||||||
|
leadingPosMeter += portion
|
||||||
|
|
||||||
|
portion = timedelta * followingVelocityMpS/1000
|
||||||
|
followingPosMeter += portion
|
||||||
|
|
||||||
|
tickDistance()
|
||||||
|
sendUpdate()
|
||||||
|
|
||||||
|
|
||||||
|
class Sim(Node):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('sim')
|
||||||
|
self.publisher_ = self.create_publisher(Float64, 'distance', 10)
|
||||||
|
self.timer = self.create_timer(0.05, self.callback)
|
||||||
|
self.subscription = self.create_subscription(Float64,'inputPower', self.call, 10)
|
||||||
|
|
||||||
|
def call(self, msg):
|
||||||
|
global controllerInput
|
||||||
|
controllerInput = max(min(msg.data,100),0)
|
||||||
|
|
||||||
|
def callback(self):
|
||||||
|
msg = Float64()
|
||||||
|
msg.data = (leadingPosMeter - followingPosMeter) * 1.00
|
||||||
|
self.publisher_.publish(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
thr = threading.Thread(target=runWebSock)
|
||||||
|
thr.start()
|
||||||
|
|
||||||
|
thr2 = threading.Thread(target=tickPhysics)
|
||||||
|
thr2.start()
|
||||||
|
|
||||||
|
sim = Sim()
|
||||||
|
rclpy.spin(sim)
|
||||||
|
|
||||||
|
pub.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
23
src/simulator/test/test_copyright.py
Normal file
23
src/simulator/test/test_copyright.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ament_copyright.main import main
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.copyright
|
||||||
|
@pytest.mark.linter
|
||||||
|
def test_copyright():
|
||||||
|
rc = main(argv=['.', 'test'])
|
||||||
|
assert rc == 0, 'Found errors'
|
||||||
25
src/simulator/test/test_flake8.py
Normal file
25
src/simulator/test/test_flake8.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ament_flake8.main import main_with_errors
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.flake8
|
||||||
|
@pytest.mark.linter
|
||||||
|
def test_flake8():
|
||||||
|
rc, errors = main_with_errors(argv=[])
|
||||||
|
assert rc == 0, \
|
||||||
|
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||||
|
'\n'.join(errors)
|
||||||
23
src/simulator/test/test_pep257.py
Normal file
23
src/simulator/test/test_pep257.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ament_pep257.main import main
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.linter
|
||||||
|
@pytest.mark.pep257
|
||||||
|
def test_pep257():
|
||||||
|
rc = main(argv=['.', 'test'])
|
||||||
|
assert rc == 0, 'Found code style errors / warnings'
|
||||||
Loading…
Reference in New Issue
Block a user