I think this should all be possible! (though it will take some work to get it all set up). I used POMDPs.jl for something similar a few years ago. This video, paper, and chapter of my thesis that address a similar driving problem were all done using POMDPs.jl.
Moreover, I think there is a lot of opportunity for improving the solvers (e.g. with low level parallelization in Julia 1.3), so if you find ways to create improved solvers, it can help all of us and easily be shared through anyone who wants to use the package.
If you get to any challenges that you think might be show-stoppers, let me know and I'll be happy to discuss them honestly with you, but I don't think it will be a waste of your time to try implementing this with POMDPs.jl.
Yes, probably an online solver would be best. BasicPOMCP and ARDESPOT will converge to a QMDP-like solution when there is a continuous observation space, see https://arxiv.org/abs/1709.06196. This is probably fine, but you should be aware that it will not take expensive info-gathering actions.POMCPOW (or DESPOT-alpha, which unfortunately does not have a POMDPs.jl implementation yet) may be able to do better in the continuous observation space, but you will have to do some modification since it is a mixed-observability model. Ask if you are interested in the details of this.
In my research, we used action spaces of size 10 - any larger than that and the trees might start to get too wide. Is the action space the cross product of the lateral and longitudinal? in that case it may be too big.
struct ObjState # you may want to make this a StaticArrays.FieldVector s::Float64 d::Float64 v::Float64 route_id::UIntend
struct EgoState s::Float64 d::Float64 v::Float64end
struct State ego::EgoState obj_states::Vector{ObjState} # you might be able to speed this up further by using a StaticArrays.SVectorend
...using POMDPs, POMDPModels, POMDPSimulators, POMCPOW, POMDPModelTools, Random
m = TigerPOMDP()
solver = POMCPOWSolver(tree_in_info=true)planner = solve(solver, m)
b = initialstate_distribution(m)
function show_tree_and_best_sequence(planner, b) a, info = action_info(planner, b)
tree = info[:tree] show(stdout, MIME("text/plain"), tree)
bindex = 1 a_sequence = actiontype(m)[] while !isempty(tree.tried[bindex]) anodes = tree.tried[bindex] bnode = POWTreeObsNode(tree, bindex) best_anode = POMCPOW.select_best(MaxQ(), bnode, MersenneTwister(1)) push!(a_sequence, tree.a_labels[best_anode]) children = [pair[2] for pair in tree.generated[best_anode]] # find most likely observation bindex = children[argmax([tree.total_n[c] for c in children])] end @show a_sequenceend
show_tree_and_best_sequence(planner, b)The issue is that POMCPOW (and DESPOT-alpha) use O(o | a, s') to weight the particles. If part of the observation is an exact measurement of the continuous state, then O will be a dirac delta function, so the weight of essentially all particles will be zero. Possible solutions are 1) add artificial gaussian noise to the observations to smooth things out (this is the easiest to implement) or 2) slightly modify how POMCPOW handles beliefs - an example (that I will need to explain more if you decide to use it :)) is here: https://github.com/sisl/Multilane.jl/blob/master/src/pow_filter.jl