Skip to content
Commits on Source (2)
#include "IkSim.h" #include "IkSim.h"
void Skeleton::init() {
// left arm
Bone leftArm(Eigen::Vector3d(-1.2, 3, 0));
leftArm.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(-0.5, -1.7, -0.8));
leftArm.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -1.4, 1.4));
// right arm
Bone rightArm(Eigen::Vector3d(1.2, 3, 0));
rightArm.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0.5, -1.7, -0.8));
rightArm.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -1.4, 1.4));
// left leg
Bone leftLeg(Eigen::Vector3d(-0.7, 0, 0));
leftLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -2, 0.2));
leftLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -2, -0.4));
leftLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, 0, 1.2));
// right leg
Bone rightLeg(Eigen::Vector3d(0.7, 0, 0));
rightLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -2, 0.2));
rightLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, -2, -0.4));
rightLeg.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, 0, 1.2));
// head
Bone head(Eigen::Vector3d(0, 3, 0));
head.addJoint(Eigen::Vector3d(0, 0, 0), Eigen::Vector3d(0, 0.7, 0));
m_bones.emplace_back(leftArm);
m_bones.emplace_back(rightArm);
m_bones.emplace_back(leftLeg);
m_bones.emplace_back(rightLeg);
m_bones.emplace_back(head);
}
bool IkSim::advance() { bool IkSim::advance() {
// advance step // advance step
m_step++; m_step++;
Eigen::Vector2d targetPos = m_skeleton.m_pos int iterNum = 100;
+ Eigen::Vector2d(5 * sqrt(0.5), sqrt(0.5)) float alpha = 0.01;
+ Eigen::Vector2d(sin(m_step/50.0), 0); // target position std::vector<Eigen::Vector3d> targetPos;
int iterNum = 10; Eigen::Vector3d footPos1 = Eigen::Vector3d(0, 0, 3);
float alpha = 0.1; Eigen::Vector3d footPos2 = Eigen::Vector3d(0, 0, -4);
for(int i = 0; i < iterNum; i++){
Eigen::Vector2d currentPos = forwardKinematics(); for (int i = 0; i < m_skeleton.m_bones.size(); i++) {
Bone& bone = m_skeleton.m_bones[i];
Eigen::Vector3d endPos = m_bone_init_end[i];
switch (i) {
case 0:
endPos += Eigen::Vector3d(0, 0, sin(m_step/ 100.0 * M_PI) * 2);
break;
case 1:
endPos += Eigen::Vector3d(0, 0, -sin(m_step / 100.0 * M_PI) * 2);
break;
case 2:
endPos += Eigen::Vector3d(0, (cos((m_step / 100.0 + 1) * M_PI) + 1)/2, -sin(m_step / 100.0 * M_PI) * 2);
break;
case 3:
endPos += Eigen::Vector3d(0, (cos((m_step/100.0 + 1) * M_PI) + 1)/2, sin(m_step / 100.0 * M_PI) * 2);
break;
default:
break;
}
targetPos.push_back(endPos);
}
Eigen::Vector2d error = targetPos - currentPos; //m_skeleton.base_pos += Eigen::Vector3d(0, 0, m_step / 1000.0);
double errorNorm = error.norm();
for(int iter = 0; iter < iterNum; iter++){
for (int i = 0; i < m_skeleton.m_bones.size(); i++) {
Bone& bone = m_skeleton.m_bones[i];
Eigen::Vector3d endPos = bone.forwardKinematics(bone.m_num_joints - 1);
if (errorNorm < 0.01) { Eigen::Vector3d error = targetPos[i] - endPos;
return false; // stop if the error is small enough
}
// construct the Jacobian matrix double errorNorm = error.norm();
Eigen::MatrixXd J = computeJacobian(targetPos);
auto deltaTheta = alpha * J.transpose() * error; // construct the Jacobian matrix
Eigen::MatrixXd J = bone.computeJacobian(endPos);
for (int i = 0; i < m_skeleton.m_num_bones; i++) { auto deltaTheta = alpha * J.transpose() * error;
m_skeleton.m_bones[i].angle += deltaTheta(i, 0);
}
}
for (int j = 0; j < bone.m_num_joints; j++) {
bone.m_joints[j].angles += deltaTheta.segment(j * 3, 3);
}
}
}
return false; return false;
} }
Eigen::MatrixXd IkSim::computeJacobian(Eigen::Vector2d targetPos) { Eigen::MatrixXd Bone::computeJacobian(Eigen::Vector3d endPos) {
Eigen::MatrixXd J(2, m_skeleton.m_num_bones); Eigen::MatrixXd J(3, m_num_joints * 3);
// compute the Jacobian matrix // compute the Jacobian matrix
double accumAngle = 0; Eigen::Matrix3d rotate = Eigen::Matrix3d::Identity();
for (int i = 0; i < m_skeleton.m_num_bones; i++) { for (int i = 0; i < m_num_joints; i++) {
accumAngle += m_skeleton.m_bones[i].angle; Eigen::Matrix3d jointRotate = m_joints[i].toRotation();
} rotate = jointRotate * rotate;
double accumX = 0;
double accumY = 0; Eigen::Vector3d jointPos = forwardKinematics(i - 1);
for (int i = m_skeleton.m_num_bones - 1; i >= 0; --i) { Eigen::Vector3d p = endPos - jointPos;
double angle = m_skeleton.m_bones[i].angle;
double length = m_skeleton.m_bones[i].length; Eigen::Vector3d xAxis = rotate.col(0);
double dx = length * cos(accumAngle); Eigen::Vector3d yAxis = rotate.col(1);
double dy = length * sin(accumAngle); Eigen::Vector3d zAxis = rotate.col(2);
J(0, i) = -dy; // partial derivative with respect to angle
J(1, i) = dx; // partial derivative with respect to length // fill the three columns of the Jacobian matrix, each column is a 3D vector
accumX += dx; J.col(i * 3) = xAxis.cross(p);
accumY += dy; J.col(i * 3 + 1) = yAxis.cross(p);
accumAngle -= angle; J.col(i * 3 + 2) = zAxis.cross(p);
} }
return J; return J;
} }
...@@ -64,21 +125,19 @@ Eigen::VectorXd IkSim::computeGradient() { ...@@ -64,21 +125,19 @@ Eigen::VectorXd IkSim::computeGradient() {
return grad; return grad;
} }
Eigen::Vector2d IkSim::forwardKinematics() { Eigen::Vector3d Bone::forwardKinematics(int joint_index) {
double accumX = 0; if (joint_index < -1 || joint_index >= m_num_joints) {
double accumY = 0; printf("joint index out of range\n");
exit(-1);
double accumAngle = 0; }
for (int i = 0; i < m_skeleton.m_num_bones; i++) { Eigen::Vector3d pos = m_root;
Bone& b = m_skeleton.m_bones[i]; Eigen::Matrix3d rotate = Eigen::Matrix3d::Identity();
accumAngle += b.angle;
double dx = b.length * cos(accumAngle);
double dy = b.length * sin(accumAngle);
accumX += dx; for (int i = 0; i <= joint_index; i++) {
accumY += dy; Eigen::Matrix3d jointRotate = m_joints[i].toRotation();
} rotate = jointRotate * rotate;
pos += rotate * m_joints[i].extent;
}
return Eigen::Vector2d(accumX + m_skeleton.m_pos.x(), accumY + m_skeleton.m_pos.y()); return pos;
} }
\ No newline at end of file
...@@ -2,35 +2,70 @@ ...@@ -2,35 +2,70 @@
#include <time.h> #include <time.h>
using namespace std; using namespace std;
struct Bone { inline float getRandomFloat(float min, float max) {
Bone(double l, double a) : length(l), angle(a) {} return min + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX / (max - min)));
~Bone() {} }
double length;
double angle; struct Joint {
Eigen::Vector3d angles; // euler angles in degrees
Eigen::Vector3d extent; // length and direction of the bone
Eigen::Matrix3d toRotation() {
Eigen::Matrix3d rotation;
rotation = Eigen::AngleAxisd(angles[2] * M_PI / 180, Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(angles[1] * M_PI / 180, Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(angles[0] * M_PI / 180, Eigen::Vector3d::UnitX());
return rotation;
}
Joint() : angles(Eigen::Vector3d(0, 0, 0)), extent(Eigen::Vector3d(1, 0, 0)) {} // default constructor
Joint(Eigen::Vector3d angles, Eigen::Vector3d extent) : angles(angles), extent(extent) {}
~Joint() {}
}; };
struct Skeleton { struct Bone {
Skeleton(): m_pos(Eigen::Vector2d(-2.5, 0)) {} Bone(): m_root(Eigen::Vector3d(0, 0, 0)) {}
~Skeleton() {} Bone(Eigen::Vector3d root) : m_root(root) {}
void init(int num_bones) { ~Bone() {}
void init(int num_joints) {
srand(time(NULL)); srand(time(NULL));
m_num_bones = num_bones; m_num_joints = num_joints;
m_bones.clear(); m_joints.clear();
m_bones.reserve(num_bones); m_joints.reserve(num_joints);
for (int i = 0; i < num_bones; ++i) { for (int i = 0; i < num_joints; ++i) {
double length = 1.0; // default length Eigen::Vector3d angles = Eigen::Vector3d(0, 0, 90 - (i % 2) * 180);
double angle = (rand() / (RAND_MAX + 1.0) * 2 - 1) * M_PI / 3; // random angle between [-pi, pi]
if (i == 0) if (i == 0)
angle = M_PI / 4; angles = Eigen::Vector3d(0, 0, 45);
else else
angle = M_PI / 2 * (1 - ((i % 2) * 2)); angles = Eigen::Vector3d(0, 0, -45);
m_bones.emplace_back(length, angle); Eigen::Vector3d extent = Eigen::Vector3d(1, 0, 0);
m_joints.emplace_back(angles, extent);
} }
} }
int m_num_bones; // number of bones in the skeleton int m_num_joints = 0; // number of joints in the bone
Eigen::Vector2d m_pos; // position of the root joint Eigen::Vector3d m_root; // root position of the bone
std::vector<Bone> m_bones; // bones of the skeleton std::vector<Joint> m_joints; // joints of the bone
Eigen::Vector3d forwardKinematics(int joint_index);
Eigen::MatrixXd computeJacobian(Eigen::Vector3d endPos);
void addJoint(Eigen::Vector3d angles, Eigen::Vector3d extent) {
m_joints.emplace_back(angles, extent);
m_num_joints++;
}
};
struct Skeleton {
Eigen::Vector3d base_pos = Eigen::Vector3d::Zero();
std::vector<Bone> m_bones; // bones of the skeleton
Skeleton(){}
~Skeleton(){}
void init();
void addBone(Bone& bone) {
m_bones.emplace_back(bone);
}
Eigen::Vector3d forwardKinematics(Bone& bone, int joint_index);
}; };
class IkSim : public Simulation { class IkSim : public Simulation {
...@@ -40,18 +75,33 @@ public: ...@@ -40,18 +75,33 @@ public:
} }
virtual void init() override { virtual void init() override {
m_skeleton.init(5); // Initialize skeleton with 5 bones m_skeleton.base_pos = Eigen::Vector3d(0, 0, 0); // Initialize root position
m_skeleton.init(); // Initialize skeleton with 2 joints
std::string path = "bone.off"; initEndVector(); // Initialize end positions of bones
std::string path = "cube.off";
m_objects.clear(); m_objects.clear();
for (int i = 0; i < m_skeleton.m_num_bones; i++) { //for (int i = 0; i < m_skeleton.m_num_joints; i++) {
m_objects.push_back(RigidObject(path)); // m_objects.push_back(RigidObject(path));
m_objects[i].setScale(0.125); // m_objects[i].setScale(0.5, 0.05, 0.05);
} //}
for (int i = 0; i < m_skeleton.m_bones.size(); i++) {
Bone& bone = m_skeleton.m_bones[i];
for (int j = 0; j < bone.m_num_joints; j++) {
m_objects.emplace_back(path);
m_objects.back().setScale(bone.m_joints[j].extent.norm()/2, 0.2, 0.2);
}
}
m_objects.emplace_back(path);
m_objects.back().setScale(1, 1.5, 0.5);
m_objects.back().setPosition(m_skeleton.base_pos + Eigen::Vector3d(0, 1.5, 0));
m_objects.push_back(RigidObject("sphere.off")); m_objects.emplace_back("sphere.off");
m_objects[m_skeleton.m_num_bones].setScale(0.01); m_objects.back().setScale(0.04);
m_objects[m_skeleton.m_num_bones].setPosition(Eigen::Vector3d(-2.5, 0, 0)); m_objects.back().setPosition(m_skeleton.base_pos + Eigen::Vector3d(0, 4, 0));
m_dt = 5e-2; m_dt = 5e-2;
m_radius = 10; m_radius = 10;
...@@ -59,40 +109,61 @@ public: ...@@ -59,40 +109,61 @@ public:
reset(); reset();
} }
void initEndVector() {
for (int i = 0; i < m_skeleton.m_bones.size(); i++) {
Bone& bone = m_skeleton.m_bones[i];
Eigen::Vector3d endPos = bone.forwardKinematics(bone.m_num_joints - 1);
m_bone_init_end.push_back(endPos);
}
}
virtual void resetMembers() override { virtual void resetMembers() override {
} }
void updateSkeletonPosition() { void updateSkeletonPosition() {
double accumX = 0;
double accumY = 0;
double accumAngle = 0;
for (int i = 0; i < m_skeleton.m_num_bones; i++) {
RigidObject& o = m_objects[i];
Bone& b = m_skeleton.m_bones[i];
accumAngle += b.angle;
double dx = b.length * cos(accumAngle);
double dy = b.length * sin(accumAngle);
o.setPosition(Eigen::Vector3d(m_skeleton.m_pos.x() + accumX + dx / 2, m_skeleton.m_pos.y() + accumY + dy / 2, 0)); int rigidIndex = 0;
for (int i = 0; i < m_skeleton.m_bones.size(); i++) {
Bone& bone = m_skeleton.m_bones[i];
Eigen::Vector3d pos = m_skeleton.base_pos + bone.m_root;
Eigen::Matrix3d rotate = Eigen::Matrix3d::Identity();
for (int j = 0; j < bone.m_num_joints; j++) {
RigidObject& o = m_objects[rigidIndex];
Joint& b = bone.m_joints[j];
Eigen::Matrix3d jointRotate = bone.m_joints[j].toRotation();
rotate = jointRotate * rotate;
Eigen::Vector3d extent = rotate * b.extent;
o.setPosition(pos + extent / 2);
Eigen::Vector3d unit = Eigen::Vector3d(1, 0, 0);
Eigen::Vector3d normalExtent = b.extent.normalized();
Eigen::Vector3d axis = unit.cross(normalExtent);
double angle = acos(unit.dot(normalExtent));
Eigen::Matrix3d extentRotate = Eigen::Matrix3d::Identity();
if (axis.norm() > 1e-6) {
axis.normalize();
extentRotate = Eigen::AngleAxisd(angle, axis).toRotationMatrix();
}
o.setRotation(rotate * extentRotate);
Eigen::Matrix3d rotate = Eigen::AngleAxisd(accumAngle, Eigen::Vector3d(0, 0, 1)).toRotationMatrix(); pos += extent;
Eigen::Matrix3d rotateInv = rotate.inverse();
o.setRotation(rotate); rigidIndex++;
accumX += dx; }
accumY += dy;
} }
}
m_objects[rigidIndex].setPosition(m_skeleton.base_pos + Eigen::Vector3d(0, 1.5, 0));
Eigen::MatrixXd computeJacobian(Eigen::Vector2d targetPos); m_objects[rigidIndex + 1].setPosition(m_skeleton.base_pos + Eigen::Vector3d(0, 4, 0));
}
Eigen::VectorXd computeGradient(); Eigen::VectorXd computeGradient();
Eigen::Vector2d forwardKinematics();
virtual void updateRenderGeometry() override { virtual void updateRenderGeometry() override {
updateSkeletonPosition(); updateSkeletonPosition();
for (size_t i = 0; i < m_objects.size(); i++) { for (size_t i = 0; i < m_objects.size(); i++) {
...@@ -141,7 +212,8 @@ public: ...@@ -141,7 +212,8 @@ public:
private: private:
float m_radius; float m_radius;
Skeleton m_skeleton; Skeleton m_skeleton;
std::vector<Eigen::Vector3d> m_bone_init_end; // initial end positions of bones
std::vector<Eigen::MatrixXd> m_renderVs; // vertex positions for rendering std::vector<Eigen::MatrixXd> m_renderVs; // vertex positions for rendering
std::vector<Eigen::MatrixXi> m_renderFs; // face indices for rendering std::vector<Eigen::MatrixXi> m_renderFs; // face indices for rendering
}; };
\ No newline at end of file
...@@ -60,7 +60,11 @@ void BaseObject::recomputeCOM() { ...@@ -60,7 +60,11 @@ void BaseObject::recomputeCOM() {
m_mesh.V = m_mesh.V.rowwise() - COM.transpose(); m_mesh.V = m_mesh.V.rowwise() - COM.transpose();
} }
void BaseObject::setScale(double s) { m_scale = s; } void BaseObject::setScale(Eigen::Vector3d s) { m_scale = s; }
void BaseObject::setScale(double s) { m_scale = Eigen::Vector3d(s, s, s); }
void BaseObject::setScale(double sX, double sY, double sZ) { m_scale = Eigen::Vector3d(sX, sY, sZ); }
void BaseObject::setID(int id) { m_id = id; } void BaseObject::setID(int id) { m_id = id; }
...@@ -80,7 +84,7 @@ void BaseObject::setRotation(const Eigen::Matrix3d& R) { ...@@ -80,7 +84,7 @@ void BaseObject::setRotation(const Eigen::Matrix3d& R) {
void BaseObject::setColors(const Eigen::MatrixXd& C) { m_mesh.C = C; } void BaseObject::setColors(const Eigen::MatrixXd& C) { m_mesh.C = C; }
double BaseObject::getScale() const { return m_scale; } Eigen::Vector3d BaseObject::getScale() const { return m_scale; }
int BaseObject::getID() const { return m_id; } int BaseObject::getID() const { return m_id; }
...@@ -93,14 +97,14 @@ Eigen::Quaterniond BaseObject::getRotation() const { return m_quat; } ...@@ -93,14 +97,14 @@ Eigen::Quaterniond BaseObject::getRotation() const { return m_quat; }
Eigen::Matrix3d BaseObject::getRotationMatrix() const { return m_rot; } Eigen::Matrix3d BaseObject::getRotationMatrix() const { return m_rot; }
Eigen::Vector3d BaseObject::getVertexPosition(int vertexIndex) const { Eigen::Vector3d BaseObject::getVertexPosition(int vertexIndex) const {
return m_mesh.V.row(vertexIndex) * m_scale * return m_mesh.V.row(vertexIndex).cwiseProduct(m_scale.transpose()) *
getRotationMatrix().transpose() + getRotationMatrix().transpose() +
getPosition().transpose(); getPosition().transpose();
} }
void BaseObject::getMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) const { void BaseObject::getMesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) const {
// get mesh after rotation and translation // get mesh after rotation and translation
V = (m_mesh.V * m_scale * getRotationMatrix().transpose()).rowwise() + V = (m_mesh.V* m_scale.asDiagonal() * getRotationMatrix().transpose()).rowwise() +
getPosition().transpose(); getPosition().transpose();
F = m_mesh.F; F = m_mesh.F;
} }
......
...@@ -35,7 +35,9 @@ public: ...@@ -35,7 +35,9 @@ public:
void reset(); void reset();
void recomputeCOM(); void recomputeCOM();
void setScale(Eigen::Vector3d s);
void setScale(double s); void setScale(double s);
void setScale(double sX, double sY, double sZ);
void setID(int id); void setID(int id);
virtual void setType(ObjType t); virtual void setType(ObjType t);
void setPosition(const Eigen::Vector3d& p); void setPosition(const Eigen::Vector3d& p);
...@@ -43,7 +45,7 @@ public: ...@@ -43,7 +45,7 @@ public:
void setRotation(const Eigen::Matrix3d& R); void setRotation(const Eigen::Matrix3d& R);
void setColors(const Eigen::MatrixXd& C); void setColors(const Eigen::MatrixXd& C);
double getScale() const; Eigen::Vector3d getScale() const;
int getID() const; int getID() const;
ObjType getType() const; ObjType getType() const;
Eigen::Vector3d getPosition() const; Eigen::Vector3d getPosition() const;
...@@ -65,7 +67,7 @@ protected: ...@@ -65,7 +67,7 @@ protected:
Mesh m_mesh; Mesh m_mesh;
ObjType m_type; ObjType m_type;
double m_scale = 1.0; // Scale Eigen::Vector3d m_scale = Eigen::Vector3d(1.0, 1.0, 1.0); // Scale
Eigen::Vector3d m_position; // Position of the center of mass Eigen::Vector3d m_position; // Position of the center of mass
Eigen::Quaterniond m_quat; // Rotation (quaternion) Eigen::Quaterniond m_quat; // Rotation (quaternion)
Eigen::Matrix3d m_rot; // Rotation (matrix) Eigen::Matrix3d m_rot; // Rotation (matrix)
......