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 planar arm safety factor #267

Merged
merged 34 commits into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
85ea0df
Added safetyFactor check
DrDab Sep 29, 2023
7630036
Move safety factor constant into Constants
DrDab Sep 29, 2023
b5ff95b
Add safetyFactor range limit assertion
DrDab Sep 29, 2023
f033804
Update planar arm tests to use safety factor
DrDab Sep 29, 2023
d7b14ce
Set setpoint in safety factor test
DrDab Sep 29, 2023
5b6bf0b
Test overreaching arm is normalized
DrDab Sep 29, 2023
2646862
Bound resolved joint pos within safety factor
DrDab Sep 29, 2023
fec0cb7
Added within factor test
DrDab Sep 29, 2023
93d0aab
Try shorter extension
DrDab Sep 29, 2023
8e6bc25
Improved formatting
DrDab Sep 30, 2023
dffe2dc
Remove now-obsolete todo
DrDab Sep 30, 2023
10044f6
More formatting fixes
DrDab Sep 30, 2023
4174d9b
Use std abs over fabs in test
DrDab Oct 3, 2023
2735417
Move vector normalization code to own func
DrDab Oct 3, 2023
edff9dd
Add more logic to normalizeVectorWithinRadius
DrDab Oct 3, 2023
affcec0
Fix return type
DrDab Oct 3, 2023
9d0cdfe
Add normalize functionality to set_setpoint
DrDab Oct 3, 2023
62a352a
Actually compute unnormalized setpoint instead of magic num
DrDab Oct 3, 2023
73e01be
Make normalizeVector private
DrDab Oct 3, 2023
cc7c16b
Rename input vector to better reflect end effector
DrDab Oct 3, 2023
0e03abe
Make normalize func name more descriptive
DrDab Oct 3, 2023
0cfc214
Add actual vector comparison
DrDab Oct 3, 2023
7edb33b
Refactored redundant code
DrDab Oct 7, 2023
fbca180
Remove unnecessary checks
DrDab Oct 13, 2023
2007f99
Minor cleanup
DrDab Oct 13, 2023
dfa279f
Fixed tests
DrDab Oct 17, 2023
03cc605
More cleanup
DrDab Oct 17, 2023
a458dbb
Improve formatting
DrDab Oct 17, 2023
699c681
More formatting improvements
DrDab Oct 17, 2023
8b41608
Merge branch 'master' into addPlanarArmSafetyFactor
DrDab Oct 17, 2023
95306c6
Rm unnecessary INFO wrapping
DrDab Oct 20, 2023
c557aa1
Remove unused constant
DrDab Oct 20, 2023
81086c2
Improve REQUIRE logging
DrDab Oct 20, 2023
109ecb3
Add safety factor logic in PlanarArmController constructor
DrDab Oct 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions src/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,6 @@ constexpr double MAX_WHEEL_VEL = WHEEL_RADIUS * MAX_DRIVE_PWM / PWM_PER_RAD_PER_
const double MAX_DTHETA = kinematics::DiffDriveKinematics(EFF_WHEEL_BASE)
.wheelVelToRobotVel(-MAX_WHEEL_VEL, MAX_WHEEL_VEL)(2);

// Joint limits
// TODO: make sure these are still accurate with the new arm.
constexpr double ARM_BASE_MIN = -M_PI / 2;
constexpr double ARM_BASE_MAX = M_PI / 2;
// constexpr double SHOULDER_MIN = M_PI / 2; // TODO mechanical problem with the moon gear.
// Use this value during actual rover operation.
constexpr double SHOULDER_MIN = 0.0;
constexpr double SHOULDER_MAX = M_PI * 5. / 6.; // Can't quite lie flat
constexpr double ELBOW_MIN = 0.0;
constexpr double ELBOW_MAX = M_PI * 29. / 30.; // I think this should prevent self-collisions

// TODO: We need to recalibrate the camera, since we replaced it with a different one.
// TODO: rename cameras (in MC as well) as appropriate
constexpr const char* AR_CAMERA_CONFIG_PATH = "../camera-config/MastCameraCalibration.yml";
Expand Down Expand Up @@ -146,6 +135,10 @@ constexpr frozen::unordered_map<robot::types::jointid_t, robot::types::motorid_t

// Arm inverse kinematics
namespace arm {
/**
* Percentage of fully extended overall arm length to limit arm extension within.
*/
constexpr double SAFETY_FACTOR = 0.95;

/**
* Maximum commanded end-effector velocity, in m/s
Expand Down
3 changes: 2 additions & 1 deletion src/Globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ robot::types::mountedperipheral_t mountedPeripheral = robot::types::mountedperip
const kinematics::PlanarArmKinematics<Constants::arm::IK_MOTORS.size()>
planarArmKinematics(getSegLens(), getJointLimits(true), getJointLimits(false),
IK_SOLVER_THRESH, IK_SOLVER_MAX_ITER);
control::PlanarArmController<2> planarArmController({0, 0}, planarArmKinematics);
control::PlanarArmController<2> planarArmController({0, 0}, planarArmKinematics,
Constants::arm::SAFETY_FACTOR);
std::atomic<bool> armIKEnabled = false;
} // namespace Globals
41 changes: 30 additions & 11 deletions src/control/PlanarArmController.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ template <unsigned int N> class PlanarArmController {
* @param currJointPos The current joint positions of the arm.
* @param kin_obj PlanarArmKinematics object for the arm (should have the same number of
* arm joints).
* @param safetyFactor the percentage factor to scale maximum arm extension radius by to
* prevent singularity lock.
*/
PlanarArmController(const navtypes::Vectord<N>& currJointPos,
kinematics::PlanarArmKinematics<N> kin_obj)
: kin(kin_obj), setpoint(kin.jointPosToEEPos(currJointPos)), velocity({0.0, 0.0}) {}
kinematics::PlanarArmKinematics<N> kin_obj, const double safetyFactor)
: kin(kin_obj), setpoint(kin.jointPosToEEPos(currJointPos)), velocity({0.0, 0.0}),
DrDab marked this conversation as resolved.
Show resolved Hide resolved
safetyFactor(safetyFactor) {
assert(safetyFactor > 0.0 && safetyFactor < 1.0);
}

