fix calibration of gripper and add max clip positions for openarm for safety

This commit is contained in:
Pepijn
2025-11-13 16:42:05 +01:00
parent dc69ae3fc0
commit 3cd10d3560
4 changed files with 132 additions and 42 deletions

View File

@@ -91,3 +91,28 @@ class OpenArmsFollowerConfig(RobotConfig):
# Calibration parameters
calibration_mode: str = "manual" # "manual" or "auto"
zero_position_on_connect: bool = False # Set zero position on connect
# Joint limits for position clipping (degrees)
# Format: [min, max] for each joint
# These limits clip commands in send_action to prevent mechanical damage
joint_limits_right: Dict[str, tuple[float, float]] = field(default_factory=lambda: {
"joint_1": (-75.0, 75.0),
"joint_2": (-9.0, 90.0),
"joint_3": (-85.0, 85.0),
"joint_4": (0.0, 135.0),
"joint_5": (-85.0, 85.0),
"joint_6": (-40.0, 40.0),
"joint_7": (-80.0, 80.0),
"gripper": (-65.0, 0.0),
})
joint_limits_left: Dict[str, tuple[float, float]] = field(default_factory=lambda: {
"joint_1": (-75.0, 75.0),
"joint_2": (-90.0, 9.0),
"joint_3": (-85.0, 85.0),
"joint_4": (0.0, 135.0),
"joint_5": (-85.0, 85.0),
"joint_6": (-40.0, 40.0),
"joint_7": (-80.0, 80.0),
"gripper": (-65.0, 0.0),
})

View File

@@ -394,11 +394,29 @@ class OpenArmsFollower(Robot):
motor_name = key.removesuffix(".pos")
if motor_name.startswith("right_"):
# Remove "right_" prefix for bus access
goal_pos_right[motor_name.removeprefix("right_")] = val # do we also do this read in other robots in send action?
goal_pos_right[motor_name.removeprefix("right_")] = val
elif motor_name.startswith("left_"):
# Remove "left_" prefix for bus access
goal_pos_left[motor_name.removeprefix("left_")] = val
# Apply joint limit clipping to right arm
for motor_name, position in goal_pos_right.items():
if motor_name in self.config.joint_limits_right:
min_limit, max_limit = self.config.joint_limits_right[motor_name]
clipped_position = max(min_limit, min(max_limit, position))
if clipped_position != position:
logger.debug(f"Clipped right_{motor_name} from {position:.2f}° to {clipped_position:.2f}°")
goal_pos_right[motor_name] = clipped_position
# Apply joint limit clipping to left arm
for motor_name, position in goal_pos_left.items():
if motor_name in self.config.joint_limits_left:
min_limit, max_limit = self.config.joint_limits_left[motor_name]
clipped_position = max(min_limit, min(max_limit, position))
if clipped_position != position:
logger.debug(f"Clipped left_{motor_name} from {position:.2f}° to {clipped_position:.2f}°")
goal_pos_left[motor_name] = clipped_position
# Apply safety limits if configured
if self.config.max_relative_target is not None:
# Get current positions

View File

@@ -192,28 +192,49 @@ class OpenArmsMini(Teleoperator):
homing_offsets = bus.set_half_turn_homings()
logger.info(f"{arm_name.capitalize()} arm zero position set.")
# Step 2: Set fixed ranges (use full motor range for all motors)
print(
f"\nSetting motor ranges to full range (0-4095)\n"
f"Normalization will handle conversion to degrees/0-100 at runtime\n"
)
# Step 2: Set ranges for joints and gripper
print(f"\nSetting motor ranges for {arm_name.upper()} arm\n")
# Create calibration data with full motor ranges
if self.calibration is None:
self.calibration = {}
# Get motor resolution
motor_resolution = bus.model_resolution_table[list(bus.motors.values())[0].model]
max_res = motor_resolution - 1
for motor_name, motor in bus.motors.items():
# Prefix motor name with arm name for storage
prefixed_name = f"{arm_name}_{motor_name}"
# Get motor resolution
motor_resolution = bus.model_resolution_table[motor.model]
max_res = motor_resolution - 1
# Use full motor range for all motors
# The normalization layer will convert to degrees or 0-100 based on norm_mode
range_min = 0
range_max = max_res
if motor_name == "gripper":
# Interactive calibration for gripper
input(
f"\nGripper Calibration ({arm_name.upper()} arm)\n"
f"Step 1: CLOSE the gripper fully\n"
f"Press ENTER when gripper is closed..."
)
closed_pos = bus.read("Present_Position", motor_name, normalize=False)
logger.info(f" Gripper closed position recorded: {closed_pos}")
input(
f"\nStep 2: OPEN the gripper fully\n"
f"Press ENTER when gripper is fully open..."
)
open_pos = bus.read("Present_Position", motor_name, normalize=False)
logger.info(f" Gripper open position recorded: {open_pos}")
# Use recorded positions as min/max for 0-100 normalization
# 0 = closed, 100 = open
range_min = int(closed_pos)
range_max = int(open_pos)
logger.info(f" {prefixed_name}: range set to [{range_min}, {range_max}] (0=closed, 100=open)")
else:
# Use full motor range for joint motors (will use degrees normalization)
range_min = 0
range_max = max_res
logger.info(f" {prefixed_name}: range set to [0, {max_res}] (full motor range)")
self.calibration[prefixed_name] = MotorCalibration(
id=motor.id,
@@ -222,7 +243,6 @@ class OpenArmsMini(Teleoperator):
range_min=range_min,
range_max=range_max,
)
logger.info(f" {prefixed_name}: range set to [0, {max_res}] (full motor range)")
# Write calibration to this arm's motors
cal_for_bus = {k.replace(f"{arm_name}_", ""): v for k, v in self.calibration.items() if k.startswith(f"{arm_name}_")}