Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding webots dynamic scenario tests #231

Merged
merged 15 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os.path
from pathlib import Path
import re
import subprocess
import sys

import pytest
Expand Down Expand Up @@ -52,6 +51,17 @@ def loader(relpath, **kwargs):
return loader


@pytest.fixture
def launchWebots():
DISPLAY = os.environ.get("DISPLAY")
if not DISPLAY:
pytest.skip("DISPLAY env variable not set.")
WEBOTS_ROOT = os.environ.get("WEBOTS_ROOT")
if not WEBOTS_ROOT:
pytest.skip("WEBOTS_ROOT env variable not set.")
return WEBOTS_ROOT


## Command-line options


Expand Down
27 changes: 27 additions & 0 deletions tests/simulators/webots/dynamic/dynamic.scenic
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Create a Webots cube in air and have it drop
"""

model scenic.simulators.webots.model

class Floor(Object):
width: 5
length: 5
height: 0.01
position: (0,0,0)
color: [0.785, 0.785, 0.785]

class Block(WebotsObject):
webotsAdhoc: {'physics': True}
shape: BoxShape()
width: 0.2
length: 0.2
height: 0.2
density: 100
color: [1, 0.502, 0]

floor = new Floor
ego = new Block at (0, 0, 0.5) #above floor by 0.5

terminate when ego.z < 0.1
record (ego.z) as BlockPosition
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os

from controller import Supervisor

import scenic
from scenic.simulators.webots import WebotsSimulator

WEBOTS_RESULT_FILE_PATH = f"{os.path.dirname(__file__)}/../../../results.txt"


def send_results(data):
with open(WEBOTS_RESULT_FILE_PATH, "w") as file:
file.write(data)


supervisor = Supervisor()
simulator = WebotsSimulator(supervisor)

path = supervisor.getCustomData()
print(f"Loading Scenic scenario {path}")
scenario = scenic.scenarioFromFile(path)

scene, _ = scenario.generate()
simulation = simulator.simulate(scene, verbosity=2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a large upper bound to the time limit so if the test fails, it does so gracefully?

block_movements = simulation.result.records["BlockPosition"]
first_block_movement = block_movements[0]
last_block_movement = block_movements[-1]
blocks = [first_block_movement, last_block_movement]
supervisor.simulationQuit(status="finished")
send_results(str(blocks))
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#VRML_SIM R2023a utf8

PROTO ScenicObject [
field SFVec3f translation 0 0 0
field SFRotation rotation 0 0 1 0
field SFString name "solid"
field SFVec3f angularVelocity 0 0 0
field MFString url ""
]
{
Solid {
translation IS translation
rotation IS rotation
name IS name
angularVelocity IS angularVelocity
children [
CadShape {
url IS url
castShadows FALSE
}
]
boundingObject Shape {
geometry Mesh {
url IS url
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#VRML_SIM R2023a utf8

PROTO ScenicObjectWithPhysics [
field SFVec3f translation 0 0 0
field SFRotation rotation 0 0 1 0
field SFString name "solid"
field SFVec3f angularVelocity 0 0 0
field MFString url ""
field SFFloat density 1000 # kg / m^3
]
{
Solid {
translation IS translation
rotation IS rotation
name IS name
angularVelocity IS angularVelocity
children [
CadShape {
url IS url
castShadows FALSE
}
]
boundingObject Shape {
geometry Mesh {
url IS url
}
}
physics Physics {
# density will be set by the simulator
density IS density
mass -1
}
}
}
34 changes: 34 additions & 0 deletions tests/simulators/webots/dynamic/webots_data/worlds/world.wbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#VRML_SIM R2023a utf8

EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2023a/projects/objects/backgrounds/protos/TexturedBackground.proto"
EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2023a/projects/objects/floors/protos/Floor.proto"
IMPORTABLE EXTERNPROTO "../protos/ScenicObject.proto"
IMPORTABLE EXTERNPROTO "../protos/ScenicObjectWithPhysics.proto"

WorldInfo {
gravity 3
basicTimeStep 16
contactProperties [
ContactProperties {
coulombFriction [
0.8
]
}
]
}
Viewpoint {
orientation -0.19729161510865992 0.07408415124219735 0.977541588446517 2.4380019050245862
position 6.683615307234937 -5.5006366813466805 4.16419445995153
}
TexturedBackground {
}
Floor {
name "FLOOR"
size 5 5
}
Robot {
name "Supervisor"
controller "scenic_supervisor"
customData "../../../dynamic.scenic"
supervisor TRUE
}
43 changes: 43 additions & 0 deletions tests/simulators/webots/test_webots.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
import os
import subprocess

import pytest

from tests.utils import pickle_test, sampleScene, tryPickling

WEBOTS_RESULTS_FILE_PATH = f"{os.path.dirname(__file__)}/dynamic/results.txt"
WEBOTS_WORLD_FILE_PATH = (
f"{os.path.dirname(__file__)}/dynamic/webots_data/worlds/world.wbt"
)


def receive_results():
with open(WEBOTS_RESULTS_FILE_PATH, "r") as file:
content = file.read()
return content


def cleanup_results():
command = f"rm -f {WEBOTS_RESULTS_FILE_PATH}"
subprocess.run(command, shell=True)


def test_dynamics_scenarios(launchWebots):
WEBOTS_ROOT = launchWebots
cleanup_results()

timeout_seconds = 300

command = f"bash {WEBOTS_ROOT}/webots --no-rendering --minimize --batch {WEBOTS_WORLD_FILE_PATH}"

try:
subprocess.run(command, shell=True, timeout=timeout_seconds)
except subprocess.TimeoutExpired:
pytest.fail(f"Webots test exceeded the timeout of {timeout_seconds} seconds and failed.")

data = receive_results()
assert data != None
start_z = float(data.split(",")[1].strip(" )]"))
end_z = float(data.split(",")[3].strip(" )]"))
assert start_z == 0.5
assert start_z > end_z
expected_value = 0.09
tolerance = 0.01
assert end_z == pytest.approx(expected_value, abs=tolerance)


def test_basic(loadLocalScenario):
scenario = loadLocalScenario("basic.scenic")
Expand Down
Loading