/**
* @brief Sets the end effector setpoint / target position.
Expand All @@ -42,7 +47,7 @@ template <unsigned int N> class PlanarArmController {
void set_setpoint(const navtypes::Vectord<N>& targetJointPos) {
Eigen::Vector2d newSetPoint = kin.jointPosToEEPos(targetJointPos);
std::lock_guard<std::mutex> lock(mutex);
setpoint = newSetPoint;
setpoint = normalizeEEWithinRadius(newSetPoint);
}

/**
Expand All @@ -64,14 +69,7 @@ template <unsigned int N> class PlanarArmController {
}

// bounds check (new pos + vel vector <= sum of joint lengths)
double radius = kin.getSegLens().sum();
if (pos.norm() > radius) {
// new position is outside of bounds
// TODO: will need to eventually shrink velocity vector until it is within radius
// instead of just normalizing it
pos.normalize();
}
return pos;
return normalizeEEWithinRadius(pos);
}

/**
Expand Down Expand Up @@ -135,5 +133,26 @@ template <unsigned int N> class PlanarArmController {
Eigen::Vector2d setpoint;
Eigen::Vector2d velocity;
std::optional<robot::types::datatime_t> velTimestamp;
const double safetyFactor;

/**
* @brief Normalize the input vector (end-effector position) to have a set radius,
* while maintaining the same direction it did before if it exceeds that set radius.
*
* @param eePos The end-effector position to normalize.
*/
Eigen::Vector2d normalizeEEWithinRadius(Eigen::Vector2d eePos) {
double radius = kin.getSegLens().sum() * safetyFactor;

if (eePos.norm() > radius) {
// TODO: will need to eventually shrink velocity vector until it is within radius
// instead of just normalizing it.

eePos.normalize();
eePos *= radius;
}

return eePos;
}
};
} // namespace control
49 changes: 48 additions & 1 deletion tests/control/PlanarArmControllerTest.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,60 @@
#include "../../src/control/PlanarArmController.h"

#include "../../src/Constants.h"

#include <iostream>

#include <catch2/catch.hpp>

using namespace Catch::literals;
using namespace control;
using namespace std::chrono_literals;

constexpr double PI = M_PI;
DrDab marked this conversation as resolved.
Show resolved Hide resolved

std::string toString(const Eigen::Vector2d& pose) {
std::stringstream ss;
ss << "(" << pose(0) << ", " << pose(1) << ")";
return ss.str();
}

void assertApprox(const Eigen::Vector2d& p1, const Eigen::Vector2d& p2, double dist = 1e-5,
double angle = 1e-5) {
Eigen::Vector2d diff = p1 - p2;
bool distEqual = diff.norm() <= dist;
double thetaDiff = std::fmod(abs(diff(1)), 2 * PI);
// change domain from [0, 2pi) to (-pi, pi]
if (thetaDiff > PI) {
thetaDiff -= 2 * PI;
}
bool angleEqual = abs(thetaDiff) <= angle;
DrDab marked this conversation as resolved.
Show resolved Hide resolved
if (distEqual && angleEqual) {
SUCCEED();
} else {
std::cout << "Expected: " << toString(p1) << ", Actual: " << toString(p2) << std::endl;
FAIL();
}
DrDab marked this conversation as resolved.
Show resolved Hide resolved
}

TEST_CASE("Test Planar Arm Controller", "[control][planararmcontroller]") {
navtypes::Vectord<2> vec({0, 0});
kinematics::PlanarArmKinematics<2> kin_obj(vec, vec, vec, 0.0, 0);
PlanarArmController<2> foo({0, 0}, kin_obj);
PlanarArmController<2> foo({0, 0}, kin_obj, Constants::arm::SAFETY_FACTOR);
}

TEST_CASE("Test Planar Arm Safety Factor", "[control][planararmcontroller]") {
navtypes::Vectord<2> segLens({4.0, 6.0});
navtypes::Vectord<2> minAngles({-M_PI, -M_PI});
navtypes::Vectord<2> maxAngles({M_PI, M_PI});
kinematics::PlanarArmKinematics<2> kin_obj(segLens, minAngles, maxAngles, 0.0, 0);

PlanarArmController<2> foo({0, 0}, kin_obj, Constants::arm::SAFETY_FACTOR);
foo.set_setpoint({10.0, 0.0});
REQUIRE(foo.get_setpoint(robot::types::dataclock::now()).norm() == 9.5);
DrDab marked this conversation as resolved.
Show resolved Hide resolved

foo.set_setpoint({2.0, 3.0});
Eigen::Vector2d origSetPoint = kin_obj.jointPosToEEPos({2.0, 3.0});
double expectedNorm = origSetPoint.norm();

assertApprox(origSetPoint, foo.get_setpoint(robot::types::dataclock::now()));
}