Driving on a Mobius Strip

AutomotiveDrivingModels is templated to efficiently run different types of simulations. Entities are parameterized by their:

  • S state, which changes over time
  • D defintion, which does not change over time
  • I id, typically an Int, which uniquely identifies that entity.

In addition to these types, the actions, environment and the driver models can also be parameterized.

This notebook demonstrates a longitudinal driving simulation where cars drive on a single straight lane that loops back on itself. The types are:

  • S - State1D, containing the position and speed
  • D - VehicleDef, containing length, width, and class
  • I - Int

We use a StraightRoadway as our environment and LaneFollowingDrivers that produce LaneFollowingAccels.


In [1]:
using AutomotiveDrivingModels
using AutoViz


┌ Info: Recompiling stale cache file C:\Users\Maxime\.julia\compiled\v1.0\AutoViz\w0rHu.ji for AutoViz [82aa6e0c-a491-5edf-8d4b-c16b98e4ea17]
└ @ Base loading.jl:1184
┌ Warning: Package AutoViz does not have AutomotiveDrivingModels in its dependencies:
│ - If you have AutoViz checked out for development and have
│   added AutomotiveDrivingModels as a dependency but haven't updated your primary
│   environment's manifest file, try `Pkg.resolve()`.
│ - Otherwise you may need to report an issue with AutoViz
└ Loading AutomotiveDrivingModels into AutoViz from project dependency, future warnings for AutoViz are suppressed.

In [2]:
road_length = 200.0 # [meters]
roadway = StraightRoadway(road_length)

scene = Scene1D()
push!(scene, Entity(State1D(10.0,  8.0), VehicleDef(), 1))
push!(scene, Entity(State1D(50.0, 12.5), VehicleDef(), 2))
push!(scene, Entity(State1D(150.0, 6.0), VehicleDef(), 3))

cam = StaticCamera(VecE2(100.0,0.0), 4.75)
render(scene, roadway, cam=cam, canvas_height=100)


Out[2]:

We can add an overlay that displays the car id for convenience:


In [3]:
overlays = [TextOverlay(text=["$(veh.id)"], incameraframe=true, pos=VecE2(veh.state.s-0.7, 3)) for veh in scene]
render(scene, roadway, overlays, cam=cam, canvas_height=100)


Out[3]:

Or we can create a new SceneOverlay which does this for us:


In [4]:
using Parameters
@with_kw struct CarIDOverlay <: SceneOverlay
    textparams::TextParams=TextParams()
end
function AutoViz.render!(rendermodel::RenderModel, overlay::CarIDOverlay, scene::Scene1D, roadway::StraightRoadway)
    for veh in scene
        x = veh.state.s - 0.7
        y = 3.0
        text = string(veh.id)
        add_instruction!(rendermodel, render_text, (text, x, y, overlay.textparams.size, overlay.textparams.color), incameraframe=true)
    end
    return rendermodel
end

render(scene, roadway, [CarIDOverlay()], cam=cam, canvas_height=100)


Out[4]:

To run a simulation we need driving models that produce actions. For this we will use LaneFollowingDrivers that produce LaneFollowingAccels. We will give each car a different model, as a demonstration.


In [5]:
models = Dict{Int, LaneFollowingDriver}()
models[1] = StaticLaneFollowingDriver(0.0) # always produce zero acceleration
models[2] = IntelligentDriverModel(v_des=12.0) # default IDM with a desired speed of 12 m/s
models[3] = PrincetonDriver(v_des = 10.0) # default Princeton driver with a desired speed of 10m/s

nticks = 100
timestep = 0.1
rec = QueueRecord(Vehicle1D, nticks+1, timestep)
simulate!(LaneFollowingAccel, rec, scene, roadway, models, nticks)
render(rec[0], roadway, [CarIDOverlay()], cam=cam, canvas_height=100)


Out[5]:

We can use Interact to inspect the simulation.


In [6]:
using Interact
@manipulate for frame_index in 1 : nframes(rec)
    render(rec[frame_index-nframes(rec)], roadway, [CarIDOverlay()], cam=cam, canvas_height=100)
end


