OpenScenarioXML Export#421
Conversation
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (17.56%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #421 +/- ##
==========================================
- Coverage 89.85% 87.97% -1.89%
==========================================
Files 48 48
Lines 13346 13419 +73
==========================================
- Hits 11992 11805 -187
- Misses 1354 1614 +260
🚀 New features to boost your workflow:
|
|
An example scenario of a pedestrian crossing the road, occluded by a car, simulated in Metadrive and then exported to XOSC and replayed in ESMINI. ESMINI Replay MP4: |
| OpenScenarioXML Export | ||
| ---------------------- | ||
|
|
||
| Scenic provides experimental support for exporting completed simulations via `toOpenScenario`. |
There was a problem hiding this comment.
This function lives in an internal module whose documentation says "The functions in this module usually do not need to be used directly.", so I think it's confusing to link to it here. How about making a method Scenario.toOpenScenario parallel to Scenario.simulationToBytes?
| simulationResult, | ||
| scenario, | ||
| scene, |
There was a problem hiding this comment.
Unless there's a particular reason to separate these, I think it would be simpler to just take a Simulation as input (which has a reference to the underlying Scene) and have this be a method on Scenario.
| "No `mapPath` provided and scenario does not have a `map` parameter defined." | ||
| ) | ||
| mapPath = ( | ||
| mapPath if mapPath is not None else os.path.abspath(scenario.params["map"]) |
There was a problem hiding this comment.
How can mapPath not be None here?
| # Create entitities | ||
| entities = xosc.Entities() | ||
| xosc_objects = {} | ||
| for obj_i, obj in enumerate(scene.objects): |
There was a problem hiding this comment.
Presumably this will break for dynamically-created objects. If XOSC doesn't support them we could raise an exception if they are present in the simulation, but at some point we should try to handle the case where all the objects are created dynamically in the first timestep.
| entities = xosc.Entities() | ||
| xosc_objects = {} | ||
| for obj_i, obj in enumerate(scene.objects): | ||
| if hasattr(obj, "isVehicle") and obj.isVehicle: |
There was a problem hiding this comment.
| if hasattr(obj, "isVehicle") and obj.isVehicle: | |
| if getattr(obj, "isVehicle", False): |
| xosc_objects = {} | ||
| for obj_i, obj in enumerate(scene.objects): | ||
| if hasattr(obj, "isVehicle") and obj.isVehicle: | ||
| obj_name = obj.name if hasattr(obj, "name") else f"Vehicle{obj_i}" |
There was a problem hiding this comment.
| obj_name = obj.name if hasattr(obj, "name") else f"Vehicle{obj_i}" | |
| obj_name = getattr(obj, "name", f"Vehicle{obj_i}") |
| elif hasattr(obj, "isPedestrian") and obj.isPedestrian: | ||
| obj_name = obj.name if hasattr(obj, "name") else f"Pedestrian{obj_i}" |
| ) | ||
| xosc_obj = xosc.Pedestrian( | ||
| name=obj_name, | ||
| mass=obj.mass, |
There was a problem hiding this comment.
Is mass required for pedestrians but not for cars?
| """ | ||
| return tuple(obj.position for obj in self.objects) | ||
|
|
||
| class SimulationState(tuple): |
There was a problem hiding this comment.
Should update the docstring now that the positions and orientations attributes are available. While we're at it we should probably discourage overriding this method, except possibly by adding extra attributes to SimulationState.
| obj, states.positions[obj_i], states.orientations[obj_i].yaw | ||
| ) | ||
| ) | ||
| action_times.append(simulationResult.timestep * t) |
There was a problem hiding this comment.
SimulationResult doesn't have a timestep attribute, so I don't understand how your test can be passing.
There was a problem hiding this comment.
Oh, I see, it's actually a Simulation object. Better change the name of the parameter!
| times the length of the vehicle. | ||
| maxSteeringAngle: The maximum steering angle of the vehicle. The full steering range would be | ||
| two times this value, going from (-maxSteeringAngle, maxSteeringAngle). Default value | ||
| 30 degrees. |
There was a problem hiding this comment.
Did you mean 35 degrees, as in the code?
| wheelDiameter: The diameter of the *entire* wheel (including the tire). Default value is 0.7 meters. | ||
| trackWidth: Distance between the vehicle's wheels when pointed straight ahead. Default value | ||
| is 0.85 times the width of the vehicle. | ||
| groundClearance: Default value is half the wheel diameter. |
There was a problem hiding this comment.
Doesn't actually say what this means.
| toOpenScenario, | ||
| ) | ||
| from scenic.core.simulators import DivergenceError, DummySimulator | ||
| from tests.simulators.metadrive.test_metadrive import getMetadriveSimulator |
There was a problem hiding this comment.
Can we use the Newtonian simulator instead? I think this would be the first core test which uses a non-built-in simulator, and we should be able to do this test without the optional MetaDrive dependency.
| facing 90 deg relative to parkedCar, | ||
| with behavior CrossRoad() | ||
|
|
||
| terminate after 30 seconds |
There was a problem hiding this comment.
I'd make the simulation much shorter, just a few steps, so that the test runs faster. I also don't think there's any particular need to use a complex scenario here, and the test would be easier to read if the scenario just had the minimum stuff necessary (say a couple cars, one with FollowLaneBehavior, plus a pedestrian with an initial velocity).
dfremont
left a comment
There was a problem hiding this comment.
Looks good overall, just one issue (how to handle/ban dynamically spawned objects) and a bunch of minor comments above.
|
|
||
| for obj, xosc_obj in xosc_objects.items(): | ||
| obj_init_action = xosc.TeleportAction( | ||
| pos_to_WorldPosition(obj, obj.position, obj.yaw) |
There was a problem hiding this comment.
Should reference obj.heading instead of obj.yaw.
| for t, states in enumerate(simulationResult.trajectory): | ||
| action_positions.append( | ||
| pos_to_WorldPosition( | ||
| obj, states.positions[obj_i], states.orientations[obj_i].yaw |
There was a problem hiding this comment.
Should reference states.orientations[obj_i].heading instead of states.orientations[obj_i].yaw

Description
Added experimental functionality to export the result of a Scenic simulation to OpenScenarioXML.
Issue Link
#334 #32
Checklist
pytestand/or other meansAdditional Notes
N/A