feat: 优化双臂采摘QP稳健性

This commit is contained in:
2026-08-13 10:23:35 +08:00
parent 807374c9fd
commit 7a5c27d6b9
10 changed files with 1078 additions and 43 deletions
+52 -4
View File
@@ -11,6 +11,7 @@ from xr_rm_teleop.single_arm_velocity_teleop import (
SingleArmVelocityTeleop,
_make_transform,
_so3_exp,
_so3_log,
)
@@ -610,7 +611,7 @@ def test_first_feedback_initializes_last_valid_target_without_solving() -> None:
assert teleop._ik_solver.solve_calls == 0
def test_qp_failure_returns_last_known_good_target() -> None:
def test_qp_failure_returns_none_and_keeps_last_known_good_target() -> None:
class FailingSolver:
def solve(self, target):
del target
@@ -624,11 +625,11 @@ def test_qp_failure_returns_last_known_good_target() -> None:
target = teleop._solve_joint_target(np.eye(4))
assert target == pytest.approx([0.1] * 7)
assert target is None
assert teleop._last_valid_joint_target == pytest.approx([0.1] * 7)
def test_qp_success_updates_last_known_good_target() -> None:
def test_qp_success_waits_for_send_before_updating_last_known_good_target() -> None:
class SuccessfulSolver:
def solve(self, target):
del target
@@ -643,7 +644,54 @@ def test_qp_success_updates_last_known_good_target() -> None:
target = teleop._solve_joint_target(np.eye(4))
assert target == pytest.approx([0.2] * 7)
assert teleop._last_valid_joint_target == pytest.approx([0.2] * 7)
assert teleop._last_valid_joint_target == pytest.approx([0.1] * 7)
def test_target_filters_do_not_commit_candidate_state() -> None:
teleop = object.__new__(SingleArmVelocityTeleop)
teleop._filtered_target = [0.0, 0.0, 0.0]
teleop._filtered_orientation_target = np.eye(3)
teleop._target_filter_alpha = 0.5
teleop._target_filter_alpha_fast = 0.5
teleop._target_filter_fast_threshold_m = 1.0
teleop._orientation_filter_alpha = 0.5
position = teleop._filter_target([0.2, 0.0, 0.0])
orientation = teleop._filter_orientation_target(
_so3_exp(np.asarray([0.0, 0.0, 0.2]))
)
assert position == pytest.approx([0.1, 0.0, 0.0])
assert teleop._filtered_target == pytest.approx([0.0, 0.0, 0.0])
assert teleop._filtered_orientation_target == pytest.approx(np.eye(3))
assert np.linalg.norm(_so3_log(orientation)) == pytest.approx(0.1)
def test_failed_send_does_not_commit_cartesian_reference_state() -> None:
teleop = object.__new__(SingleArmVelocityTeleop)
teleop._last_valid_joint_target = [0.1] * 7
teleop._filtered_target = [0.2, 0.0, 0.0]
teleop._filtered_orientation_target = np.eye(3)
teleop._last_sent_target = [0.2, 0.0, 0.0]
teleop._last_sent_orientation = np.eye(3)
teleop._last_command_time = FakeTime()
teleop._send_joint_target = lambda joints: False
sent = teleop._send_and_commit_joint_target(
[0.3] * 7,
[0.3, 0.0, 0.0],
_so3_exp(np.asarray([0.0, 0.0, 0.1])),
[0.3, 0.0, 0.0],
_so3_exp(np.asarray([0.0, 0.0, 0.1])),
FakeTime(),
)
assert not sent
assert teleop._last_valid_joint_target == pytest.approx([0.1] * 7)
assert teleop._filtered_target == pytest.approx([0.2, 0.0, 0.0])
assert teleop._filtered_orientation_target == pytest.approx(np.eye(3))
assert teleop._last_sent_target == pytest.approx([0.2, 0.0, 0.0])
assert teleop._last_sent_orientation == pytest.approx(np.eye(3))
def test_enter_active_control_initializes_se3_orientation_state() -> None: