Files
IK_qp/kine_ctrl/rm75_mujoco.py
LiuzhengSJ 8363b3fa6d try mujoco
2026-05-29 13:34:59 +01:00

250 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""
RM75 Robot Controller for URDF without actuators
Direct joint position control (kinematic mode)
"""
import mujoco
import mujoco.viewer
import numpy as np
import time
from pathlib import Path
class RM75Controller:
def __init__(self, urdf_path: str ="/home/zl/Downloads/urdf_rm75/RM75-B.urdf", enable_viewer: bool = True):
"""
Initialize RM75 robot simulation from URDF
Args:
urdf_path: Path to RM75-B.urdf file
enable_viewer: Show visualization window
"""
# Load model
self.model = mujoco.MjModel.from_xml_path(urdf_path)
self.data = mujoco.MjData(self.model)
# Robot info
self.n_joints = self.model.njnt
self.n_actuators = self.model.nu
print(f"✓ Loaded RM75 robot")
print(f" - Joints: {self.n_joints}")
print(f" - Actuators: {self.n_actuators} (using direct joint control)")
print(f" - Bodies: {self.model.nbody}")
# Get joint names for reference
self.joint_names = []
for i in range(self.n_joints):
self.joint_names.append(self.model.joint(i).name)
print(f" - Joints: {', '.join(self.joint_names)}")
# Store home position (current joint angles)
self.home_position = self.data.qpos[:self.n_joints].copy()
print(f" - Home position: {self.home_position}")
# For position control without actuators, we'll use qpos directly
self.use_actuators = self.n_actuators > 0
# Viewer
self.viewer = None
if enable_viewer:
try:
self.viewer = mujoco.viewer.launch_passive(self.model, self.data)
print("✓ Viewer launched successfully")
except Exception as e:
print(f"Warning: Could not launch viewer: {e}")
self.viewer = None
def get_joint_positions(self):
"""Get current joint angles (radians)"""
return self.data.qpos[:self.n_joints].copy()
def get_joint_velocities(self):
"""Get current joint velocities (rad/s)"""
return self.data.qvel[:self.n_joints].copy()
def get_end_effector_pose(self):
"""Get end-effector position and orientation"""
# Last body is usually end-effector
end_effector_id = self.model.nbody - 1
position = self.data.xpos[end_effector_id].copy()
orientation = self.data.xmat[end_effector_id].copy().reshape(3, 3)
return position, orientation
def set_joint_positions(self, positions):
"""
Set joint positions directly (kinematic control)
Args:
positions: Target joint angles in radians (length should match n_joints)
"""
if len(positions) != self.n_joints:
print(f"Warning: Expected {self.n_joints} joints, got {len(positions)}")
positions = positions[:self.n_joints]
# Directly set joint positions (kinematic control)
self.data.qpos[:self.n_joints] = positions
# Also set velocities to zero to avoid unwanted motion
self.data.qvel[:self.n_joints] = 0
def step(self):
"""Advance simulation one step"""
mujoco.mj_step(self.model, self.data)
if self.viewer:
self.viewer.sync()
def step_n(self, n_steps: int):
"""Advance simulation by N steps"""
for _ in range(n_steps):
self.step()
def move_to_position(self, target_positions, steps=500):
"""
Smoothly move to target joint positions
Args:
target_positions: Target joint angles
steps: Number of simulation steps for the movement
"""
current = self.get_joint_positions()
target = np.array(target_positions[:self.n_joints])
for i in range(steps):
# Linear interpolation
alpha = (i + 1) / steps
positions = current + alpha * (target - current)
self.set_joint_positions(positions)
self.step()
# Ensure exact target
self.set_joint_positions(target)
self.step_n(10)
def run_trajectory(self, trajectory_points, steps_between_points=500):
"""
Execute a joint trajectory
Args:
trajectory_points: List of joint position arrays
steps_between_points: Steps between each point
"""
print(f"Executing trajectory with {len(trajectory_points)} points...")
for i, target in enumerate(trajectory_points):
print(f" Moving to point {i + 1}/{len(trajectory_points)}")
self.move_to_position(target, steps_between_points)
print("✓ Trajectory complete")
def run_forever(self, dt=0.01):
"""Run simulation with real-time control loop"""
print("\n✓ Simulation running. Close viewer window to exit.\n")
try:
while self.viewer and self.viewer.is_running():
self.step()
time.sleep(dt)
except KeyboardInterrupt:
print("\n✓ Stopped by user")
finally:
if self.viewer:
self.viewer.close()
def print_state(self):
"""Print current robot state"""
positions = self.get_joint_positions()
print(f"Joint positions (rad): {[f'{p:.3f}' for p in positions]}")
# Test trajectories for RM75
def create_sine_wave_trajectory(controller, duration_seconds=5, frequency=0.5):
"""Create a sine wave trajectory for testing"""
steps = int(duration_seconds / controller.model.opt.timestep)
trajectory = []
for i in range(steps):
t = i * controller.model.opt.timestep
positions = controller.home_position.copy()
# Create sine wave motion on first 3 joints (shoulder, elbow, wrist)
positions[0] = controller.home_position[0] + 0.5 * np.sin(2 * np.pi * frequency * t)
positions[1] = controller.home_position[1] + 0.3 * np.sin(2 * np.pi * frequency * t + 1.0)
positions[2] = controller.home_position[2] + 0.2 * np.sin(2 * np.pi * frequency * t + 2.0)
trajectory.append(positions)
return trajectory
if __name__ == "__main__":
# Path to your URDF
urdf_file = "/home/zl/Downloads/urdf_rm75/RM75-B.urdf"
# Check if file exists
if not Path(urdf_file).exists():
print(f"Error: URDF file not found at {urdf_file}")
exit(1)
# Create robot controller
print("Initializing RM75 Controller...")
robot = RM75Controller(urdf_file, enable_viewer=True)
if robot.viewer is None:
print("Error: Could not initialize viewer. Running without visualization.")
# Give time for viewer to initialize
time.sleep(1)
# Test 1: Print current state
print("\n>>> Current robot state:")
robot.print_state()
# Test 2: Move to home position
print("\n>>> Moving to home position...")
robot.move_to_position(robot.home_position, steps=300)
robot.print_state()
# Test 3: Create and execute a pose sequence
print("\n>>> Testing different poses...")
# Pose 1: Slightly raised arm
pose1 = robot.home_position.copy()
pose1[0] = 0.5 # Joint 1
pose1[1] = -0.3 # Joint 2
pose1[2] = 0.2 # Joint 3
# Pose 2: Extended arm
pose2 = robot.home_position.copy()
pose2[0] = 0.8
pose2[1] = -0.5
pose2[2] = 0.4
pose2[3] = 0.3 # Joint 4
# Pose 3: Folded position
pose3 = robot.home_position.copy()
pose3[0] = -0.5
pose3[1] = 0.3
pose3[2] = -0.2
trajectory = [pose1, pose2, pose3, robot.home_position]
robot.run_trajectory(trajectory, steps_between_points=400)
# Test 4: Continuous sine wave motion (5 seconds)
print("\n>>> Starting sine wave motion (5 seconds)...")
sine_trajectory = create_sine_wave_trajectory(robot, duration_seconds=5, frequency=0.8)
robot.run_trajectory(sine_trajectory, steps_between_points=1)
# Return to home
print("\n>>> Returning to home position...")
robot.move_to_position(robot.home_position, steps=300)
print("\n✓ Demo complete!")
# Keep viewer open until user closes
if robot.viewer:
print("\nPress Ctrl+C in terminal to exit, or close the viewer window.")
robot.run_forever()
else:
print("\nSimulation completed.")