feat: add null space aviod

This commit is contained in:
lgv 2026-02-28 16:29:16 +08:00
parent e37073f493
commit 720d851cd0
7 changed files with 121 additions and 9 deletions

View File

@ -161,6 +161,18 @@ public:
* @param vmax6 线/
*/
void setVelocityLimit6(const std::array<double, 6>& vmax6);
/**
* @brief null-space
*
* @param enable
* @param gain rad/s
* @param margin_ratio (0, 0.5)
* @param max_push rad/s<=0
*/
void setJointLimitAvoidance(bool enable,
double gain = 0.2,
double margin_ratio = 0.05,
double max_push = 0.25);
/**
* @brief AbstractCamera ViSP
* @param R_cv
@ -280,6 +292,11 @@ private:
double depth_z_kp_{1.0};
// 相机 twist 六维限幅。
std::array<double, 6> vmax6_{{0.15, 0.15, 0.20, 0.6, 0.6, 0.6}};
// 关节限位避障 null-space 参数。
bool limit_avoidance_enabled_{false};
double limit_avoidance_gain_{0.2};
double limit_avoidance_margin_ratio_{0.15};
double limit_avoidance_max_push_{0.25};
// tag 平面中用于采样深度的控制点。
Eigen::Vector2d depth_control_point_tag_{Eigen::Vector2d::Zero()};

View File

@ -531,7 +531,7 @@ protected:
// MuJoCo 渲染线程与控制线程不同步:允许复用最近一帧,避免长时间 no_new_frame。
mujoco_camera_->setConsumeNewFrameOnly(false);
q_home_ = {0.25, 1.00, M_PI / 2 - 0.2, M_PI / 2 - 0.2, -M_PI + 0.5, 0.0, 0.0};
q_home_ = {-0.2423, 1.2929,1.61, 1.58, -2.8792, 0.1150,-0.08};
ibvs_controller_ = std::make_unique<IbvsController>();
ibvs_controller_->setMu(mu_);
@ -540,7 +540,8 @@ protected:
ibvs_controller_->setDepthZGain(1.0);
ibvs_controller_->setVelocityLimit6(vmax6_);
ibvs_controller_->setTrackedTagId(tracked_tag_id_);
ibvs_controller_->setTarget(0.0,0.0,0.35);
ibvs_controller_->setTarget(0.0,0.0,0.4);
ibvs_controller_->setJointLimitAvoidance(true, 0.2, 0.15, 0.25);
const Eigen::Matrix3d R_align = (Eigen::Matrix3d() <<
1, 0, 0,

View File

@ -83,6 +83,10 @@ bool IbvsController::init(const std::shared_ptr<device::AbstractCamera>& camera,
initialized_ = dls_solver_->init();
if (initialized_) {
dls_solver_->setJointLimitAvoidance(limit_avoidance_enabled_,
limit_avoidance_gain_,
limit_avoidance_margin_ratio_,
limit_avoidance_max_push_);
has_joint_position_limits_ =
dls_solver_->getJointPositionLimits(q_lower_limits_, q_upper_limits_);
} else {
@ -340,11 +344,11 @@ bool IbvsController::compute(const std::vector<double>& joints_angle,
}
std::cout << "b_cmd: " ;
for (const auto& it : q_cmd_) {
std::cout << it << " ";
}
std::cout << std::endl;
// std::cout << "b_cmd: " ;
// for (const auto& it : q_cmd_) {
// std::cout << it << " ";
// }
// std::cout << std::endl;
clampJointCommandInPlace(q_cmd_);
q_cmd_out = q_cmd_;
@ -420,6 +424,22 @@ void IbvsController::setVelocityLimit6(const std::array<double, 6>& vmax6) {
vmax6_ = vmax6;
}
void IbvsController::setJointLimitAvoidance(bool enable,
double gain,
double margin_ratio,
double max_push) {
limit_avoidance_enabled_ = enable;
limit_avoidance_gain_ = std::max(0.0, gain);
limit_avoidance_margin_ratio_ = std::clamp(margin_ratio, 1e-3, 0.49);
limit_avoidance_max_push_ = max_push;
if (dls_solver_) {
dls_solver_->setJointLimitAvoidance(limit_avoidance_enabled_,
limit_avoidance_gain_,
limit_avoidance_margin_ratio_,
limit_avoidance_max_push_);
}
}
void IbvsController::setAlignCameraToVisp(const Eigen::Matrix3d& R_cv) {
R_cv_ = R_cv;
}

View File

@ -201,6 +201,7 @@ TEST(HumanoidRobotTest,IBVSWithRealRobot) {
ibvs_controller.setMu(0.1);
ibvs_controller.setLambda(0.6);
ibvs_controller.setQdotMax(0.6);
ibvs_controller.setJointLimitAvoidance(true, 0.2, 0.15, 0.25);
ibvs_controller.setTagSize(0.12);
ibvs_controller.setTrackedTagId(0);
ibvs_controller.setTarget(0.0, 0.0, 0.40);
@ -221,6 +222,7 @@ TEST(HumanoidRobotTest,IBVSWithRealRobot) {
// ibvs_controller.setVelocityLimit6({0.15, 0.15, 0.20, 0, 0, 0});
// ibvs_controller.setVelocityLimit6({0.03,0.03,0.03,0.005,0.005,0.005});
ibvs_controller.setQdotMax(0.15);
ibvs_controller.setJointLimitAvoidance(true, 0.2, 0.15, 0.25);
@ -321,4 +323,3 @@ TEST(HumanoidRobotTest,IBVSWithRealRobot) {

View File

@ -78,6 +78,19 @@ public:
double damping = -1.0,
double qdot_abs_max = std::numeric_limits<double>::infinity());
/**
* @brief null-space
*
* @param enable
* @param gain rad/s
* @param margin_ratio (0, 0.5)
* @param max_push rad/s<=0
*/
void setJointLimitAvoidance(bool enable,
double gain = 0.2,
double margin_ratio = 0.15,
double max_push = 0.25);
/**
* @brief URDF
* @param lower size=chain_dof_
@ -122,6 +135,12 @@ private:
Eigen::VectorXd q_lower_chain_;
Eigen::VectorXd q_upper_chain_;
// null-space 关节限位避障参数
bool limit_avoidance_enabled_{false};
double limit_avoidance_gain_{0.2};
double limit_avoidance_margin_ratio_{0.15};
double limit_avoidance_max_push_{0.25};
// base pose cache (PELVIS_S fixed)
bool base_pose_cached_{false};
pinocchio::SE3 oM_base_cached_;

View File

@ -289,6 +289,16 @@ bool PinocchioDlsIKSolver::fk(const std::string& base_link,
return true;
}
void PinocchioDlsIKSolver::setJointLimitAvoidance(bool enable,
double gain,
double margin_ratio,
double max_push) {
limit_avoidance_enabled_ = enable;
limit_avoidance_gain_ = std::max(0.0, gain);
limit_avoidance_margin_ratio_ = std::clamp(margin_ratio, 1e-3, 0.49);
limit_avoidance_max_push_ = max_push;
}
bool PinocchioDlsIKSolver::velocityIk(const std::vector<double>& cur_angle,
const Eigen::Matrix<double,6,1>& ee_velocity,
std::vector<double>& joints_vel,
@ -359,6 +369,50 @@ bool PinocchioDlsIKSolver::velocityIk(const std::vector<double>& cur_angle,
A.diagonal().array() += (lambda * lambda);
Eigen::VectorXd qdot = J.transpose() * A.ldlt().solve(twist_base);
// 6.1) null-space 关节限位避障:在主任务零空间叠加“离限位推回”速度。
if (limit_avoidance_enabled_ &&
limit_avoidance_gain_ > 0.0 &&
chain_v_dof_ == chain_dof_ &&
q_lower_chain_.size() == chain_dof_ &&
q_upper_chain_.size() == chain_dof_) {
const Eigen::VectorXd q_chain = q_full.segment(chain_q_start_, chain_dof_);
Eigen::VectorXd qdot_avoid = Eigen::VectorXd::Zero(chain_v_dof_);
for (int i = 0; i < chain_dof_; ++i) {
const double lo = q_lower_chain_[i];
const double hi = q_upper_chain_[i];
if (!std::isfinite(lo) || !std::isfinite(hi) || hi <= lo) {
continue;
}
const double span = hi - lo;
const double margin = std::max(1e-4, limit_avoidance_margin_ratio_ * span);
double push = 0.0;
if (q_chain[i] < lo + margin) {
const double s = (lo + margin - q_chain[i]) / margin; // 0..1+
push += limit_avoidance_gain_ * s * s;
} else if (q_chain[i] > hi - margin) {
const double s = (q_chain[i] - (hi - margin)) / margin; // 0..1+
push -= limit_avoidance_gain_ * s * s;
}
if (limit_avoidance_max_push_ > 0.0) {
push = clampd(push, -limit_avoidance_max_push_, limit_avoidance_max_push_);
}
qdot_avoid[i] = push;
}
if (qdot_avoid.squaredNorm() > 1e-16) {
const Eigen::Matrix<double,6,6> A_inv =
A.ldlt().solve(Eigen::Matrix<double,6,6>::Identity());
const Eigen::MatrixXd J_pinv = J.transpose() * A_inv; // n x 6
const Eigen::MatrixXd N =
Eigen::MatrixXd::Identity(chain_v_dof_, chain_v_dof_) - J_pinv * J;
qdot += N * qdot_avoid;
}
}
// 7) 关节速度限幅:取调用者限幅与 URDF velocityLimit 的更严格值。
joints_vel.resize(chain_v_dof_);
for (int i = 0; i < chain_v_dof_; ++i) {

View File

@ -66,7 +66,7 @@
</asset>
<worldbody>
<body name="tag_board" pos="0.7 -0.2 1.1" euler="1.57 -1.57 0">
<body name="tag_board" pos="0.7 -0.2 1.05" euler="1.37 -1.57 0">
<!-- 15cm x 15cm 白板,同时贴 apriltag 纹理(纹理里自带白边) -->
<geom name="tag_board_geom"