Raw File
ImpPDController.cpp
#include "ImpPDController.h"
#include <iostream>

#include "util/Util.h"
#include "sim/SimCharacter.h"
#include "sim/RBDUtil.h"

cImpPDController::cImpPDController()
{
	mExternRBDModel = true;

#if defined(IMP_PD_CTRL_PROFILER)
	mPerfSolveTime = 0;
	mPerfTotalTime = 0;
	mPerfSolveCount = 0;
	mPerfTotalCount = 0;
#endif // IMP_PD_CTRL_PROFILER
}

cImpPDController::~cImpPDController()
{
}

void cImpPDController::Init(cSimCharacter* character, const Eigen::MatrixXd& pd_params, const tVector& gravity)
{
	std::shared_ptr<cRBDModel> model = BuildRBDModel(*character, gravity);
	Init(character, model, pd_params, gravity);
	mExternRBDModel = false;
}

void cImpPDController::Init(cSimCharacter* character, const std::shared_ptr<cRBDModel>& model, const Eigen::MatrixXd& pd_params, const tVector& gravity)
{
	cController::Init(character);
	int num_joints = mChar->GetNumJoints();
	mPDCtrls.resize(num_joints);
	assert(pd_params.rows() == num_joints);

	mGravity = gravity;
	mRBDModel = model;

	int idx = 0;
	for (int j = 0; j < num_joints; ++j)
	{
		cJoint& joint = mChar->GetJoint(j);
		if (joint.IsValid())
		{
			const cPDController::tParams& curr_params = pd_params.row(j);
			cPDController& ctrl = mPDCtrls[j];
			ctrl.Init(mChar, curr_params);
		}
	}

	InitGains();
	mValid = true;
}

void cImpPDController::Reset()
{
	cController::Reset();
	for (size_t i = 0; i < mPDCtrls.size(); ++i)
	{
		mPDCtrls[i].Reset();
	}
}

void cImpPDController::Clear()
{
	cController::Clear();
	mPDCtrls.clear();
	mExternRBDModel = true;
	mRBDModel.reset();
}

void cImpPDController::Update(double time_step)
{
	Eigen::VectorXd tau = Eigen::VectorXd::Zero(GetNumDof());
	UpdateControlForce(time_step, tau);
	ApplyControlForces(tau);
}

void cImpPDController::UpdateControlForce(double time_step, Eigen::VectorXd& out_tau)
{
	cController::Update(time_step);

#if defined(IMP_PD_CTRL_PROFILER)
	TIMER_RECORD_BEG(Update_Ctrl_Force)
#endif

	if (time_step > 0)
	{
		if (!mExternRBDModel)
		{
			UpdateRBDModel();
		}

		Eigen::VectorXd tau;
		CalcControlForces(time_step, tau);
		out_tau += tau;
	}

#if defined(IMP_PD_CTRL_PROFILER)
	TIMER_RECORD_END(Update_Ctrl_Force, mPerfTotalTime, mPerfTotalCount)
#endif

#if defined(IMP_PD_CTRL_PROFILER)
	printf("Solve Time: %.5f\n", mPerfSolveTime);
	printf("Total Time: %.5f\n", mPerfTotalTime);
#endif
}

int cImpPDController::GetNumJoints() const
{
	return mChar->GetNumJoints();
}

int cImpPDController::GetNumDof() const
{
	return mChar->GetNumDof();
}

double cImpPDController::GetTargetTheta(int joint_id) const
{
	const cPDController& pd_ctrl = GetPDCtrl(joint_id);
	return pd_ctrl.GetTargetTheta();
}

void cImpPDController::SetTargetTheta(int joint_id, double theta)
{
	cPDController& pd_ctrl = GetPDCtrl(joint_id);
	pd_ctrl.SetTargetTheta(theta);
}

void cImpPDController::SetTargetVel(int joint_id, double vel)
{
	cPDController& pd_ctrl = GetPDCtrl(joint_id);
	pd_ctrl.SetTargetVel(vel);
}

bool cImpPDController::UseWorldCoord(int joint_id) const
{
	const cPDController& pd_ctrl = GetPDCtrl(joint_id);
	return pd_ctrl.UseWorldCoord();
}

void cImpPDController::SetUseWorldCoord(int joint_id, bool use)
{
	cPDController& pd_ctrl = GetPDCtrl(joint_id);
	pd_ctrl.SetUseWorldCoord(use);
}

void cImpPDController::SetKp(int joint_id, double kp)
{
	cPDController& pd_ctrl = GetPDCtrl(joint_id);
	pd_ctrl.SetKp(kp);

	int param_offset = mChar->GetParamOffset(joint_id);
	int param_size = mChar->GetParamSize(joint_id);

	auto curr_kp = mKp.segment(param_offset, param_size);
	curr_kp.setOnes();
	curr_kp *= kp;
}

void cImpPDController::SetKd(int joint_id, double kd)
{
	cPDController& pd_ctrl = GetPDCtrl(joint_id);
	pd_ctrl.SetKd(kd);

	int param_offset = mChar->GetParamOffset(joint_id);
	int param_size = mChar->GetParamSize(joint_id);

	auto curr_kd = mKp.segment(param_offset, param_size);
	curr_kd.setOnes();
	curr_kd *= kd;
}