Out[6]:
WebIO.mount(this.previousSibling,{"props":{},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{"className":"field"},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{},"nodeType":"Scope","type":"node","instanceArgs":{"imports":{"data":[{"name":"knockout","type":"js","url":"/assetserver/7727d7cb3453b9ff588804f2f297360e756850e6-knockout.js"},{"name":"knockout_punches","type":"js","url":"/assetserver/dc191505b241da77a1bd06986b7132036babe315-knockout_punches.js"},{"name":null,"type":"js","url":"/assetserver/cf39e83f3445855334efae1f7d0ee012952fe319-all.js"},{"name":null,"type":"css","url":"/assetserver/495af69608d773e6cf6efd8fe98ee33ddbad0df5-style.css"},{"name":null,"type":"css","url":"/assetserver/9d22e7e1c4dccb3a3bef6ae4d24134fd256d4bfd-main.css"}],"type":"async_block"},"id":"knockout-component-fddc2108-64dc-441f-be9a-4530cb07f790","handlers":{"_promises":{"importsLoaded":[function (ko, koPunches) { ko.punches.enableAll(); ko.bindingHandlers.numericValue = { init : function(element, valueAccessor, allBindings, data, context) { var stringified = ko.observable(ko.unwrap(valueAccessor())); stringified.subscribe(function(value) { var val = parseFloat(value); if (!isNaN(val)) { valueAccessor()(val); } }) valueAccessor().subscribe(function(value) { var str = JSON.stringify(value); if ((str == "0") && (["-0", "-0."].indexOf(stringified()) >= 0)) return; if (["null", ""].indexOf(str) >= 0) return; stringified(str); }) ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context); } }; var json_data = JSON.parse("{\"changes\":0,\"value\":51}"); var self = this; function AppViewModel() { for (var key in json_data) { var el = json_data[key]; this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el); } [this["changes"].subscribe((function (val){!(this.valueFromJulia["changes"]) ? (WebIO.setval({"name":"changes","scope":"knockout-component-fddc2108-64dc-441f-be9a-4530cb07f790","id":"ob_02","type":"observable"},val)) : undefined; return this.valueFromJulia["changes"]=false}),self),this["value"].subscribe((function (val){!(this.valueFromJulia["value"]) ? (WebIO.setval({"name":"value","scope":"knockout-component-fddc2108-64dc-441f-be9a-4530cb07f790","id":"ob_01","type":"observable"},val)) : undefined; return this.valueFromJulia["value"]=false}),self)] } self.model = new AppViewModel(); self.valueFromJulia = {}; for (var key in json_data) { self.valueFromJulia[key] = false; } ko.applyBindings(self.model, self.dom); } ]},"changes":[(function (val){return (val!=this.model["changes"]()) ? (this.valueFromJulia["changes"]=true, this.model["changes"](val)) : undefined})],"value":[(function (val){return (val!=this.model["value"]()) ? (this.valueFromJulia["value"]=true, this.model["value"](val)) : undefined})]},"systemjs_options":null,"observables":{"changes":{"sync":false,"id":"ob_02","value":0},"value":{"sync":true,"id":"ob_01","value":51}}},"children":[{"props":{"attributes":{"style":"display:flex; justify-content:center; align-items:center;"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{"attributes":{"style":"text-align:right;width:18%"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{"className":"interact ","style":{"padding":"5px 10px 0px 10px"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"label"},"children":["frame_index"]}]},{"props":{"attributes":{"style":"flex-grow:1; margin: 0 2%"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{"max":101,"min":1,"attributes":{"type":"range","data-bind":"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}","orient":"horizontal"},"step":1,"className":"slider slider is-fullwidth","style":{}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"input"},"children":[]}]},{"props":{"attributes":{"style":"width:18%"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[{"props":{"attributes":{"data-bind":"text: value"}},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"p"},"children":[]}]}]}]}]},{"props":{},"nodeType":"Scope","type":"node","instanceArgs":{"imports":{"data":[],"type":"async_block"},"id":"scope-8cfbd96b-0f0a-48ef-8107-eed5c1134dca","handlers":{"obs-output":[function (updated_htmlstr) { var el = this.dom.querySelector("#out"); WebIO.propUtils.setInnerHtml(el, updated_htmlstr); }]},"systemjs_options":null,"observables":{"obs-output":{"sync":false,"id":"ob_05","value":"<div class='display:none'></div><unsafe-script style='display:none'>\nWebIO.mount(this.previousSibling,{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;display:flex; justify-content:center; align-items:center;&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;setInnerHtml&quot;:&quot;&lt;img src=&#39;data:image/png;base64,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&#39;&gt;&lt;/img&gt;&quot;},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[]}]})</unsafe-script>"}}},"children":[{"props":{"id":"out","setInnerHtml":"<div class='display:none'></div><unsafe-script style='display:none'>\nWebIO.mount(this.previousSibling,{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;display:flex; justify-content:center; align-items:center;&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;setInnerHtml&quot;:&quot;&lt;img src=&#39;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAABkCAIAAACaW42NAAAABmJLR0QA/wD/AP+gvaeTAAAFvElEQVR4nO3cb2xVdxkH8Oece6HNoKWIlA78N9aaMcGOLRbikmmMGLqZOKOLbgmLMfHfC1Pma11M8M1eie+siYnZnKLGqPHPljg1Lusic4ZigQ6ZW6Jkg3WTdmNA2917fAEz99Jbeu24PXDv5/Oq/d1zkm9fnN/z9JznnggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALqF3aCQbHsw7BVyVeodGsgtcRUCjDA7baYD/7QU2AliEweFsZKg3Is538G/+DHAZVdxfc6sNWtfgcJZlI0ODtgFYlMHhykun+jeABtC5Q6uzC8BlUHH3HaAxVGxoebYBeKsMygANduEbNXYaaHEad3hLBofVUmCJtNzDvTTvAAA0i96hkex3cXty63eezTsK0Aoe+eX3PnhjX94plpDGHYDLoXdo5NjeQ7cnX3ok7yRAE6u6yT545xefPHIs1zxAnozKwKJUvMPdC5aBBqrYbVprTgYAAAAAAAAAAAAAAAAAAAAAgCaT7L7va3lnAAAAFlBct/H9eWcAAAAWUDz5ymTeGQAAgAUU8w4AOdh0bPbjj53tmSin5aye48tpcmJt4TcfbR/vW9bobHDlWzab3fHHszePzXScrusKiohzbcl4X/EXO695bWXa0GxAM9l8dPaOP5ztniil9W025TR5sbvw6x3tR69vznpd2P7hnXlngCX17uNvfOXB0x2vR5rV23MkWaw8k908NvNM3/KpTm0Hre6u35659emZtpn/45RiKda9nPU9P7t/6/IsSRoWDWge1/3rjS//8PTKM1mdXXucr9evl28Zmz383uJrHU1Yr5vwT4JLGxidiSRJ5uvad/XHPVsivbixSLIsS2JgdLrh+eDKVijFwOhMcok6undn3PD2uctpOXvnC6Wel0qNywY0k20HprMkmXe3+dxN8ZnNc+t1mkUW2cDB2UbHy4VRGVpO11TFhExnWwxsqPp4x8bY1R9fvy2+9XjsOxQVszSFcqyeLC9hUrgSdZ4up5XXwaa1saGj6ohd/fHVbbHvUOz5czzz8kWnr57KXuhpeEigCbxtsqJer2qPD6yv+vhj18fdW+Ibt8Wex+NnhyvrdZrF6snmvEegcae19a2J399bY33T2nj4U7HnI/HAE/H9A1HSr8M87tseX7ilxvo9W+Kzm+PnR+L+P1W276ZkgMW4cW3tev2+7tj36bj/Q/HAE/HwWNPXa6MyAABwFXDHndZ27JXY8WDVyr39sas/xifmjsoANXz7L/HTw1UrP7krutrnG5VxRQGLcWTi4nr9+a1x95Y4/NLcUZkmpnGn5UyuSstpXBjSfXU6Hnuu6uNrO+LRZ2u27KU0TnV5SEWre3VlWiokhdKbF8j4RIxPVB3x0MH47tNzW/bzTq0yLAPU5T9daTlNLoy5T527uF6/ozN+dbRmy15OklNdhaWKuaQ07rSc/Vvbtv9tOkui9hfVHzpY86zzxz91U1tDs8GVr1SIv/Yv33Zget5XPex+tOZyOU2O96QnupuzmgKX3f6tbQOjM9l8L4L7wWjNs7IkSSKe6vced2gKU53pvzcU158srTg7T+8+RzlNTnQX9n1ixT/f439diH9sLC6fzdZMlut/lfu59nTshmU/+uSK6TaPrYC6TK5Kj19bXH+ytOJMVuejunKavNhT+PGd1zz/ruZs3JPd39ybdwYAAGABxXVruvLOAAAALKB48rm/550BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALhq/BfNEMXNVk1y9gAAAABJRU5ErkJggg==&#39;&gt;&lt;/img&gt;&quot;},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[]}]})</unsafe-script>"},"nodeType":"DOM","type":"node","instanceArgs":{"namespace":"html","tag":"div"},"children":[]}]}]})

In [ ]: