Sidewalk

In this notebook, we will be creating a sidewalk environment in which pedestrians can walk along the sidewalk and cross the street as cars pass.


In [1]:
using Parameters
using AutomotiveDrivingModels
using AutoViz
using Interact



In [2]:
# Define sidewalk IDs
const TOP = 1
const BOTTOM = 2


Out[2]:
2

Creating the Environment

Here, we create a new type of environment called SidewalkEnv. It consists of a roadway, crosswalk, and sidewalk. A sidewalk is a Vector of Lanes that run alongside the road.


In [3]:
@with_kw mutable struct SidewalkEnv
    roadway::Roadway
    crosswalk::Lane
    sidewalk::Vector{Lane}
end


Out[3]:
SidewalkEnv

Defining the Sidewalk

We define the sidewalk's parameters.


In [4]:
# Geometry parameters
roadway_length = 100.
crosswalk_length = 15.
crosswalk_width = 6.0
crosswalk_pos = roadway_length/2
sidewalk_width = 3.0
sidewalk_pos = crosswalk_length/2 - sidewalk_width / 2


Out[4]:
6.0

Now we create the sidewalk environment. Our environment will consist of:

  • 1-way road with 2 lanes
  • Unsignalized zebra crosswalk perpendicular to the road
  • Sidewalks above and below the road

In [5]:
# Generate straight roadway of length roadway_length with 2 lanes.
# Returns a Roadway type (Array of segments).
# There is already a method to generate a simple straight roadway, which we use here.
roadway = gen_straight_roadway(2, roadway_length) 

# Generate the crosswalk.
# Our crosswalk does not have a predefined method for generation, so we define it with a LaneTag and a curve.
n_samples = 2 # for curve generation
crosswalk = Lane(LaneTag(2,1), gen_straight_curve(VecE2(crosswalk_pos, -crosswalk_length/2),
                                                  VecE2(crosswalk_pos, crosswalk_length/2),
                                                   n_samples), width = crosswalk_width)
cw_segment = RoadSegment(2, [crosswalk])
push!(roadway.segments, cw_segment) # Append the crosswalk to the roadway

# Generate the sidewalk.
top_sidewalk = Lane(LaneTag(3, TOP), gen_straight_curve(VecE2(0., sidewalk_pos),
                                                      VecE2(roadway_length, sidewalk_pos),
                                                        n_samples), width = sidewalk_width)
bottom_sidewalk = Lane(LaneTag(3, BOTTOM), gen_straight_curve(VecE2(0., -(sidewalk_pos - sidewalk_width)),
                                                          VecE2(roadway_length, -(sidewalk_pos - sidewalk_width)),
                                                            n_samples), width = sidewalk_width) 
    # Note: we subtract the sidewalk_width from the sidewalk position so that the edge is flush with the road.


sw_segment = RoadSegment(3, [top_sidewalk, bottom_sidewalk])
push!(roadway.segments, sw_segment)

# Initialize crosswalk environment
env = SidewalkEnv(roadway, crosswalk, [top_sidewalk, bottom_sidewalk]);

Since there is no defined render! method for the crosswalk and the sidewalk, we must define it ourselves.


In [6]:
function AutoViz.render!(rendermodel::RenderModel, env::SidewalkEnv)
    # Render sidewalk
    for sw in env.sidewalk
        curve = sw.curve
        n = length(curve)
        pts = Array{Float64}(undef, 2, n)
        for (i,pt) in enumerate(curve)
            pts[1,i] = pt.pos.x
            pts[2,i] = pt.pos.y
        end
        add_instruction!(rendermodel, render_line, (pts, colorant"grey", sw.width, Cairo.CAIRO_LINE_CAP_BUTT))
    end
    
    # Render roadway
    roadway = gen_straight_roadway(2, roadway_length)
    render!(rendermodel, roadway)
    
    # Render crosswalk
    curve = env.crosswalk.curve
    n = length(curve)
    pts = Array{Float64}(undef, 2, n)
    for (i,pt) in enumerate(curve)
        pts[1,i] = pt.pos.x
        pts[2,i] = pt.pos.y
    end

    # We can add render instructions from AutoViz.
    # Here we want the crosswalk to appear as a white-striped zebra crossing rather than a road.
    add_instruction!(rendermodel, render_dashed_line, (pts, colorant"white", env.crosswalk.width, 1.0, 1.0, 0.0, Cairo.CAIRO_LINE_CAP_BUTT))

    return rendermodel
end

In [7]:
cam = FitToContentCamera(0.0)
render(Scene(), env, cam = cam);

Now we can define our pedestrian.


In [8]:
# We define its class and the dimensions of its bounding box.
const PEDESTRIAN_DEF = VehicleDef(AgentClass.PEDESTRIAN, 1.0, 1.0)


Out[8]:
VehicleDef(PEDESTRIAN, 1.000, 1.000)

We assign models to each agent in the scene.


In [9]:
timestep = 0.1

# Crossing pedestrian definition
ped_init_state = VehicleState(VecSE2(49.0,-3.0,0.), env.sidewalk[BOTTOM], roadway, 1.3)
ped = Vehicle(ped_init_state, PEDESTRIAN_DEF, 1)

# Car definition
car_initial_state = VehicleState(VecSE2(0.0, 0., 0.), roadway.segments[1].lanes[1],roadway, 8.0)
car = Vehicle(car_initial_state, VehicleDef(), 2)

scene = Scene()
push!(scene, ped)
push!(scene, car)

# Define a model for each entity present in the scene
models = Dict{Int, DriverModel}()

ped_id = 1
car_id = 2

models[ped_id] = SidewalkPedestrianModel(timestep=timestep, 
                                            crosswalk=env.crosswalk,
                                            sw_origin = env.sidewalk[BOTTOM],
                                            sw_dest = env.sidewalk[TOP]
                                            )

models[car_id] = 
LatLonSeparableDriver( # produces LatLonAccels
        ProportionalLaneTracker(), # lateral model
        IntelligentDriverModel(), # longitudinal model
)


Out[9]:
LatLonSeparableDriver(ProportionalLaneTracker(NaN, NaN, 3.0, 2.0), IntelligentDriverModel
  a: Float64 NaN
  σ: Float64 NaN
  k_spd: Float64 1.0
  δ: Float64 4.0
  T: Float64 1.5
  v_des: Float64 29.0
  s_min: Float64 5.0
  a_max: Float64 3.0
  d_cmf: Float64 2.0
  d_max: Float64 9.0
)

Simulate

Finally, we simulate the scene.


In [10]:
nticks = 300
rec = SceneRecord(nticks+1, timestep)
# Execute the simulation
simulate!(rec, scene, roadway, models, nticks)
render(rec[0], env, cam=cam)


Out[10]:

We can use a slider to scroll through each frame in the simulation. This usually takes less time than rendering a video.


In [11]:
using Interact
@manipulate for frame_index in 1 : nframes(rec)
    render(rec[frame_index-nframes(rec)], env, cam=cam)
end


Out[11]:
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\":151}"); 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":151}}},"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":301,"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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AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/AP4fvrd6/DK5MZ0AAAAASUVORK5CYII=&#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,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AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/AP4fvrd6/DK5MZ0AAAAASUVORK5CYII=&#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 [ ]: