Skip to content

Commit

Permalink
Merge branch 'master' into UnifyCANImplementation
Browse files Browse the repository at this point in the history
  • Loading branch information
DrDab authored Apr 26, 2024
2 parents ed55be0 + 1ece44b commit f5633f0
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 299 deletions.
6 changes: 4 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,12 @@ if(WITH_TESTS)
../tests/control/JacobianControllerTest.cpp
../tests/control/PIDControllerTest.cpp
../tests/control/PlanarArmControllerTest.cpp
# Autonomy tests
# Kinematics tests
../tests/kinematics/DiffDriveKinematicsTest.cpp
../tests/kinematics/SwerveDriveKinematicsTest.cpp
../tests/kinematics/PlanarArmKinematicsTest.cpp
../tests/kinematics/PlanarArmFKTest.cpp
../tests/kinematics/FabrikSolver2DTest.cpp
# Filter tests
../tests/filters/RollingAvgFilterTest.cpp
../tests/filters/EKFTest.cpp
../tests/filters/MultiSensorEKFTest.cpp
Expand Down
14 changes: 10 additions & 4 deletions src/Globals.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "Globals.h"

#include "Constants.h"
#include "kinematics/FabrikSolver.h"
#include "kinematics/PlanarArmFK.h"
#include "world_interface/data.h"

#include <atomic>
Expand Down Expand Up @@ -29,6 +31,13 @@ navtypes::Vectord<IK_MOTORS.size()> getJointLimits(bool getLow) {
return ret;
}

kinematics::ArmKinematics<2, Constants::arm::IK_MOTORS.size()> createArmKinematics() {
auto fk = std::make_shared<kinematics::PlanarArmFK<2>>(getSegLens(), getJointLimits(true),
getJointLimits(false));
auto ik = std::make_shared<kinematics::FabrikSolver2D<2>>(fk, IK_SOLVER_THRESH,
IK_SOLVER_MAX_ITER);
return kinematics::ArmKinematics<2, 2>(fk, ik);
}
} // namespace

namespace Globals {
Expand All @@ -38,10 +47,7 @@ net::websocket::SingleClientWSServer websocketServer("DefaultServer",
std::atomic<bool> E_STOP = false;
std::atomic<bool> AUTONOMOUS = false;
robot::types::mountedperipheral_t mountedPeripheral = robot::types::mountedperipheral_t::none;
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(planarArmKinematics,
control::PlanarArmController<2> planarArmController(createArmKinematics(),
Constants::arm::SAFETY_FACTOR);
std::atomic<bool> armIKEnabled = false;
} // namespace Globals
2 changes: 1 addition & 1 deletion src/Globals.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "control/PlanarArmController.h"
#include "kinematics/ArmKinematics.h"
#include "network/websocket/WebSocketServer.h"
#include "world_interface/data.h"

Expand All @@ -27,7 +28,6 @@ extern net::websocket::SingleClientWSServer websocketServer;
extern std::atomic<bool> E_STOP;
extern std::atomic<bool> AUTONOMOUS;
extern robot::types::mountedperipheral_t mountedPeripheral;
extern const kinematics::PlanarArmKinematics<2> planarArmKinematics;
extern control::PlanarArmController<2> planarArmController;
extern std::atomic<bool> armIKEnabled;
} // namespace Globals
18 changes: 12 additions & 6 deletions src/control/PlanarArmController.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "../kinematics/PlanarArmKinematics.h"
#include "../kinematics/ArmKinematics.h"
#include "../navtypes.h"
#include "../utils/time.h"
#include "../world_interface/data.h"
Expand Down Expand Up @@ -30,12 +30,14 @@ class PlanarArmController {
/**
* @brief Construct a new controller object.
*
* @param kin_obj PlanarArmKinematics object for the arm (should have the same number of
* arm joints).
* @param currJointPos The current joint positions of the arm.
* @param kin_obj ArmKinematics 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. Must be in range (0, 1).
*/
PlanarArmController(kinematics::PlanarArmKinematics<N> kin_obj, const double safetyFactor)
PlanarArmController(const kinematics::ArmKinematics<2, N>& kin_obj,
const double safetyFactor)
: kin(kin_obj), safetyFactor(safetyFactor) {
CHECK_F(safetyFactor > 0.0 && safetyFactor < 1.0);

Expand Down Expand Up @@ -91,7 +93,7 @@ class PlanarArmController {
* @return whether the target joint positions are within the arm controller's radius limit.
*/
static bool is_setpoint_valid(const navtypes::Vectord<N>& targetJointPos,
kinematics::PlanarArmKinematics<N> kin_obj,
kinematics::ArmKinematics<2, N> kin_obj,
double safetyFactor) {
// Compute the new EE position to determine if it is within
// safety factor * length of fully extended arm.
Expand All @@ -100,6 +102,10 @@ class PlanarArmController {
return eeRadius <= maxRadius;
}

const kinematics::ArmKinematics<2, N>& kinematics() const {
return kin;
}

/**
* @brief Sets the end effector setpoint / target position.
*
Expand Down Expand Up @@ -230,7 +236,7 @@ class PlanarArmController {

std::optional<MutableFields> mutableFields;
std::mutex mutex;
const kinematics::PlanarArmKinematics<N> kin;
const kinematics::ArmKinematics<2, N> kin;
const double safetyFactor;

/**
Expand Down
52 changes: 52 additions & 0 deletions src/kinematics/ArmKinematics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include "ForwardArmKinematics.h"
#include "InverseArmKinematics.h"

#include <memory>

#include <Eigen/Core>

namespace kinematics {

/**
* @brief A class that combines the forward and inverse kinematics of an arm.
*
* @tparam D The number of dimensions of the end effector space.
* @tparam N The number of joints in the arm.
*/
template <unsigned int D, unsigned int N>
class ArmKinematics : public ForwardArmKinematics<D, N>, public InverseArmKinematics<D, N> {
public:
ArmKinematics(std::shared_ptr<const ForwardArmKinematics<D, N>> forwardKin,
std::shared_ptr<const InverseArmKinematics<D, N>> inverseKin)
: InverseArmKinematics<D, N>(forwardKin), ik(inverseKin) {}

bool satisfiesConstraints(const navtypes::Vectord<N>& jointPos) const override {
return this->fk->satisfiesConstraints(jointPos);
}

navtypes::Vectord<N> getSegLens() const override {
return this->fk->getSegLens();
}

navtypes::Vectord<D> jointPosToEEPos(const navtypes::Vectord<N>& jointPos) const override {
return this->fk->jointPosToEEPos(jointPos);
}

navtypes::Matrixd<D, N> getJacobian(const navtypes::Vectord<N>& jointPos) const override {
return this->fk->getJacobian(jointPos);
}

protected:
navtypes::Vectord<N> solve(const navtypes::Vectord<D>& eePos,
const navtypes::Vectord<N>& jointAngles,
bool& success) const override {
return ik->eePosToJointPos(eePos, jointAngles, success);
}

private:
const std::shared_ptr<const InverseArmKinematics<D, N>> ik;
};

} // namespace kinematics
53 changes: 16 additions & 37 deletions src/kinematics/FabrikSolver.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#pragma once

#include "../navtypes.h"
#include "InverseArmKinematics.h"
#include "PlanarArmFK.h"

#include <memory>

#include <Eigen/Core>
#include <Eigen/Dense>
Expand All @@ -17,41 +21,24 @@ namespace kinematics {
*
* @see http://www.andreasaristidou.com/FABRIK.html
*/
template <int N> class FabrikSolver2D {
template <int N>
class FabrikSolver2D : public InverseArmKinematics<2, N> {
public:
/**
* @brief Construct an IK solver object.
*
* @param segLens The length of each arm segment.
* @param jointMin The minimum angle of each joint. Set to -pi for no limit.
* @param jointMax The maximum angle of each joint. Set to pi for no limit.
* @param fk The forward kinematics of the arm.
* @param thresh The IK solver will succeed when the EE position is off by at most this
* much.
* @param maxIter The maximum number of iterations to run the solver before failing.
*/
FabrikSolver2D(const navtypes::Arrayd<N>& segLens, const navtypes::Arrayd<N>& jointMin,
const navtypes::Arrayd<N>& jointMax, double thresh, int maxIter)
: segLens(segLens), jointMin(jointMin), jointMax(jointMax), thresh(thresh),
FabrikSolver2D(std::shared_ptr<const PlanarArmFK<N>> fk, double thresh, int maxIter)
: InverseArmKinematics<2, N>(fk), segLens(fk->getSegLens()), thresh(thresh),
maxIter(maxIter) {}

/**
* @brief Solve for the joint angles that yield the given EE position.
*
* @param eePos The desired end effector position.
* @param jointAngles The current joint angles. Used as a starting guess for the IK solver.
* @param[out] success Output parameter that signals if the IK solver succeeded. Failure
* may occur if the target is unreachable or the IK solver exceeds the iteration limit.
* @return navtypes::Arrayd<N> The target joint angles that achieve the desired EE
* position.
*/
navtypes::Arrayd<N> solve(const Eigen::Vector2d& eePos,
const navtypes::Arrayd<N>& jointAngles, bool& success) const {
double armLen = segLens.sum();
if (eePos.norm() >= armLen) {
success = false;
return jointAngles;
}

navtypes::Vectord<N> solve(const Eigen::Vector2d& eePos,
const navtypes::Vectord<N>& jointAngles,
bool& success) const override {
navtypes::Arrayd<N + 1, 2> jointPos = jointAnglesToJointPos(jointAngles);
success = false;
for (int iter = 0; iter < maxIter; iter++) {
Expand Down Expand Up @@ -80,25 +67,17 @@ template <int N> class FabrikSolver2D {
}
}

navtypes::Arrayd<N> targetJointAngles = jointPosToJointAngles(jointPos);
for (int i = 0; i < N; i++) {
if (targetJointAngles[i] > jointMax[i] || targetJointAngles[i] < jointMin[i]) {
success = false;
return jointAngles;
}
}
navtypes::Vectord<N> targetJointAngles = jointPosToJointAngles(jointPos);
return targetJointAngles;
}

private:
navtypes::Arrayd<N> segLens;
navtypes::Arrayd<N> jointMin;
navtypes::Arrayd<N> jointMax;
double thresh;
int maxIter;

navtypes::Arrayd<N + 1, 2>
jointAnglesToJointPos(const navtypes::Arrayd<N>& jointAngles) const {
jointAnglesToJointPos(const navtypes::Vectord<N>& jointAngles) const {
std::array<double, N> jointAngleCumSum;
jointAngleCumSum[0] = jointAngles[0];
for (int i = 1; i < N; i++) {
Expand All @@ -115,12 +94,12 @@ template <int N> class FabrikSolver2D {
return jointPos;
}

navtypes::Arrayd<N>
navtypes::Vectord<N>
jointPosToJointAngles(const navtypes::Arrayd<N + 1, 2>& jointPos) const {
navtypes::Arrayd<N + 1, 3> jointPos3d;
jointPos3d << jointPos, navtypes::Arrayd<N + 1>::Zero();

navtypes::Arrayd<N> jointAngles;
navtypes::Vectord<N> jointAngles;
jointAngles[0] = std::atan2(jointPos(1, 1), jointPos(1, 0));
for (int i = 1; i < N; i++) {
Eigen::RowVector3d prevSeg =
Expand Down
98 changes: 98 additions & 0 deletions src/kinematics/ForwardArmKinematics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#pragma once

#include "../navtypes.h"

#include <Eigen/Core>

namespace kinematics {

/**
* @brief A class that calculates the forward kinematics of an arm.
*
* @tparam D The number of dimensions of the end effector space.
* @tparam N The number of joints in the arm.
*/
template <unsigned int D, unsigned int N>
class ForwardArmKinematics {
public:
virtual ~ForwardArmKinematics() = default;

/**
* @brief Get the number of segments in this arm.
*
* @return constexpr unsigned int The number of segments.
*/
constexpr unsigned int getNumSegments() const {
return N;
}

/**
* @brief Get the number of dimensions of the end effector space.
*
* @return constexpr unsigned int The number of dimensions.
*/
constexpr unsigned int getNumDimensions() const {
return D;
}

/**
* @brief Get the segment lengths for this arm.
*
* @return The vector of segment lengths
*/
virtual navtypes::Vectord<N> getSegLens() const = 0;

/**
* @brief Check if the given joint configuration is valid.
*
* @param jointPos The joint positions to check.
* @return true iff the joint configuration is valid, false otherwise.
*/
virtual bool satisfiesConstraints(const navtypes::Vectord<N>& jointPos) const = 0;

/**
* @brief Get the jacobian matrix for the arm at the given joint angles.
*
* @param jointPos The joint angles of the arm.
* @return navtypes::Matrixd<D, N> The jacobian matrix at the point.
* @see https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant
*/
virtual navtypes::Matrixd<D, N>
getJacobian(const navtypes::Vectord<N>& jointPos) const = 0;

/**
* @brief Given a joint position, calculate the current EE position.
*
* @param jointPos Current joint position.
* @return navtypes::Vectord<D> The EE position.
*/
virtual navtypes::Vectord<D>
jointPosToEEPos(const navtypes::Vectord<N>& jointPos) const = 0;

/**
* @brief Calculate the EE velocity.
*
* @param jointPos Current joint position.
* @param jointVel Current velocity of each joint angle.
* @return navtypes::Vectord<D> The EE velocity.
*/
navtypes::Vectord<D> jointVelToEEVel(const navtypes::Vectord<N>& jointPos,
const navtypes::Vectord<N>& jointVel) const {
return getJacobian(jointPos) * jointVel;
}

/**
* @brief Calculates the joint velocity that yields the desired EE velocity.
*
* @param jointPos The current joint angles.
* @param eeVel The desired EE velocity.
* @return navtypes::Vectord<N> The joint velocities that yields the desired EE velocity.
*/
navtypes::Vectord<N> eeVelToJointVel(const navtypes::Vectord<N>& jointPos,
const Eigen::Vector2d& eeVel) const {
navtypes::Matrixd<2, N> jacobian = getJacobian(jointPos);
return jacobian.colPivHouseholderQR().solve(eeVel);
}
};

} // namespace kinematics
Loading

0 comments on commit f5633f0

Please sign in to comment.