@@ -71,6 +71,9 @@ bool VanillaCartesianAdmittanceRule::compute_controls(
71
71
success = false ;
72
72
}
73
73
74
+ size_t num_joints = vic_state_.input_data .joint_state_position .size ();
75
+ size_t dims = 6 ; // 6 DoF
76
+
74
77
// auto num_joints = vic_input_data.joint_state_position.size();
75
78
// Get reference compliant frame at t_k
76
79
RCLCPP_DEBUG (logger_, " Reading reference frame..." );
@@ -180,9 +183,6 @@ bool VanillaCartesianAdmittanceRule::compute_controls(
180
183
J_dot_
181
184
);
182
185
183
- RCLCPP_DEBUG (logger_, " Computing J_pinv..." );
184
- J_pinv_ = (J_.transpose () * J_ + alpha_pinv_ * I_joint_space_).inverse () * J_.transpose ();
185
-
186
186
if (!model_is_ok) {
187
187
success = false ;
188
188
RCLCPP_ERROR (
@@ -191,6 +191,21 @@ bool VanillaCartesianAdmittanceRule::compute_controls(
191
191
);
192
192
}
193
193
194
+
195
+ RCLCPP_DEBUG (logger_, " Computing J_pinv..." );
196
+ const Eigen::JacobiSVD<Eigen::MatrixXd> J_svd =
197
+ Eigen::JacobiSVD<Eigen::MatrixXd>(J_, Eigen::ComputeThinU | Eigen::ComputeThinV);
198
+ const Eigen::MatrixXd matrix_s = J_svd.singularValues ().asDiagonal ();
199
+ if (J_svd.singularValues ()(0 ) / J_svd.singularValues ()(dims - 1 ) > 30 ) {
200
+ success = false ;
201
+ RCLCPP_ERROR (
202
+ logger_,
203
+ " Jacobian singularity detected!"
204
+ );
205
+ }
206
+ // J_pinv_ = J_svd.matrixV() * matrix_s.inverse() * J_svd.matrixU().transpose();
207
+ J_pinv_ = (J_.transpose () * J_ + alpha_pinv_ * I_joint_space_).inverse () * J_.transpose ();
208
+
194
209
// Compute desired inertia matrix and its inverse
195
210
RCLCPP_DEBUG (logger_, " Computing M_inv..." );
196
211
Eigen::Matrix<double , 6 , 6 > M_inv;
@@ -214,33 +229,72 @@ bool VanillaCartesianAdmittanceRule::compute_controls(
214
229
// VIC rule: M * err_p_ddot + D * err_p_dot + K * err_p = F_ext - F_ref
215
230
// where err_p = p_desired - p_current
216
231
//
217
- // -> commanded_acc = p_ddot_desired + inv(M) * (K * err_p + D * err_p_dot - F_ext + F_ref)
218
- // Implement "normal" impedance control
219
- RCLCPP_DEBUG (logger_, " Cmd cartesian acc..." );
220
- Eigen::Matrix<double , 6 , 1 > commanded_cartesian_acc = reference_compliant_frame.acceleration + \
221
- M_inv * (K * error_pose + D * error_velocity - F_ext + reference_compliant_frame.wrench );
222
-
223
- // TODO(tpoignonec): clip cartesian acceleration in min/max range
224
- // (and same for velocity if possible)
225
232
226
233
/*
227
- // Basic scheme for reference (not great): integrate cartesian velocity
228
-
229
- // Compute commanded cartesian twist
230
- auto robot_command_twist = J_ * previous_jnt_cmd_velocity + commanded_cartesian_acc * dt;
234
+ auto robot_command_twist = D.inverse() * (
235
+ K * error_pose
236
+ + M * (reference_compliant_frame.acceleration - vic_input_data.robot_estimated_acceleration)
237
+ - F_ext + reference_compliant_frame.wrench
238
+ ) + reference_compliant_frame.velocity;
231
239
success &= dynamics_->convert_cartesian_deltas_to_joint_deltas(
232
240
vic_input_data.joint_state_position,
233
241
robot_command_twist,
234
242
vic_input_data.control_frame,
235
243
vic_command_data.joint_command_velocity
236
244
);
245
+
246
+ // Nullspace objective for stability
247
+ // ------------------------------------------------
248
+ if (false){ //(vic_input_data.activate_nullspace_control) {
249
+ RCLCPP_DEBUG(logger_, "Cmd nullspace joint acc...");
250
+ nullspace_projection_ = I_joint_space_ - J_pinv_ * J_;
251
+ M_nullspace_.diagonal() = vic_input_data.nullspace_joint_inertia;
252
+ K_nullspace_.diagonal() = vic_input_data.nullspace_joint_stiffness;
253
+ D_nullspace_.diagonal() = vic_input_data.nullspace_joint_damping;
254
+ auto D_inv_nullspace_ = D_nullspace_;
255
+ D_inv_nullspace_.diagonal() = D_nullspace_.diagonal().cwiseInverse();
256
+ auto error_position_nullspace = \
257
+ vic_input_data.nullspace_desired_joint_positions - vic_input_data.joint_state_position;
258
+ // Add nullspace contribution to joint velocity
259
+ vic_command_data.joint_command_velocity += nullspace_projection_ * D_inv_nullspace_ * (
260
+ K_nullspace_ * error_position_nullspace
261
+ + external_joint_torques_);
262
+ } else {
263
+ // Pure (small) damping in nullspace for stability
264
+ RCLCPP_WARN_THROTTLE(
265
+ logger_,
266
+ internal_clock_,
267
+ 10000, // every 10 seconds
268
+ "WARNING! nullspace impedance control is disabled!"
269
+ );
270
+ }
271
+
272
+ std::cout << "ref vel: " << reference_compliant_frame.velocity.transpose() << std::endl;
273
+ std::cout << "cur vel: " << vic_input_data.robot_current_velocity.transpose() << std::endl;
274
+ std::cout << "ref acc: " << reference_compliant_frame.acceleration.transpose() << std::endl;
275
+ std::cout << "est acc: " << vic_input_data.robot_estimated_acceleration.transpose() << std::endl;
276
+ std::cout << "ref wrench: " << reference_compliant_frame.wrench.transpose() << std::endl;
277
+ std::cout << "D.inv(): " << D.inverse() << std::endl;
278
+ std::cout << "K: " << K << std::endl;
279
+ std::cout << "M: " << M << std::endl;
280
+ std::cout << "robot_command_twist: " << robot_command_twist.transpose() << std::endl;
237
281
*/
238
282
283
+ // Alternative: use joint accelerations to integrate cartesian velocity
239
284
// Compute joint command accelerations
285
+
286
+ // -> commanded_acc = p_ddot_desired + inv(M) * (K * err_p + D * err_p_dot - F_ext + F_ref)
287
+ // Implement "normal" impedance control
288
+ RCLCPP_DEBUG (logger_, " Cmd cartesian acc..." );
289
+ Eigen::Matrix<double , 6 , 1 > commanded_cartesian_acc = reference_compliant_frame.acceleration + \
290
+ M_inv * (K * error_pose + D * error_velocity - F_ext + reference_compliant_frame.wrench );
291
+
292
+ // TODO(tpoignonec): clip cartesian acceleration in min/max range
293
+ // (and same for velocity if possible)
294
+
240
295
RCLCPP_DEBUG (logger_, " Cmd joint acc..." );
241
296
vic_command_data.joint_command_acceleration = \
242
297
J_pinv_ * (commanded_cartesian_acc - J_dot_ * vic_input_data.joint_state_velocity );
243
-
244
298
// Nullspace objective for stability
245
299
// ------------------------------------------------
246
300
if (vic_input_data.activate_nullspace_control ) {
@@ -274,6 +328,71 @@ bool VanillaCartesianAdmittanceRule::compute_controls(
274
328
vic_command_data.joint_command_velocity += \
275
329
vic_command_data.joint_command_acceleration * dt;
276
330
331
+ // Detect and handle singularity issues
332
+ /*
333
+ // Singularity detection
334
+ // from https://github.com/moveit/moveit2/blob/8a0c655e2ba48e1f93f551cd52fb5aa093021659/moveit_ros/moveit_servo/src/utils/common.cpp#L282
335
+ double singularity_step_scale = 0.01;
336
+ double hard_stop_singularity_threshold = 30.0;
337
+ double lower_singularity_threshold = 17.0;
338
+ double leaving_singularity_threshold_multiplier = 1.5;
339
+
340
+ Eigen::VectorXd vector_towards_singularity = J_svd.matrixU().col(dims - 1);
341
+ const double current_condition_number = J_svd.singularValues()(0) / J_svd.singularValues()(dims - 1);
342
+ const Eigen::VectorXd delta_x = vector_towards_singularity * singularity_step_scale;
343
+ // Compute the new joint angles if we take the small step delta_x
344
+ Eigen::VectorXd next_joint_position = vic_input_data.joint_state_position;
345
+ next_joint_position += J_pinv_ * delta_x;
346
+
347
+ // Compute the Jacobian SVD for the new robot state.
348
+ Eigen::Matrix<double, 6, Eigen::Dynamic> J_next = Eigen::Matrix<double, 6, Eigen::Dynamic>::Zero(6, dims);
349
+ dynamics_->calculate_jacobian(
350
+ next_joint_position,
351
+ vic_input_data.control_frame,
352
+ J_next
353
+ );
354
+ const Eigen::JacobiSVD<Eigen::MatrixXd> next_svd = Eigen::JacobiSVD<Eigen::MatrixXd>(
355
+ J_next, Eigen::ComputeThinU | Eigen::ComputeThinV);
356
+
357
+ // Compute condition number for the new Jacobian.
358
+ const double next_condition_number = next_svd.singularValues()(0) / next_svd.singularValues()(dims - 1);
359
+ if (next_condition_number <= current_condition_number) {
360
+ vector_towards_singularity *= -1;
361
+ }
362
+ // Double check the direction using dot product.
363
+ const bool moving_towards_singularity = vector_towards_singularity.dot(target_delta_x) > 0;
364
+
365
+ // Compute upper condition variable threshold based on if we are moving towards or away from singularity.
366
+ // See https://github.com/moveit/moveit2/pull/620#issuecomment-1201418258 for visual explanation.
367
+ double upper_threshold;
368
+ if (moving_towards_singularity)
369
+ {
370
+ upper_threshold = hard_stop_singularity_threshold;
371
+ }
372
+ else
373
+ {
374
+ const double threshold_size = (hard_stop_singularity_threshold - lower_singularity_threshold);
375
+ upper_threshold = lower_singularity_threshold + (threshold_size * leaving_singularity_threshold_multiplier);
376
+ }
377
+
378
+ // Compute the scale based on the current condition number.
379
+ double velocity_scale = 1.0;
380
+ const bool is_above_lower_limit = current_condition_number > lower_singularity_threshold;
381
+ const bool is_below_hard_stop_limit = current_condition_number < hard_stop_singularity_threshold;
382
+ if (is_above_lower_limit && is_below_hard_stop_limit)
383
+ {
384
+ velocity_scale -=
385
+ (current_condition_number - lower_singularity_threshold) / (upper_threshold - lower_singularity_threshold);
386
+ }
387
+ // If condition number has crossed hard stop limit, halt the robot.
388
+ else if (!is_below_hard_stop_limit)
389
+ {
390
+ success = false;
391
+ velocity_scale = 0.0;
392
+ }
393
+ */
394
+
395
+
277
396
// Filter joint command velocity
278
397
double cutoff_freq_cmd = parameters_.filters .command_filter_cuttoff_freq ;
279
398
if (cutoff_freq_cmd > 0.0 ) { // No smoothing otherwise
0 commit comments