Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the option for the external force orientation to be reperesented in the link frame #463

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ The format of this document is based on [Keep a Changelog](https://keepachangelo
## [Unreleased]

### Added
- Add the option for the external force orientation to be reperesented in the link frame with the `externalwrench` plugin (https://github.com/robotology/gazebo-yarp-plugins/pull/463).
- Add the possibility to create different types of joints with the `linkattacher` plugin (https://github.com/robotology/gazebo-yarp-plugins/pull/461).

HosameldinMohamed marked this conversation as resolved.
Show resolved Hide resolved
### Fixed
- Fixed the case if the user writes any 1-word command other than single, the mode is switched to multiple wrenches mode (see https://github.com/robotology/gazebo-yarp-plugins/pull/418). Now, if the user writes any 1-word command other than `single`, `multiple`, `orient_global`, `orient_local`, they will receive an error message (https://github.com/robotology/gazebo-yarp-plugins/pull/463).

## [3.3.0] - 2019-12-13

### Added
Expand Down
4 changes: 4 additions & 0 deletions plugins/externalwrench/include/gazebo/ApplyExternalWrench.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public:
// single wrench or multiple wrenches
std::string m_mode;

// variable for orientation mode
// global or local orientation modes
std::string m_orient;

int wrenchCount;

virtual bool threadInit();
Expand Down
3 changes: 2 additions & 1 deletion plugins/externalwrench/include/gazebo/ExternalWrench.hh
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ public:
void setTick(double& tickTime);
void setTock(double& tockTime);
void setVisual();
void applyWrench();
void applyGlobalOrientationWrench();
void applyLocalOrientationWrench();
void deleteWrench();
void setModel();
};
Expand Down
109 changes: 91 additions & 18 deletions plugins/externalwrench/src/ApplyExternalWrench.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ void ApplyExternalWrench::onUpdate(const gazebo::common::UpdateInfo& _info)
bool duration_check = m_rpcThread.wrenchesVector.at(i).duration_done;
if(duration_check == false)
{
m_rpcThread.wrenchesVector.at(i).applyWrench();
if(this->m_rpcThread.m_orient == "orient_global")
{
m_rpcThread.wrenchesVector.at(i).applyGlobalOrientationWrench();
}
else if(this->m_rpcThread.m_orient == "orient_local")
{
m_rpcThread.wrenchesVector.at(i).applyLocalOrientationWrench();
}
}
}
this->m_lock.unlock();
Expand All @@ -107,6 +114,9 @@ void ApplyExternalWrench::onReset()
// Change the operation mode to default option 'single'
this->m_rpcThread.m_mode = "single";

// Change the orientation to default option 'orient_global'
this->m_rpcThread.m_orient = "orient_global";

// Reset wrench count
this->m_rpcThread.wrenchCount = 0;

Expand All @@ -129,6 +139,9 @@ bool RPCServerThread::threadInit()
// Set the default operation mode
this->m_mode = "single";

// Set the default operation mode
this->m_orient = "orient_global";

// Set wrench count default value
this->wrenchCount = 0;

Expand All @@ -142,16 +155,18 @@ void RPCServerThread::run()
m_rpcPort.read ( command,true );
if ( command.get ( 0 ).asString() == "help" ) {
this->m_reply.addVocab ( yarp::os::Vocab::encode ( "many" ) );
this->m_reply.addString ( "The defaul operation mode is with single wrench" );
this->m_reply.addString ( "The default operation mode is with single wrench" );
this->m_reply.addString ( "Insert [single] or [multiple] to change the operation mode" );
this->m_reply.addString ( "The default frame orientation is the one of the base/root frame ([orient_global])" );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it one of the base/root frame or the world frame? The words base/root and global seem to not to go along well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right it's confusing. However the phrase "The default frame orientation is the one of the base/root frame" was written before and I didn't want to change it even though the applied force's frame seems to have the orientation of the global one.

Check out this little demo where I apply forces in X,Y and Z directions in global_orient mode:
Peek 2020-02-16 17-21

this->m_reply.addString ( "Insert [orient_global] or [orient_local] to change the frame orientation mode" );
this->m_reply.addString ( "Insert a command with the following format:" );
this->m_reply.addString ( "[link] [force] [torque] [duration]" );
this->m_reply.addString ( "e.g. chest 10 0 0 0 0 0 1");
this->m_reply.addString ( "[link]: (string) Link ID of the robot as specified in robot's SDF" );
this->m_reply.addString ( "[force]: (double x, y, z) Force components in N w.r.t. world reference frame" );
this->m_reply.addString ( "[torque]: (double x, y, z) Torque components in N.m w.r.t world reference frame" );
this->m_reply.addString ( "[force]: (double x, y, z) Force components in N w.r.t. world reference frame (or link reference frame if [orient_local] is selected)" );
this->m_reply.addString ( "[torque]: (double x, y, z) Torque components in N.m w.r.t world reference frame (or link reference frame if [orient_local] is selected)" );
this->m_reply.addString ( "[duration]: (double) Duration of the applied force in seconds" );
this->m_reply.addString ( "Note: The reference frame is the base/root robot frame with x pointing backwards and z upwards.");
this->m_reply.addString ( "Note: If orientation is set to [orient_global], the reference frame is the base/root robot frame with x pointing backwards and z upwards.");
this->m_rpcPort.reply ( this->m_reply );
} else{
if((command.size() == 8) && (command.get(0).isString() \
Expand Down Expand Up @@ -216,27 +231,85 @@ void RPCServerThread::run()
}
else if (command.size() == 1 && command.get(0).isString()) {

this->m_mode = command.get(0).asString();
if (command.get(0).asString() == "single") {

this->m_mode = command.get(0).asString();

this->m_message = command.get(0).asString() + " wrench operation mode set";

// Reset wrench count
wrenchCount = 0;

// Delete the previous wrenches
if (wrenchesVector.size() != 0) {
this->m_message = this->m_message + " . Clearing previous wrenches.";
for (int i = 0; i < wrenchesVector.size(); i++)
{
ExternalWrench wrench = wrenchesVector.at(i);
wrench.deleteWrench();
}
wrenchesVector.clear();
}
}
else if (command.get(0).asString() == "multiple") {

this->m_mode = command.get(0).asString();

this->m_message = command.get(0).asString() + " wrench operation mode set";

// Reset wrench count
wrenchCount = 0;

// Delete the previous wrenches
if (wrenchesVector.size() != 0) {
this->m_message = this->m_message + " . Clearing previous wrenches.";
for (int i = 0; i < wrenchesVector.size(); i++)
{
ExternalWrench wrench = wrenchesVector.at(i);
wrench.deleteWrench();
}
wrenchesVector.clear();
}
}
else if (command.get(0).asString() == "orient_global") {
this->m_orient = command.get(0).asString();

this->m_message = command.get(0).asString() + " wrench orientation option set";

this->m_message = command.get(0).asString() + " wrench operation mode set";
// Delete the previous wrenches
if (wrenchesVector.size() != 0) {
this->m_message = this->m_message + " . Clearing previous wrenches.";
for (int i = 0; i < wrenchesVector.size(); i++)
{
ExternalWrench wrench = wrenchesVector.at(i);
wrench.deleteWrench();
}
wrenchesVector.clear();
}
}
else if (command.get(0).asString() == "orient_local") {
this->m_orient = command.get(0).asString();

// Reset wrench count
wrenchCount = 0;
this->m_message = command.get(0).asString() + " wrench orientation option set";

// Delete the previous wrenches
if (wrenchesVector.size() != 0) {
this->m_message = this->m_message + " . Clearing previous wrenches.";
for (int i = 0; i < wrenchesVector.size(); i++)
{
ExternalWrench wrench = wrenchesVector.at(i);
wrench.deleteWrench();
// Delete the previous wrenches
if (wrenchesVector.size() != 0) {
this->m_message = this->m_message + " . Clearing previous wrenches.";
for (int i = 0; i < wrenchesVector.size(); i++)
{
ExternalWrench wrench = wrenchesVector.at(i);
wrench.deleteWrench();
}
wrenchesVector.clear();
}
wrenchesVector.clear();
}
else {
this->m_reply.clear();
this->m_message = "ERROR: Incorrect command format. Insert [help] to know the correct command format";
}

this->m_reply.addString (m_message);
this->m_rpcPort.reply ( m_reply );

}
else {
this->m_reply.clear();
Expand Down
135 changes: 128 additions & 7 deletions plugins/externalwrench/src/ExternalWrench.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ bool ExternalWrench::setWrench(physics::ModelPtr& _model,yarp::os::Bottle& cmd)
else return false;
}

void ExternalWrench::applyWrench()
void ExternalWrench::applyGlobalOrientationWrench()
{
if((tock-tick) < wrench.duration)
{
Expand All @@ -147,7 +147,12 @@ void ExternalWrench::applyWrench()
ignition::math::Vector3d newY = newZ.Cross (newX);
ignition::math::Matrix4d rotation = ignition::math::Matrix4d (newX[0],newY[0],newZ[0],0,newX[1],newY[1],newZ[1],0,newX[2],newY[2],newZ[2],0, 0, 0, 0, 1);
ignition::math::Quaterniond forceOrientation = rotation.Rotation();
ignition::math::Pose3d linkCoGPose (linkCoGPos - rotation*ignition::math::Vector3d ( 0,0,.15 ), forceOrientation);

//For the cylindrical shapes used to visualize the applied forces
const ignition::math::Vector3d cylinderHalfLength = ignition::math::Vector3d ( 0,0,-0.15 );

//Construct the pose of the cylindrical shape putting the cylinder end at force location
ignition::math::Pose3d cylinderPose (linkCoGPos - rotation*cylinderHalfLength, forceOrientation);
#else
math::Vector3d force (wrench.force[0], wrench.force[1], wrench.force[2]);
math::Vector3d torque (wrench.torque[0], wrench.torque[1], wrench.torque[2]);
Expand All @@ -157,17 +162,133 @@ void ExternalWrench::applyWrench()

math::Vector3d linkCoGPos = link->WorldCoGPose().Pos(); // Get link's COG position where wrench will be applied
math::Vector3d newZ = force.Normalize(); // normalized force. I want the z axis of the cylinder's reference frame to coincide with my force vector
math::Vector3d newX = newZ.Cross (ignition::math::Vector3d::UnitZ);
math::Vector3d newX = newZ.Cross (math::Vector3d::UnitZ);
math::Vector3d newY = newZ.Cross (newX);
math::Matrix4d rotation = ignition::math::Matrix4d (newX[0],newY[0],newZ[0],0,newX[1],newY[1],newZ[1],0,newX[2],newY[2],newZ[2],0, 0, 0, 0, 1);
math::Matrix4d rotation = math::Matrix4d (newX[0],newY[0],newZ[0],0,newX[1],newY[1],newZ[1],0,newX[2],newY[2],newZ[2],0, 0, 0, 0, 1);
math::Quaterniond forceOrientation = rotation.Rotation();
math::Pose3d linkCoGPose (linkCoGPos - rotation*ignition::math::Vector3d ( 0,0,.15 ), forceOrientation);

//For the cylindrical shapes used to visualize the applied forces
const math::Vector3d cylinderHalfLength = math::Vector3d ( 0,0,-0.15 );

//Construct the pose of the cylindrical shape putting the cylinder end at force location
math::Pose3d cylinderPose (linkCoGPos - rotation*cylinderHalfLength, forceOrientation);
#endif

#if GAZEBO_MAJOR_VERSION == 7
msgs::Set(visualMsg.mutable_pose(), cylinderPose.Ign());
#else
msgs::Set(visualMsg.mutable_pose(), cylinderPose);
#endif
#if GAZEBO_MAJOR_VERSION >= 9
msgs::Set(visualMsg.mutable_material()->mutable_ambient(), ignition::math::Color(color[0],color[1],color[2],color[3]));
#else
msgs::Set(visualMsg.mutable_material()->mutable_ambient(),common::Color(color[0],color[1],color[2],color[3]));
#endif
visualMsg.set_visible(1);
visPub->Publish(visualMsg);
}
else
{
deleteWrench();
}
}

void ExternalWrench::applyLocalOrientationWrench()
{
if((tock-tick) < wrench.duration)
{
#if GAZEBO_MAJOR_VERSION >= 8
ignition::math::Vector3d force (wrench.force[0], wrench.force[1], wrench.force[2]);
ignition::math::Vector3d torque (wrench.torque[0], wrench.torque[1], wrench.torque[2]);

//Apply the wrench locally to the link
link->AddRelativeForce(force);
link->AddRelativeTorque(torque);

//The abbreviations below are used in the following section
//Frames:
//World frame = W
//Link frame = L
//Link CoG frame = Lg
//Applied Force frame = F

//Quantities:
//Transformation = T
//Position 3d = P
//Rotation Matrix = R

//Examples:
//W_T_L = the Transformation from the World frame to the Link frame
//Lg_R_F = the Rotation from the Link CoG frame to the Applied Force frame

//Transformation from Link CoG (Lg) frame -> Applied Force (F) frame
ignition::math::Vector3d newZ = force.Normalized(); // normalized force. I want the z axis of the cylinder's reference frame to coincide with my force vector
ignition::math::Vector3d newX = newZ.Cross (ignition::math::Vector3d::UnitZ);
ignition::math::Vector3d newY = newZ.Cross (newX);

ignition::math::Matrix4d Lg_T_F = ignition::math::Matrix4d (newX[0],newY[0],newZ[0],0,newX[1],newY[1],newZ[1],0,newX[2],newY[2],newZ[2],0, 0, 0, 0, 1);

//Transformation from World frame -> Link CoG (Lg) frame
ignition::math::Matrix4d W_T_Lg = ignition::math::Matrix4d (link->WorldCoGPose());

//Transformation from World frame -> Applied Force (F) frame
ignition::math::Matrix4d W_T_F = W_T_Lg*Lg_T_F;
HosameldinMohamed marked this conversation as resolved.
Show resolved Hide resolved
ignition::math::Quaterniond W_R_F = W_T_F.Rotation();

//For the cylindrical shapes used to visualize the applied forces
const ignition::math::Vector3d cylinderHalfLength = ignition::math::Vector3d ( 0,0,-0.15 );
HosameldinMohamed marked this conversation as resolved.
Show resolved Hide resolved

//Construct the pose of the cylindrical shape putting the cylinder end at force location
ignition::math::Pose3d cylinderPose (W_T_F*cylinderHalfLength, W_R_F);
#else
math::Vector3d force (wrench.force[0], wrench.force[1], wrench.force[2]);
math::Vector3d torque (wrench.torque[0], wrench.torque[1], wrench.torque[2]);

//Apply the wrench locally to the link
link->AddRelativeForce(force);
link->AddRelativeTorque(torque);

//The abbreviations below are used in the following section
//Frames:
//World frame = W
//Link frame = L
//Link CoG frame = Lg
//Applied Force frame = F

//Quantities:
//Transformation = T
//Position 3d = P
//Rotation Matrix = R

//Examples:
//W_T_L = the Transformation from the World frame to the Link frame
//Lg_R_F = the Rotation from the Link CoG frame to the Applied Force frame

//Transformation from Link CoG (Lg) frame -> Applied Force (F) frame
math::Vector3d newZ = force.Normalized(); // normalized force. I want the z axis of the cylinder's reference frame to coincide with my force vector
math::Vector3d newX = newZ.Cross (math::Vector3d::UnitZ);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider here the UnitZ of the Gazebo world ? or the UnitZ of the link?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the current setting is OK (in Gazebo world). Since UnitZ is used to construct the transformation from the applied force frame to the Link CoG frame (Lg_T_F), which then is multiplied to the transformation between the world frame and the Link CoG frame:

        ignition::math::Matrix4d W_T_F = W_T_Lg*Lg_T_F;

In this line for example.

math::Vector3d newY = newZ.Cross (newX);

math::Matrix4d Lg_T_F = math::Matrix4d (newX[0],newY[0],newZ[0],0,newX[1],newY[1],newZ[1],0,newX[2],newY[2],newZ[2],0, 0, 0, 0, 1);

//Transformation from World frame -> Link CoG (Lg) frame
math::Matrix4d W_T_Lg = math::Matrix4d (link->WorldCoGPose());

//Transformation from World frame -> Applied Force (F) frame
math::Matrix4d W_T_F = W_T_Lg*Lg_T_F;
math::Quaterniond W_R_F = W_T_F.Rotation();

//For the cylindrical shapes used to visualize the applied forces
const math::Vector3d cylinderHalfLength = math::Vector3d ( 0,0,-0.15 );

//Construct the pose of the cylindrical shape putting the cylinder end at force location
math::Pose3d cylinderPose (W_T_F*cylinderHalfLength, W_R_F);
#endif

#if GAZEBO_MAJOR_VERSION == 7
msgs::Set(visualMsg.mutable_pose(), linkCoGPose.Ign());
msgs::Set(visualMsg.mutable_pose(), cylinderPose.Ign());
#else
msgs::Set(visualMsg.mutable_pose(), linkCoGPose);
msgs::Set(visualMsg.mutable_pose(), cylinderPose);
#endif
#if GAZEBO_MAJOR_VERSION >= 9
msgs::Set(visualMsg.mutable_material()->mutable_ambient(), ignition::math::Color(color[0],color[1],color[2],color[3]));
Expand Down