bool cImpPDController::IsValidPDCtrl(int joint_id) const
{
	const cPDController& pd_ctrl = GetPDCtrl(joint_id);
	return pd_ctrl.IsValid();
}

void cImpPDController::InitGains()
{
	int num_dof = GetNumDof();
	mKp = Eigen::VectorXd::Zero(num_dof);
	mKd = Eigen::VectorXd::Zero(num_dof);

	for (int j = 0; j < GetNumJoints(); ++j)
	{
		const cPDController& pd_ctrl = GetPDCtrl(j);
		if (pd_ctrl.IsValid())
		{
			int param_offset = mChar->GetParamOffset(j);
			int param_size = mChar->GetParamSize(j);

			double kp = pd_ctrl.GetKp();
			double kd = pd_ctrl.GetKd();

			mKp.segment(param_offset, param_size) = Eigen::VectorXd::Ones(param_size) * kp;
			mKd.segment(param_offset, param_size) = Eigen::VectorXd::Ones(param_size) * kd;
		}
	}
}

std::shared_ptr<cRBDModel> cImpPDController::BuildRBDModel(const cSimCharacter& character, const tVector& gravity) const
{
	std::shared_ptr<cRBDModel> model = std::shared_ptr<cRBDModel>(new cRBDModel());
	model->Init(character.GetJointMat(), character.GetBodyDefs(), gravity);
	return model;
}

void cImpPDController::UpdateRBDModel()
{
	Eigen::VectorXd pose;
	Eigen::VectorXd vel;
	mChar->BuildPose(pose);
	mChar->BuildVel(vel);
	mRBDModel->Update(pose, vel);
}

cPDController& cImpPDController::GetPDCtrl(int joint_id)
{
	assert(joint_id >= 0 && joint_id < GetNumJoints());
	return mPDCtrls[joint_id];
}

const cPDController& cImpPDController::GetPDCtrl(int joint_id) const
{
	assert(joint_id >= 0 && joint_id < GetNumJoints());
	return mPDCtrls[joint_id];
}

void cImpPDController::CalcControlForces(double time_step, Eigen::VectorXd& out_tau)
{
	double t = time_step;

	Eigen::VectorXd pose_err;
	Eigen::VectorXd vel_err;
	BuildPoseErr(pose_err);
	BuildVelErr(vel_err);

	Eigen::DiagonalMatrix<double, Eigen::Dynamic> Kp_mat = mKp.asDiagonal();
	Eigen::DiagonalMatrix<double, Eigen::Dynamic> Kd_mat = mKd.asDiagonal();

	for (int j = 0; j < GetNumJoints(); ++j)
	{
		const cPDController& pd_ctrl = GetPDCtrl(j);
		if (!pd_ctrl.IsValid() || !pd_ctrl.IsActive())
		{
			int param_offset = mChar->GetParamOffset(j);
			int param_size = mChar->GetParamSize(j);
			Kp_mat.diagonal().segment(param_offset, param_size).setZero();
			Kd_mat.diagonal().segment(param_offset, param_size).setZero();
		}
	}

	Eigen::MatrixXd M = mRBDModel->GetMassMat();
	const Eigen::VectorXd& C = mRBDModel->GetBiasForce();

	M.diagonal() += t * mKd;

	const Eigen::VectorXd& vel = mRBDModel->GetVel();
	Eigen::VectorXd acc;
	acc = Kp_mat * (pose_err - t * vel) + Kd_mat * vel_err - C;
	
#if defined(IMP_PD_CTRL_PROFILER)
	TIMER_RECORD_BEG(Solve)
#endif

	acc = M.ldlt().solve(acc);

#if defined(IMP_PD_CTRL_PROFILER)
	TIMER_RECORD_END(Solve, mPerfSolveTime, mPerfSolveCount)
#endif
	
	out_tau = Kp_mat * (pose_err - t * vel) + Kd_mat * (vel_err - t * acc);
}

void cImpPDController::BuildPoseErr(Eigen::VectorXd& out_pose_err) const
{
	out_pose_err = Eigen::VectorXd::Zero(GetNumDof());
	for (int j = 0; j < GetNumJoints(); ++j)
	{
		const cPDController& pd_ctrl = GetPDCtrl(j);
		if (pd_ctrl.IsValid())
		{
			double curr_err = pd_ctrl.CalcThetaErr();
			int param_offset = mChar->GetParamOffset(j);
			int param_size = mChar->GetParamSize(j);
			out_pose_err.segment(param_offset, param_size) = Eigen::VectorXd::Ones(param_size) * curr_err;
		}
	}
}

void cImpPDController::BuildVelErr(Eigen::VectorXd& out_vel_err) const
{
	out_vel_err = Eigen::VectorXd::Zero(GetNumDof());
	for (int j = 0; j < GetNumJoints(); ++j)
	{
		const cPDController& pd_ctrl = GetPDCtrl(j);
		if (pd_ctrl.IsValid())
		{
			double curr_err = pd_ctrl.CalcVelErr();
			int param_offset = mChar->GetParamOffset(j);
			int param_size = mChar->GetParamSize(j);
			out_vel_err.segment(param_offset, param_size) = Eigen::VectorXd::Ones(param_size) * curr_err;
		}
	}
}

void cImpPDController::ApplyControlForces(const Eigen::VectorXd& tau)
{
	mChar->ApplyControlForces(tau);
}
back to top