-
Notifications
You must be signed in to change notification settings - Fork 4
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
Callbacks for custom behaviour during simulation #19
base: main
Are you sure you want to change the base?
Conversation
- Generalised urdf_path input for robotModel class to pass either string or a pathlib object - [To review] Added save_xml flag on robotModel.get_mujoco_model to save the modified xml file
… with associated default values taken from mujoco
Still work in progress, implemented basic structure for PR. - ScoreCallback associates a simulation to a score which can be customly defined - TrackerCallback helps to store variable values for either printing or eventual plotting
Current usage: def score_func(t: float, data: mujoco.MjData) -> float:
return 0.1
scb = ScoreCallback(score_func) # score function = f: (t, data) -> score
tcb = TrackerCallback(["qpos"], False) # (list of tracked variables, print variables on step)
# Define simulator and set initial position
mujoco_instance = MujocoSimulator()
mujoco_instance.load_model(
robot_model,
s=[-0.55, 0],
xyz_rpy=np.asarray([0, 0, initial_height, 0.5, 0, 0]),
floor_opts={}
)
s, ds, tau = mujoco_instance.get_state()
t = mujoco_instance.get_simulation_time()
H_b = mujoco_instance.get_base()
w_b = mujoco_instance.get_base_velocity()
mujoco_instance.set_visualize_robot_flag(False)
t = 0
scb.on_simulation_start()
tcb.on_simulation_start()
while t < 4:
# Reading robot state from simulator
s, ds, tau = mujoco_instance.get_state()
t = mujoco_instance.get_simulation_time()
# Step the simulator
mujoco_instance.step()
scb.on_simulation_step(t, mujoco_instance.data)
tcb.on_simulation_step(t, mujoco_instance.data)
scb.on_simulation_end()
tcb.on_simulation_end()
Ideally the list of callbacks should be passed to the constructor of |
New EarlyStoppingCallback to finish a simulation early. Usage: sim = MujocoSimulator()
sim.load_model(...)
cb = EarlyStoppingCallback(lambda t, data: t > 3.0)
sim.run(6.0, callbacks=[cb]) To achieve mass callback behaviour, the main simulation loop has been grouped inside the |
- Dumping urdf string to temp file if mujoco fails to load - Added should_stop flag to mujocoSimulator class
An idea for a useful callback is the Input: The user passes a list of objects (str) for which he is interested of gathering contact information of (for example: ("plane", "model.right_foot" or "plane", "model.base"). If nothing is passed, all contacts can be considered by default (memory expensive) or no contacts. My doubt is whether to keep this logic inside a callback or implement it internally to the Update: Could be done as both... Mujoco simulator stores it internally for eventual computations, while the callback exposes these contacts to the user in the way mentioned above. |
…ontactInfo wrapper
Currently facing a problem on
The problem is how the values of the variables are stored. We'd obviously expect the values of a variable to be stored in the following way:
but for some reason, they are stored in the following way:
and so the returned values are all the same. Below is an example: Output
The lines starting with Minimal working example
from comodo.mujocoSimulator.callbacks import ScoreCallback, TrackerCallback, EarlyStoppingCallback, ContactCallback
from comodo.robotModel.robotModel import RobotModel
from comodo.mujocoSimulator.mujocoSimulator import MujocoSimulator
import numpy as np
import pathlib
import rod
import rod.builder
import rod.builder.primitives
from rod.urdf.exporter import UrdfExporter
# Build model
dynamics = rod.Dynamics(
damping=0.0,
friction=0.0,
spring_stiffness=0.0,
spring_reference=0.0,
)
box_builder = rod.builder.primitives.BoxBuilder(
name="root",
mass=1.,
x=1., y=1., z=1.,
)
box = box_builder.build_link(
name=box_builder.name,
pose=rod.builder.primitives.PrimitiveBuilder.build_pose(
relative_to="__model__",
)
).add_inertial().add_visual().add_collision().build()
rodmodel = rod.Model(
name="model",
canonical_link=box.name,
link=[box],
joint=[],
frame=[],
)
# Save model
rod_sdf = rod.Sdf(version="1.7", model=rodmodel)
urdf_string = UrdfExporter.sdf_to_urdf_string(
rod_sdf, pretty=True, gazebo_preserve_fixed_joints=True
)
file = pathlib.Path("robot.urdf").absolute()
with open(str(file), "w") as f:
f.write(urdf_string)
path = pathlib.PurePosixPath(file)
tc = TrackerCallback(["qpos"], True)
escb = EarlyStoppingCallback(lambda t, iter, data, opts: iter == 10) # Stop after 10 iterations
# Load model
model = RobotModel(file, "robot", [])
mujoco_instance = MujocoSimulator()
mujoco_instance.load_model(
model,
s=[],
xyz_rpy=np.array([0., 0., 1.5, 0., 0., 0.]),
floor_opts={
"inclination_deg" : [0, 0, 0]
}
)
mujoco_instance.run(3.0, callbacks=[tc, escb], visualise=True)
qpos_data = tc.get_tracked_values()[1]['qpos']
t =tc.t
body_z_pos = np.array([q[2] for q in qpos_data])
print(f"Time: {t}")
print(f"Body z position: {body_z_pos}") |
✔️ Solved! Thanks to @fils99's enormous help, I found out that the values were stored as references inside the TrackerCallback and were modified at the beginning of each iteration when |
My usage of the library (e.g. zero-order optimisation) requires the definition of a score associated with a simulation.
This method defines a score as a function that takes the simulation time and the state of the dynamics (data) at a given frame to return the score of the model for that frame. As of now the score is independent of previous values, but this can be implemented easily.
The definition of such through callbacks which are called pre, during and post simulation has the following advantages: