You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.py 4.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. """Mujoco Client: This node is used to represent simulated robot, it can be used to read virtual positions, or can be controlled."""
  2. import argparse
  3. import json
  4. import os
  5. import time
  6. import mujoco
  7. import mujoco.viewer
  8. import pyarrow as pa
  9. from dora import Node
  10. class Client:
  11. """TODO: Add docstring."""
  12. def __init__(self, config: dict[str, any]):
  13. """TODO: Add docstring."""
  14. self.config = config
  15. self.m = mujoco.MjModel.from_xml_path(filename=config["scene"])
  16. self.data = mujoco.MjData(self.m)
  17. self.node = Node(config["name"])
  18. def run(self):
  19. """TODO: Add docstring."""
  20. with mujoco.viewer.launch_passive(self.m, self.data) as viewer:
  21. for event in self.node:
  22. event_type = event["type"]
  23. if event_type == "INPUT":
  24. event_id = event["id"]
  25. if event_id == "tick":
  26. self.node.send_output("tick", pa.array([]), event["metadata"])
  27. if not viewer.is_running():
  28. break
  29. step_start = time.time()
  30. # Step the simulation forward
  31. mujoco.mj_step(self.m, self.data)
  32. viewer.sync()
  33. # Rudimentary time keeping, will drift relative to wall clock.
  34. time_until_next_step = self.m.opt.timestep - (
  35. time.time() - step_start
  36. )
  37. if time_until_next_step > 0:
  38. time.sleep(time_until_next_step)
  39. elif event_id == "pull_position":
  40. self.pull_position(self.node, event["metadata"])
  41. elif event_id == "pull_velocity":
  42. self.pull_velocity(self.node, event["metadata"])
  43. elif event_id == "pull_current":
  44. self.pull_current(self.node, event["metadata"])
  45. elif event_id == "write_goal_position":
  46. self.write_goal_position(event["value"])
  47. elif event_id == "end":
  48. break
  49. elif event_type == "ERROR":
  50. raise ValueError(
  51. "An error occurred in the dataflow: " + event["error"],
  52. )
  53. self.node.send_output("end", pa.array([]))
  54. def pull_position(self, node, metadata):
  55. """TODO: Add docstring."""
  56. def pull_velocity(self, node, metadata):
  57. """TODO: Add docstring."""
  58. def pull_current(self, node, metadata):
  59. """TODO: Add docstring."""
  60. def write_goal_position(self, goal_position_with_joints):
  61. """TODO: Add docstring."""
  62. joints = goal_position_with_joints.field("joints")
  63. goal_position = goal_position_with_joints.field("values")
  64. for i, joint in enumerate(joints):
  65. self.data.joint(joint.as_py()).qpos[0] = goal_position[i].as_py()
  66. def main():
  67. """Handle dynamic nodes, ask for the name of the node in the dataflow."""
  68. parser = argparse.ArgumentParser(
  69. description="MujoCo Client: This node is used to represent a MuJoCo simulation. It can be used instead of a "
  70. "follower arm to test the dataflow.",
  71. )
  72. parser.add_argument(
  73. "--name",
  74. type=str,
  75. required=False,
  76. help="The name of the node in the dataflow.",
  77. default="mujoco_client",
  78. )
  79. parser.add_argument(
  80. "--scene",
  81. type=str,
  82. required=False,
  83. help="The scene file of the MuJoCo simulation.",
  84. )
  85. parser.add_argument(
  86. "--config", type=str, help="The configuration of the joints.", default=None,
  87. )
  88. args = parser.parse_args()
  89. if not os.getenv("SCENE") and args.scene is None:
  90. raise ValueError(
  91. "Please set the SCENE environment variable or pass the --scene argument.",
  92. )
  93. scene = os.getenv("SCENE", args.scene)
  94. # Check if config is set
  95. if not os.environ.get("CONFIG") and args.config is None:
  96. raise ValueError(
  97. "The configuration is not set. Please set the configuration of the simulated motors in the environment "
  98. "variables or as an argument.",
  99. )
  100. with open(os.environ.get("CONFIG") if args.config is None else args.config) as file:
  101. config = json.load(file)
  102. joints = config.keys()
  103. # Create configuration
  104. bus = {
  105. "name": args.name,
  106. "scene": scene,
  107. "joints": pa.array(joints, pa.string()),
  108. }
  109. print("Mujoco Client Configuration: ", bus, flush=True)
  110. client = Client(bus)
  111. client.run()
  112. if __name__ == "__main__":
  113. main()