Resurgence (PY2022)
Codebase for the Husky Robotics 2021-2022 rover Resurgence
Loading...
Searching...
No Matches
PlanarArmController.h
1#pragma once
2
3#include "../kinematics/ArmKinematics.h"
4#include "../navtypes.h"
5#include "../utils/time.h"
6#include "../world_interface/data.h"
7
8#include <array>
9#include <cmath>
10#include <initializer_list>
11#include <loguru.hpp>
12#include <mutex>
13#include <numeric>
14#include <optional>
15
16#include <Eigen/Core>
17
18namespace control {
19
27template <unsigned int N>
29public:
40 const double safetyFactor)
41 : kin(kin_obj), safetyFactor(safetyFactor) {
42 CHECK_F(safetyFactor > 0.0 && safetyFactor < 1.0);
43
44 for (unsigned int i = 0; i < kin_obj.getNumSegments(); i++) {
45 CHECK_F(kin_obj.getSegLens()[i] > 0.0);
46 }
47 }
48
55 : kin(std::move(other.kin)), safetyFactor(other.safetyFactor) {
57 mutableFields = std::move(other.mutableFields);
58 }
59
71 bool tryInitController(const navtypes::Vectord<N>& currJointPos) {
73
74 if (is_setpoint_valid(currJointPos, kin, safetyFactor)) {
75 if (!mutableFields.has_value()) {
76 mutableFields.emplace();
77 }
78
79 Eigen::Vector2d newSetPoint = kin.jointPosToEEPos(currJointPos);
80 mutableFields->setpoint = normalizeEEWithinRadius(newSetPoint);
81 return true;
82 }
83
84 mutableFields.reset();
85 return false;
86 }
87
95 static bool is_setpoint_valid(const navtypes::Vectord<N>& targetJointPos,
97 double safetyFactor) {
98 // Compute the new EE position to determine if it is within
99 // safety factor * length of fully extended arm.
100 double eeRadius = kin_obj.jointPosToEEPos(targetJointPos).norm();
101 double maxRadius = kin_obj.getSegLens().sum() * safetyFactor;
102 return eeRadius <= maxRadius;
103 }
104
105 const kinematics::ArmKinematics<2, N>& kinematics() const {
106 return kin;
107 }
108
114 void set_setpoint(const navtypes::Vectord<N>& targetJointPos) {
115 Eigen::Vector2d newSetPoint = kin.jointPosToEEPos(targetJointPos);
117 mutableFields->setpoint = normalizeEEWithinRadius(newSetPoint);
118 }
119
126 Eigen::Vector2d get_setpoint(robot::types::datatime_t currTime) {
127 Eigen::Vector2d pos;
128 {
130 // calculate current EE setpoint
131 pos = mutableFields->setpoint;
132 if (mutableFields->velTimestamp.has_value()) {
133 double dt =
134 util::durationToSec(currTime - mutableFields->velTimestamp.value());
135 pos += mutableFields->velocity * dt;
136 }
137 }
138
139 // bounds check (new pos + vel vector <= sum of joint lengths)
140 return normalizeEEWithinRadius(pos);
141 }
142
151 void set_x_vel(robot::types::datatime_t currTime, double targetVel,
152 const navtypes::Vectord<N>& jointPos) {
154 if (mutableFields->velTimestamp.has_value()) {
155 // If we recieve a request for 0 velocity and the y velocity is 0, the arm should
156 // stop moving. Set its setpoint to the current joint position to ensure this.
157 if (targetVel == 0.0 && mutableFields->velocity(1) == 0.0) {
158 Eigen::Vector2d newSetPoint = kin.jointPosToEEPos(jointPos);
159 mutableFields->setpoint = normalizeEEWithinRadius(newSetPoint);
160 } else {
161 double dt =
162 util::durationToSec(currTime - mutableFields->velTimestamp.value());
163 // bounds check (new pos + vel vector <= sum of joint lengths)
164 mutableFields->setpoint = normalizeEEWithinRadius(
165 mutableFields->setpoint + mutableFields->velocity * dt);
166 }
167 }
168
169 mutableFields->velocity(0) = targetVel;
170 mutableFields->velTimestamp = currTime;
171 }
172
181 void set_y_vel(robot::types::datatime_t currTime, double targetVel,
182 const navtypes::Vectord<N>& jointPos) {
184 if (mutableFields->velTimestamp.has_value()) {
185 // If we recieve a request for 0 velocity and the x velocity is 0, the arm should
186 // stop moving. Set its setpoint to the current joint position to ensure this.
187 if (mutableFields->velocity(0) == 0.0 && targetVel == 0.0) {
188 Eigen::Vector2d newSetPoint = kin.jointPosToEEPos(jointPos);
189 mutableFields->setpoint = normalizeEEWithinRadius(newSetPoint);
190 } else {
191 double dt =
192 util::durationToSec(currTime - mutableFields->velTimestamp.value());
193 // bounds check (new pos + vel vector <= sum of joint lengths)
194 mutableFields->setpoint = normalizeEEWithinRadius(
195 mutableFields->setpoint + mutableFields->velocity * dt);
196 }
197 }
198
199 mutableFields->velocity(1) = targetVel;
200 mutableFields->velTimestamp = currTime;
201 }
202
211 navtypes::Vectord<N> getCommand(robot::types::datatime_t currTime,
212 const navtypes::Vectord<N>& currJointPos) {
213 Eigen::Vector2d newPos = get_setpoint(currTime);
214 // lock after calling get_setpoint since that internally locks the mutex
216
217 // get new joint positions for target EE
218 bool success = false;
219 navtypes::Vectord<N> jp = kin.eePosToJointPos(newPos, currJointPos, success);
220 mutableFields->velTimestamp = currTime;
221 if (!success) {
222 LOG_F(WARNING, "IK Failure!");
223 mutableFields->velocity.setZero();
224 } else {
225 mutableFields->setpoint = newPos;
226 }
227 return jp;
228 }
229
230private:
231 struct MutableFields {
232 Eigen::Vector2d setpoint;
233 Eigen::Vector2d velocity;
234 std::optional<robot::types::datatime_t> velTimestamp;
235 };
236
237 std::optional<MutableFields> mutableFields;
238 std::mutex mutex;
240 const double safetyFactor;
241
248 Eigen::Vector2d normalizeEEWithinRadius(Eigen::Vector2d eePos) {
249 double radius = kin.getSegLens().sum() * safetyFactor;
250 if (eePos.norm() > (radius + 1e-4)) {
251 // new position is outside of bounds. Set new EE setpoint so it will follow the
252 // velocity vector instead of drifting along the radius.
253 // setpoint = setpoint inside circle
254 // eePos = new setpoint outside circle
255 // radius = ||setpoint + a*(eePos - setpoint)|| solve for a
256 double diffDotProd =
257 (eePos - mutableFields->setpoint).dot(mutableFields->setpoint);
258 double differenceNorm = (eePos - mutableFields->setpoint).squaredNorm();
259 double discriminant =
260 std::pow(diffDotProd, 2) -
261 differenceNorm * (mutableFields->setpoint.squaredNorm() - std::pow(radius, 2));
262 double a = (-diffDotProd + std::sqrt(discriminant)) / differenceNorm;
263 // new constrained eePos = (1 - a) * (ee inside circle) + a * (ee outside circle)
264 eePos = (1 - a) * mutableFields->setpoint + a * eePos;
265 }
266 return eePos;
267 }
268};
269} // namespace control
complex< _Tp > pow(const complex< _Tp > &, int)
complex< _Tp > sqrt(const complex< _Tp > &)
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
void set_x_vel(robot::types::datatime_t currTime, double targetVel, const navtypes::Vectord< N > &jointPos)
Sets the x velocity for the end effector and returns the new command.
Definition PlanarArmController.h:151
Eigen::Vector2d get_setpoint(robot::types::datatime_t currTime)
Gets the current end effector setpoint / target position.
Definition PlanarArmController.h:126
navtypes::Vectord< N > getCommand(robot::types::datatime_t currTime, const navtypes::Vectord< N > &currJointPos)
Get the current command for the arm.
Definition PlanarArmController.h:211
void set_setpoint(const navtypes::Vectord< N > &targetJointPos)
Sets the end effector setpoint / target position.
Definition PlanarArmController.h:114
void set_y_vel(robot::types::datatime_t currTime, double targetVel, const navtypes::Vectord< N > &jointPos)
Sets the y velocity for the end effector and returns the new command.
Definition PlanarArmController.h:181
PlanarArmController(const kinematics::ArmKinematics< 2, N > &kin_obj, const double safetyFactor)
Construct a new controller object.
Definition PlanarArmController.h:39
static bool is_setpoint_valid(const navtypes::Vectord< N > &targetJointPos, kinematics::ArmKinematics< 2, N > kin_obj, double safetyFactor)
Returns whether the target joint positions are within the arm controller's radius limit.
Definition PlanarArmController.h:95
bool tryInitController(const navtypes::Vectord< N > &currJointPos)
Instantiates the PlanarArmController with the current joint positions, returning true if the joint po...
Definition PlanarArmController.h:71
PlanarArmController(PlanarArmController &&other)
Constructs a copy of an existing PlanarArmController object.
Definition PlanarArmController.h:54
A class that combines the forward and inverse kinematics of an arm.
Definition ArmKinematics.h:19
navtypes::Vectord< N > getSegLens() const override
Get the segment lengths for this arm.
Definition ArmKinematics.h:29
navtypes::Vectord< D > jointPosToEEPos(const navtypes::Vectord< N > &jointPos) const override
Given a joint position, calculate the current EE position.
Definition ArmKinematics.h:33
constexpr unsigned int getNumSegments() const
Get the number of segments in this arm.
Definition ForwardArmKinematics.h:25
std::chrono::time_point< dataclock > datatime_t
A point in time as measured by dataclock.
Definition data.h:31
double durationToSec(std::chrono::duration< Rep, Period > dur)
Convert a duration to seconds, as a double.
Definition time.h:18