|
16 | 16 | DecomposeLumpedParameters,
|
17 | 17 | Evaluate,
|
18 | 18 | Expression,
|
| 19 | + JacobianWrtVariable, |
19 | 20 | MathematicalProgram,
|
20 | 21 | MultibodyForces_,
|
21 | 22 | MultibodyPlant_,
|
| 23 | + SpatialAcceleration, |
| 24 | + SpatialForce, |
22 | 25 | )
|
23 | 26 | from tqdm import tqdm
|
24 | 27 |
|
@@ -317,6 +320,179 @@ def extract_numeric_data_matrix_autodiff(
|
317 | 320 | return W_data, w0, tau_data
|
318 | 321 |
|
319 | 322 |
|
| 323 | +def construct_ft_matrix( |
| 324 | + linear_gravity: np.ndarray, |
| 325 | + angular_velocity: np.ndarray, |
| 326 | + linear_acceleration: np.ndarray, |
| 327 | + angular_acceleration: np.ndarray, |
| 328 | +) -> np.ndarray: |
| 329 | + """ |
| 330 | + Constructs the 6x10 matrix that maps the inertial parameters to the force-torque |
| 331 | + sensor measurements. The equations take the form [f, tau] = mat * alpha, where f |
| 332 | + and tau are the force and torque measurements, and alpha are the lumped inertial |
| 333 | + parameters of form [m, hx, hy, hz, Ixx, Ixy, Ixz, Iyy, Iyz, Izz]. |
| 334 | + All parameters and measurements should be expressed in the sensor frame S. |
| 335 | +
|
| 336 | + See equation 5 in "Improving Force Control Performance by Computational Elimination |
| 337 | + of Non-Contact Forces/Torques" by Kubus et al. |
| 338 | +
|
| 339 | + Args: |
| 340 | + linear_gravity (np.ndarray): The linear gravity vector of shape (3,). Expressed |
| 341 | + in the sensor frame. g_W_S. |
| 342 | + angular_velocity (np.ndarray): The angular velocity vector of shape (3,). |
| 343 | + Expressed in the sensor frame. omega_WS_S. |
| 344 | + linear_acceleration (np.ndarray): The linear acceleration vector of shape (3,). |
| 345 | + Expressed in the sensor frame. a_WS_S. |
| 346 | + angular_acceleration (np.ndarray): The angular acceleration vector of shape |
| 347 | + (3,). Expressed in the sensor frame. alpha_WS_S. |
| 348 | +
|
| 349 | + Returns: |
| 350 | + np.ndarray: The 6x10 matrix that maps the inertial parameters to the |
| 351 | + force-torque sensor measurements. |
| 352 | + """ |
| 353 | + mat = np.zeros((6, 10)) |
| 354 | + mat[0, 0:4] = [ |
| 355 | + linear_acceleration[0] - linear_gravity[0], |
| 356 | + -angular_velocity[1] ** 2 - angular_velocity[2] ** 2, |
| 357 | + angular_velocity[0] * angular_velocity[1] - angular_acceleration[2], |
| 358 | + angular_velocity[0] * angular_velocity[2] + angular_acceleration[1], |
| 359 | + ] |
| 360 | + mat[1, 0:4] = [ |
| 361 | + linear_acceleration[1] - linear_gravity[1], |
| 362 | + angular_velocity[0] * angular_velocity[1] + angular_acceleration[2], |
| 363 | + -angular_velocity[0] ** 2 - angular_velocity[2] ** 2, |
| 364 | + angular_velocity[1] * angular_velocity[2] - angular_acceleration[0], |
| 365 | + ] |
| 366 | + mat[2, 0:4] = [ |
| 367 | + linear_acceleration[2] - linear_gravity[2], |
| 368 | + angular_velocity[0] * angular_velocity[2] - angular_acceleration[1], |
| 369 | + angular_velocity[1] * angular_velocity[2] + angular_acceleration[0], |
| 370 | + -angular_velocity[1] ** 2 - angular_velocity[0] ** 2, |
| 371 | + ] |
| 372 | + mat[3, 0:4] = [ |
| 373 | + 0.0, |
| 374 | + 0.0, |
| 375 | + linear_acceleration[2] - linear_gravity[2], |
| 376 | + linear_gravity[1] - linear_acceleration[1], |
| 377 | + ] |
| 378 | + mat[4, 0:4] = [ |
| 379 | + 0.0, |
| 380 | + linear_gravity[2] - linear_acceleration[2], |
| 381 | + 0.0, |
| 382 | + linear_acceleration[0] - linear_gravity[0], |
| 383 | + ] |
| 384 | + mat[5, 0:4] = [ |
| 385 | + 0.0, |
| 386 | + linear_acceleration[1] - linear_gravity[1], |
| 387 | + linear_gravity[0] - linear_acceleration[0], |
| 388 | + 0.0, |
| 389 | + ] |
| 390 | + mat[3, 4:10] = [ |
| 391 | + angular_acceleration[0], |
| 392 | + angular_acceleration[1] - angular_velocity[0] * angular_velocity[2], |
| 393 | + angular_acceleration[2] + angular_velocity[0] * angular_velocity[1], |
| 394 | + -angular_velocity[1] * angular_velocity[2], |
| 395 | + angular_velocity[1] ** 2 - angular_velocity[2] ** 2, |
| 396 | + angular_velocity[1] * angular_velocity[2], |
| 397 | + ] |
| 398 | + mat[4, 4:10] = [ |
| 399 | + angular_velocity[0] * angular_velocity[2], |
| 400 | + angular_acceleration[0] + angular_velocity[1] * angular_velocity[2], |
| 401 | + angular_velocity[2] ** 2 - angular_velocity[0] ** 2, |
| 402 | + angular_acceleration[1], |
| 403 | + angular_acceleration[2] - angular_velocity[0] * angular_velocity[1], |
| 404 | + -angular_velocity[0] * angular_velocity[2], |
| 405 | + ] |
| 406 | + mat[5, 4:10] = [ |
| 407 | + -angular_velocity[0] * angular_velocity[1], |
| 408 | + angular_velocity[0] ** 2 - angular_velocity[1] ** 2, |
| 409 | + angular_acceleration[0] - angular_velocity[1] * angular_velocity[2], |
| 410 | + angular_velocity[0] * angular_velocity[1], |
| 411 | + angular_acceleration[1] + angular_velocity[0] * angular_velocity[2], |
| 412 | + angular_acceleration[2], |
| 413 | + ] |
| 414 | + return mat |
| 415 | + |
| 416 | + |
| 417 | +def construct_ft_data_matrix( |
| 418 | + plant_components: ArmPlantComponents, |
| 419 | + ft_body_name: str, |
| 420 | + ft_sensor_frame_name: str, |
| 421 | + joint_data: JointData, |
| 422 | + use_progress_bar: bool = True, |
| 423 | +) -> np.ndarray: |
| 424 | + """Constructs the numeric data matrix for the force-torque sensor measurements. |
| 425 | + See `construct_ft_matrix` for more details. |
| 426 | +
|
| 427 | + Args: |
| 428 | + plant_components (ArmPlantComponents): This must contain the plant. Optionally, |
| 429 | + it can also contain the plant's context. |
| 430 | + body_name: The name of the body to which the force-torque sensor is welded. |
| 431 | + This could be the F/T sensor body if it is modelled as such. |
| 432 | + ft_sensor_frame_name (str): The name of the frame that the F/T sensor is |
| 433 | + measuring in. |
| 434 | + joint_data (JointData): The joint data. Only the joint positions, velocities, |
| 435 | + and accelerations are used for the data matrix computation. |
| 436 | + use_progress_bar (bool, optional): Whether to use a progress bar. |
| 437 | +
|
| 438 | + Returns: |
| 439 | + np.ndarray: The numeric data matrix of shape (num_joints * num_timesteps, 10). |
| 440 | + """ |
| 441 | + plant = plant_components.plant |
| 442 | + context = plant_components.plant_context |
| 443 | + |
| 444 | + ft_sensor_body = plant.GetBodyByName(ft_body_name) |
| 445 | + ft_sensor_body_frame = ft_sensor_body.body_frame() |
| 446 | + # Transform from sensor body frame S to measurement frame F |
| 447 | + measurement_frame = plant.GetFrameByName(ft_sensor_frame_name) |
| 448 | + X_SF = measurement_frame.CalcPose(context, ft_sensor_body_frame) |
| 449 | + |
| 450 | + # Transform from the world frame to the sensor frame S |
| 451 | + X_WS = plant.EvalBodyPoseInWorld(context=context, body=ft_sensor_body) |
| 452 | + X_WF = X_WS @ X_SF |
| 453 | + X_FW = X_WF.inverse() |
| 454 | + R_FW = X_FW.rotation() |
| 455 | + |
| 456 | + num_timesteps = len(joint_data.sample_times_s) |
| 457 | + data_matrix = np.zeros((num_timesteps * 6, 10)) |
| 458 | + for i in tqdm( |
| 459 | + range(num_timesteps), |
| 460 | + desc="Extracting data matrix", |
| 461 | + disable=not use_progress_bar, |
| 462 | + ): |
| 463 | + # Set joint data |
| 464 | + plant.SetPositions(context, joint_data.joint_positions[i]) |
| 465 | + plant.SetVelocities(context, joint_data.joint_velocities[i]) |
| 466 | + |
| 467 | + # Compute forces due to gravity |
| 468 | + g_W = [0.0, 0.0, -9.81] |
| 469 | + g_W_F = R_FW @ g_W |
| 470 | + |
| 471 | + # Compute spatial velocity |
| 472 | + V_WS = plant.EvalBodySpatialVelocityInWorld( |
| 473 | + context=context, |
| 474 | + body=ft_sensor_body, |
| 475 | + ) |
| 476 | + omega_WS_F = R_FW @ V_WS.rotational() |
| 477 | + |
| 478 | + # Compute spatial accelerations |
| 479 | + A_WS = plant.EvalBodySpatialAccelerationInWorld( |
| 480 | + context=context, |
| 481 | + body=ft_sensor_body, |
| 482 | + ) |
| 483 | + a_WS_F = R_FW @ A_WS.translational() |
| 484 | + alpha_WS_F = R_FW @ A_WS.rotational() |
| 485 | + |
| 486 | + data_matrix[i * 6 : (i + 1) * 6, :] = construct_ft_matrix( |
| 487 | + linear_gravity=g_W_F, |
| 488 | + angular_velocity=omega_WS_F, |
| 489 | + linear_acceleration=a_WS_F, |
| 490 | + angular_acceleration=alpha_WS_F, |
| 491 | + ) |
| 492 | + |
| 493 | + return data_matrix |
| 494 | + |
| 495 | + |
320 | 496 | def compute_base_param_mapping(
|
321 | 497 | W_data: np.ndarray, scale_by_singular_values: bool = False, tol: float = 1e-6
|
322 | 498 | ) -> np.ndarray:
|
|
0 commit comments