roboapi 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roboapi/__init__.py +2 -0
- roboapi/calibration/__init__.py +6 -0
- roboapi/calibration/profile.py +139 -0
- roboapi/calibration/session.py +350 -0
- roboapi/cli.py +215 -0
- roboapi/config/__init__.py +22 -0
- roboapi/config/loader.py +119 -0
- roboapi/config/models.py +191 -0
- roboapi/embodiment.py +251 -0
- roboapi/errors.py +37 -0
- roboapi/geometry.py +337 -0
- roboapi/hardware/__init__.py +5 -0
- roboapi/hardware/base.py +113 -0
- roboapi/hardware/camera.py +167 -0
- roboapi/hardware/franka/__init__.py +5 -0
- roboapi/hardware/franka/arm.py +476 -0
- roboapi/hardware/franka/desk.py +555 -0
- roboapi/hardware/franka/feedback.py +196 -0
- roboapi/hardware/franka/guiding.py +104 -0
- roboapi/hardware/franka/ik.py +543 -0
- roboapi/hardware/franka/limits.py +110 -0
- roboapi/hardware/franka/settings.py +116 -0
- roboapi/hardware/robotiq.py +350 -0
- roboapi/hardware/ur/__init__.py +16 -0
- roboapi/hardware/ur/build.py +92 -0
- roboapi/hardware/ur/compliant.py +128 -0
- roboapi/hardware/ur/dashboard.py +103 -0
- roboapi/hardware/ur/feedback.py +159 -0
- roboapi/hardware/ur/ipc.py +206 -0
- roboapi/hardware/ur/native/CMakeLists.txt +37 -0
- roboapi/hardware/ur/native/dependencies.lock.json +13 -0
- roboapi/hardware/ur/native/include/ur_cpp/cartesian_speedl.hpp +170 -0
- roboapi/hardware/ur/native/include/ur_cpp/control_loop.hpp +126 -0
- roboapi/hardware/ur/native/include/ur_cpp/geom.hpp +72 -0
- roboapi/hardware/ur/native/include/ur_cpp/motion_primitives.hpp +350 -0
- roboapi/hardware/ur/native/include/ur_cpp/robot_io.hpp +26 -0
- roboapi/hardware/ur/native/include/ur_cpp/streaming_pose.hpp +369 -0
- roboapi/hardware/ur/native/src/control_loop.cpp +512 -0
- roboapi/hardware/ur/native/src/robot_io_urcl.cpp +143 -0
- roboapi/hardware/ur/native/tests/cartesian_speedl_unit.cpp +113 -0
- roboapi/hardware/ur/native/tests/control_modes_unit.cpp +137 -0
- roboapi/hardware/ur/native/tests/streaming_pose_unit.cpp +348 -0
- roboapi/hardware/ur/native/tests/velocity_governor_unit.cpp +65 -0
- roboapi/hardware/ur/native/worker.cpp +147 -0
- roboapi/hardware/ur/position.py +253 -0
- roboapi/hardware/ur/settings.py +95 -0
- roboapi/images.py +16 -0
- roboapi/initialize.py +137 -0
- roboapi/mcp.py +497 -0
- roboapi/motion.py +125 -0
- roboapi/naming.py +7 -0
- roboapi/operations.py +45 -0
- roboapi/policy/__init__.py +7 -0
- roboapi/policy/client.py +277 -0
- roboapi/policy/protocol.py +166 -0
- roboapi/policy/settings.py +79 -0
- roboapi/product-names.json +12 -0
- roboapi/records.py +149 -0
- roboapi/runtime.py +911 -0
- roboapi/semantics.py +6 -0
- roboapi/tasks.py +337 -0
- roboapi/web/__init__.py +5 -0
- roboapi/web/presets.py +44 -0
- roboapi/web/server.py +419 -0
- roboapi/web/static/assets/Bundler-LICENSE.txt +48 -0
- roboapi/web/static/assets/CalibrationScene-BO_spRoL.js +4220 -0
- roboapi/web/static/assets/Inter-OFL.txt +93 -0
- roboapi/web/static/assets/Inter-Variable-c8O0ljhh.ttf +0 -0
- roboapi/web/static/assets/Lucide-LICENSE.txt +43 -0
- roboapi/web/static/assets/Meshoptimizer-LICENSE.txt +23 -0
- roboapi/web/static/assets/MiSans-Regular-ES4DrbaF.woff2 +0 -0
- roboapi/web/static/assets/MiSans-Semibold-B34HIk9C.woff2 +0 -0
- roboapi/web/static/assets/PokeBrand-NOTICE.txt +20 -0
- roboapi/web/static/assets/React-LICENSE.txt +23 -0
- roboapi/web/static/assets/Three-LICENSE.txt +23 -0
- roboapi/web/static/assets/home-reference-CTpf8-oV.png +0 -0
- roboapi/web/static/assets/index-CGLhpUm0.js +28 -0
- roboapi/web/static/assets/index-QXqV-fDC.css +1 -0
- roboapi/web/static/assets/robotiq-ace-pro-2-EJuoOXTy.glb +0 -0
- roboapi/web/static/index.html +15 -0
- roboapi-0.4.2.dist-info/METADATA +205 -0
- roboapi-0.4.2.dist-info/RECORD +94 -0
- roboapi-0.4.2.dist-info/WHEEL +5 -0
- roboapi-0.4.2.dist-info/entry_points.txt +2 -0
- roboapi-0.4.2.dist-info/licenses/LICENSE +202 -0
- roboapi-0.4.2.dist-info/licenses/NOTICE +16 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Bundler-LICENSE.txt +48 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Inter-OFL.txt +93 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Lucide-LICENSE.txt +43 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Meshoptimizer-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/PokeBrand-NOTICE.txt +20 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/React-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Three-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
// Cartesian reference generator, independent of robot I/O and wall-clock timing.
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <array>
|
|
6
|
+
#include <cmath>
|
|
7
|
+
#include <cstddef>
|
|
8
|
+
#include <limits>
|
|
9
|
+
|
|
10
|
+
#include "ur_cpp/motion_primitives.hpp"
|
|
11
|
+
|
|
12
|
+
inline double streamingPosePeriod(const Args &args) {
|
|
13
|
+
return std::isfinite(args.freq_) && args.freq_ > 0.0 ? 1.0 / args.freq_ : 0.002;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// A moving-target follower that retains Ruckig velocity and acceleration
|
|
17
|
+
// between updates. last_vel_ is a spatial
|
|
18
|
+
// twist; it must not be converted from a rotvec rate a second time.
|
|
19
|
+
struct StreamingPoseShaper {
|
|
20
|
+
Args args_;
|
|
21
|
+
double gain_ = 15.0;
|
|
22
|
+
double dt_ = 0.002;
|
|
23
|
+
TwistGovernor tracker_;
|
|
24
|
+
vec6 last_cmd_{}, last_vel_{}, previous_error_{};
|
|
25
|
+
Quat orientation_{1.0, 0.0, 0.0, 0.0};
|
|
26
|
+
bool have_last_ = false;
|
|
27
|
+
bool errors_ = false;
|
|
28
|
+
|
|
29
|
+
struct PositionBounds {
|
|
30
|
+
double min = std::numeric_limits<double>::infinity();
|
|
31
|
+
double max = -std::numeric_limits<double>::infinity();
|
|
32
|
+
bool valid = false;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
explicit StreamingPoseShaper(const Args &args, double gain = 15.0)
|
|
36
|
+
: args_(args), gain_(gain), dt_(streamingPosePeriod(args)),
|
|
37
|
+
tracker_(dt_, args.cmd_vel_trans_, args.cmd_vel_rot_, args.cmd_acc_trans_,
|
|
38
|
+
args.cmd_acc_rot_, 20.0 * args.cmd_acc_trans_, 20.0 * args.cmd_acc_rot_) {}
|
|
39
|
+
|
|
40
|
+
static bool plausiblePose(const vec6 &pose) {
|
|
41
|
+
// Bound intermediate arithmetic before rotation-vector conversion;
|
|
42
|
+
// physical workspace bounds are checked by the control law.
|
|
43
|
+
constexpr double kNumericPoseLimit = 1e6;
|
|
44
|
+
return finite6(pose) && std::all_of(pose.begin(), pose.end(),
|
|
45
|
+
[](double v) { return std::abs(v) <= kNumericPoseLimit; });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
bool validLimits(int axis) const {
|
|
49
|
+
const double maxv = axis < 3 ? args_.cmd_vel_trans_ : args_.cmd_vel_rot_;
|
|
50
|
+
const double maxa = axis < 3 ? args_.cmd_acc_trans_ : args_.cmd_acc_rot_;
|
|
51
|
+
return std::isfinite(maxv) && maxv > 0.0 && std::isfinite(maxa) && maxa > 0.0 &&
|
|
52
|
+
std::isfinite(20.0 * maxa) && std::isfinite(dt_) && dt_ > 0.0 && std::isfinite(gain_) &&
|
|
53
|
+
gain_ > 0.0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void sync(const vec6 &pose, const vec6 &velocity = {}) {
|
|
57
|
+
last_cmd_ = pose;
|
|
58
|
+
last_vel_ = velocity;
|
|
59
|
+
previous_error_ = {};
|
|
60
|
+
orientation_ =
|
|
61
|
+
plausiblePose(pose) ? rotvecToQuat(pose[3], pose[4], pose[5]) : Quat{1.0, 0.0, 0.0, 0.0};
|
|
62
|
+
tracker_.reset(velocity);
|
|
63
|
+
have_last_ = plausiblePose(pose) && finite6(velocity);
|
|
64
|
+
errors_ = !have_last_;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
vec6 reject() {
|
|
68
|
+
errors_ = true;
|
|
69
|
+
return last_cmd_;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static bool addPoint(PositionBounds &bounds, double p) {
|
|
73
|
+
if (!std::isfinite(p))
|
|
74
|
+
return false;
|
|
75
|
+
bounds.min = std::min(bounds.min, p);
|
|
76
|
+
bounds.max = std::max(bounds.max, p);
|
|
77
|
+
bounds.valid = true;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Add a cubic jerk segment, clipped to [0, horizon]. Every field of a
|
|
82
|
+
// positive-duration segment must be finite: silently skipping a malformed
|
|
83
|
+
// segment would turn a numerical failure into an apparent safe stop.
|
|
84
|
+
static bool addSegmentBounds(PositionBounds &bounds, double p, double v, double a, double j,
|
|
85
|
+
double duration, double horizon, double &end_p, double &end_v,
|
|
86
|
+
double &end_a) {
|
|
87
|
+
if (!std::isfinite(duration) || duration < 0.0 || !std::isfinite(horizon) || horizon < 0.0)
|
|
88
|
+
return false;
|
|
89
|
+
if (!std::isfinite(p) || !std::isfinite(v) || !std::isfinite(a) || !std::isfinite(j))
|
|
90
|
+
return false;
|
|
91
|
+
const double used = std::min(duration, horizon);
|
|
92
|
+
auto eval = [&](long double t) {
|
|
93
|
+
return static_cast<double>(p + t * (v + t * (a / 2.0L + t * j / 6.0L)));
|
|
94
|
+
};
|
|
95
|
+
auto add = [&](long double t) {
|
|
96
|
+
if (!std::isfinite(t))
|
|
97
|
+
return false;
|
|
98
|
+
if (t < 0.0L || t > static_cast<long double>(used))
|
|
99
|
+
return true;
|
|
100
|
+
return addPoint(bounds, eval(t));
|
|
101
|
+
};
|
|
102
|
+
if (!add(0.0) || !add(used))
|
|
103
|
+
return false;
|
|
104
|
+
|
|
105
|
+
// Position extrema occur where v(t)=0. Avoid Profile::pf and
|
|
106
|
+
// get_position_extrema(): velocity-interface profiles do not initialize
|
|
107
|
+
// those fields consistently in the pinned Ruckig commit.
|
|
108
|
+
if (j == 0.0) {
|
|
109
|
+
if (a != 0.0 && !add(-static_cast<long double>(v) / a))
|
|
110
|
+
return false;
|
|
111
|
+
} else {
|
|
112
|
+
const long double qa = static_cast<long double>(j) / 2.0L;
|
|
113
|
+
const long double qb = a, qc = v;
|
|
114
|
+
const long double discriminant = qb * qb - 4.0L * qa * qc;
|
|
115
|
+
if (!std::isfinite(discriminant))
|
|
116
|
+
return false;
|
|
117
|
+
if (discriminant >= 0.0L) {
|
|
118
|
+
const long double root = std::sqrt(discriminant);
|
|
119
|
+
// Stable quadratic roots: avoid cancellation when |a| ~= sqrt(D).
|
|
120
|
+
const long double q = -0.5L * (qb + std::copysign(root, qb));
|
|
121
|
+
if (!std::isfinite(root))
|
|
122
|
+
return false;
|
|
123
|
+
if (q == 0.0L) {
|
|
124
|
+
if (!add(-qb / (2.0L * qa)))
|
|
125
|
+
return false;
|
|
126
|
+
} else if (!add(q / qa) || !add(qc / q)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Never evaluate a future part of a segment outside the requested horizon.
|
|
132
|
+
end_p = eval(used);
|
|
133
|
+
end_v = static_cast<double>(v + static_cast<long double>(used) *
|
|
134
|
+
(a + static_cast<long double>(used) * j / 2.0L));
|
|
135
|
+
end_a = static_cast<double>(a + static_cast<long double>(used) * j);
|
|
136
|
+
return std::isfinite(end_p) && std::isfinite(end_v) && std::isfinite(end_a);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Bounds over a trajectory's first `horizon` seconds. Arrival shaping
|
|
140
|
+
// evaluates the next period and the subsequent deceleration to the target. The horizon may
|
|
141
|
+
// exceed the trajectory duration: at_time() then extrapolates its final velocity and
|
|
142
|
+
// acceleration, including when the entire trajectory has zero duration.
|
|
143
|
+
static bool trajectoryBounds(const ruckig::Trajectory<1> &trajectory, double horizon,
|
|
144
|
+
double fallback_position, PositionBounds &bounds) {
|
|
145
|
+
bounds = {};
|
|
146
|
+
if (!std::isfinite(horizon) || horizon < 0.0 || !std::isfinite(fallback_position) ||
|
|
147
|
+
!std::isfinite(trajectory.get_duration()) || trajectory.get_duration() < 0.0)
|
|
148
|
+
return false;
|
|
149
|
+
const auto profiles = trajectory.get_profiles();
|
|
150
|
+
if (profiles.size() != 1)
|
|
151
|
+
return false;
|
|
152
|
+
const auto &profile = profiles[0][0];
|
|
153
|
+
if (!std::isfinite(profile.brake.duration) || profile.brake.duration < 0.0)
|
|
154
|
+
return false;
|
|
155
|
+
|
|
156
|
+
// Explicit endpoint checks cover zero-duration trajectories and ensure the
|
|
157
|
+
// caller's initial position agrees with Ruckig's actual local coordinates.
|
|
158
|
+
std::array<double, 1> p{}, v{}, a{};
|
|
159
|
+
trajectory.at_time(0.0, p, v, a);
|
|
160
|
+
if (!std::isfinite(p[0]) || !std::isfinite(v[0]) || !std::isfinite(a[0]) ||
|
|
161
|
+
std::abs(p[0] - fallback_position) > 1e-9 * (1.0 + std::abs(fallback_position)) ||
|
|
162
|
+
!addPoint(bounds, p[0]))
|
|
163
|
+
return false;
|
|
164
|
+
trajectory.at_time(horizon, p, v, a);
|
|
165
|
+
if (!std::isfinite(p[0]) || !std::isfinite(v[0]) || !std::isfinite(a[0]) ||
|
|
166
|
+
!addPoint(bounds, p[0]))
|
|
167
|
+
return false;
|
|
168
|
+
|
|
169
|
+
double elapsed = 0.0;
|
|
170
|
+
auto add = [&](double p, double v, double a, double j, double duration) {
|
|
171
|
+
double end_p = 0.0, end_v = 0.0, end_a = 0.0;
|
|
172
|
+
return addSegmentBounds(bounds, p, v, a, j, duration, horizon - elapsed, end_p, end_v, end_a);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// A BrakeProfile with duration==0 need not initialize any of its arrays.
|
|
176
|
+
// With an active brake, t[] is initialized but p/v/a of zero-length slots
|
|
177
|
+
// is not. Check durations first, before passing value-array arguments.
|
|
178
|
+
if (profile.brake.duration > 0.0) {
|
|
179
|
+
for (std::size_t i = 0; i < 2; ++i) {
|
|
180
|
+
const double duration = profile.brake.t[i];
|
|
181
|
+
if (!std::isfinite(duration) || duration < 0.0)
|
|
182
|
+
return false;
|
|
183
|
+
if (duration > 0.0 && elapsed < horizon &&
|
|
184
|
+
!add(profile.brake.p[i], profile.brake.v[i], profile.brake.a[i], profile.brake.j[i],
|
|
185
|
+
duration))
|
|
186
|
+
return false;
|
|
187
|
+
elapsed += duration;
|
|
188
|
+
}
|
|
189
|
+
if (!std::isfinite(elapsed) ||
|
|
190
|
+
std::abs(elapsed - profile.brake.duration) > 1e-10 * (1.0 + elapsed))
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
// Pin to the same start time used by Trajectory::at_time, not a rounded
|
|
194
|
+
// sum of the pre-brake segments.
|
|
195
|
+
elapsed = profile.brake.duration;
|
|
196
|
+
double main_duration = 0.0;
|
|
197
|
+
for (std::size_t i = 0; i < 7; ++i) {
|
|
198
|
+
const double duration = profile.t[i];
|
|
199
|
+
if (!std::isfinite(duration) || duration < 0.0)
|
|
200
|
+
return false;
|
|
201
|
+
if (duration > 0.0 && elapsed < horizon &&
|
|
202
|
+
!add(profile.p[i], profile.v[i], profile.a[i], profile.j[i], duration))
|
|
203
|
+
return false;
|
|
204
|
+
main_duration += duration;
|
|
205
|
+
if (!std::isfinite(main_duration) || !std::isfinite(profile.t_sum[i]) ||
|
|
206
|
+
std::abs(main_duration - profile.t_sum[i]) > 1e-10 * (1.0 + main_duration))
|
|
207
|
+
return false;
|
|
208
|
+
elapsed = profile.brake.duration + profile.t_sum[i];
|
|
209
|
+
}
|
|
210
|
+
if (!std::isfinite(elapsed) || elapsed > trajectory.get_duration() + 1e-10 * (1.0 + elapsed))
|
|
211
|
+
return false;
|
|
212
|
+
|
|
213
|
+
// p/v/a.back() are initialized by successful velocity-profile validation,
|
|
214
|
+
// including zero-duration profiles. Never use the unrelated Profile::pf.
|
|
215
|
+
// This tail is required both for synchronization and duration < dt_.
|
|
216
|
+
if (elapsed < horizon) {
|
|
217
|
+
const double tail = horizon - elapsed;
|
|
218
|
+
if (!add(profile.p.back(), profile.v.back(), profile.a.back(), 0.0, tail))
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
return bounds.valid && std::isfinite(bounds.min) && std::isfinite(bounds.max);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
double stoppingLimitedGoal(int axis, double goal, double error) {
|
|
225
|
+
if (axis < 0 || axis >= 6 || !validLimits(axis) || !std::isfinite(goal) ||
|
|
226
|
+
!std::isfinite(error)) {
|
|
227
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
228
|
+
}
|
|
229
|
+
const double maxv = axis < 3 ? args_.cmd_vel_trans_ : args_.cmd_vel_rot_;
|
|
230
|
+
const double maxa = (axis < 3 ? args_.cmd_acc_trans_ : args_.cmd_acc_rot_) / std::sqrt(3.0);
|
|
231
|
+
if (!std::isfinite(maxv) || !std::isfinite(maxa) || maxv <= 0.0 || maxa <= 0.0) {
|
|
232
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
233
|
+
}
|
|
234
|
+
goal = std::clamp(goal, -maxv, maxv);
|
|
235
|
+
ruckig::Ruckig<1> solver(dt_);
|
|
236
|
+
ruckig::InputParameter<1> input;
|
|
237
|
+
input.control_interface = ruckig::ControlInterface::Velocity;
|
|
238
|
+
input.synchronization = ruckig::Synchronization::None;
|
|
239
|
+
input.current_position = {0.0};
|
|
240
|
+
input.target_position = {0.0};
|
|
241
|
+
input.current_velocity = {tracker_.in_.current_velocity[axis]};
|
|
242
|
+
input.current_acceleration = {tracker_.in_.current_acceleration[axis]};
|
|
243
|
+
input.target_acceleration = {0.0};
|
|
244
|
+
input.max_velocity = {maxv};
|
|
245
|
+
input.max_acceleration = {maxa};
|
|
246
|
+
input.max_jerk = {20.0 * maxa};
|
|
247
|
+
if (!std::isfinite(input.current_velocity[0]) ||
|
|
248
|
+
!std::isfinite(input.current_acceleration[0])) {
|
|
249
|
+
return std::numeric_limits<double>::quiet_NaN();
|
|
250
|
+
}
|
|
251
|
+
if (goal == 0.0 && input.current_velocity[0] == 0.0 && input.current_acceleration[0] == 0.0)
|
|
252
|
+
return 0.0;
|
|
253
|
+
const double direction = std::copysign(1.0, error);
|
|
254
|
+
enum class Fit { WithinTarget, Overshoot, Invalid };
|
|
255
|
+
auto feasible = [&](double velocity) {
|
|
256
|
+
if (!std::isfinite(velocity)) {
|
|
257
|
+
return Fit::Invalid;
|
|
258
|
+
}
|
|
259
|
+
auto test = input;
|
|
260
|
+
test.target_velocity = {velocity};
|
|
261
|
+
ruckig::Trajectory<1> motion, brake;
|
|
262
|
+
if (solver.calculate(test, motion) < 0) {
|
|
263
|
+
return Fit::Invalid;
|
|
264
|
+
}
|
|
265
|
+
std::array<double, 1> p{}, v{}, a{};
|
|
266
|
+
motion.at_time(dt_, p, v, a);
|
|
267
|
+
if (!std::isfinite(p[0]) || !std::isfinite(v[0]) || !std::isfinite(a[0])) {
|
|
268
|
+
return Fit::Invalid;
|
|
269
|
+
}
|
|
270
|
+
auto brake_input = test;
|
|
271
|
+
brake_input.current_position = p;
|
|
272
|
+
brake_input.current_velocity = v;
|
|
273
|
+
brake_input.current_acceleration = a;
|
|
274
|
+
brake_input.target_velocity = {0.0};
|
|
275
|
+
brake_input.target_acceleration = {0.0};
|
|
276
|
+
if (solver.calculate(brake_input, brake) < 0) {
|
|
277
|
+
return Fit::Invalid;
|
|
278
|
+
}
|
|
279
|
+
PositionBounds motion_bounds, brake_bounds;
|
|
280
|
+
if (!trajectoryBounds(motion, dt_, 0.0, motion_bounds) ||
|
|
281
|
+
!trajectoryBounds(brake, brake.get_duration(), p[0], brake_bounds)) {
|
|
282
|
+
return Fit::Invalid;
|
|
283
|
+
}
|
|
284
|
+
const double motion_forward =
|
|
285
|
+
error == 0.0 ? std::max(std::abs(motion_bounds.min), std::abs(motion_bounds.max))
|
|
286
|
+
: (direction > 0.0 ? motion_bounds.max : -motion_bounds.min);
|
|
287
|
+
const double brake_forward =
|
|
288
|
+
error == 0.0 ? std::max(std::abs(brake_bounds.min), std::abs(brake_bounds.max))
|
|
289
|
+
: (direction > 0.0 ? brake_bounds.max : -brake_bounds.min);
|
|
290
|
+
return std::isfinite(motion_forward) && std::isfinite(brake_forward) &&
|
|
291
|
+
std::max(motion_forward, brake_forward) <= std::abs(error) + 1e-10
|
|
292
|
+
? Fit::WithinTarget
|
|
293
|
+
: Fit::Overshoot;
|
|
294
|
+
};
|
|
295
|
+
if (feasible(goal) != Fit::Overshoot)
|
|
296
|
+
return goal;
|
|
297
|
+
const auto stop = feasible(0.0);
|
|
298
|
+
if (stop == Fit::Invalid)
|
|
299
|
+
return goal;
|
|
300
|
+
if (stop == Fit::Overshoot)
|
|
301
|
+
return 0.0;
|
|
302
|
+
double lo = 0.0, hi = 1.0;
|
|
303
|
+
for (int i = 0; i < 16; ++i) {
|
|
304
|
+
const double mid = (lo + hi) / 2.0;
|
|
305
|
+
const auto fit = feasible(mid * goal);
|
|
306
|
+
if (fit == Fit::Invalid)
|
|
307
|
+
return goal;
|
|
308
|
+
if (fit == Fit::WithinTarget)
|
|
309
|
+
lo = mid;
|
|
310
|
+
else
|
|
311
|
+
hi = mid;
|
|
312
|
+
}
|
|
313
|
+
return lo * goal;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
vec6 step(const vec6 &target) {
|
|
317
|
+
errors_ = false;
|
|
318
|
+
if (!have_last_ || !plausiblePose(target) || !std::isfinite(gain_) || gain_ <= 0.0 ||
|
|
319
|
+
!std::isfinite(args_.freq_) || args_.freq_ <= 0.0)
|
|
320
|
+
return reject();
|
|
321
|
+
|
|
322
|
+
vec6 goal{}, geometric_error{};
|
|
323
|
+
for (int i = 0; i < 3; ++i)
|
|
324
|
+
geometric_error[i] = target[i] - last_cmd_[i];
|
|
325
|
+
const auto delta =
|
|
326
|
+
quatMul(rotvecToQuat(target[3], target[4], target[5]), quatConj(orientation_));
|
|
327
|
+
vec6 error{};
|
|
328
|
+
quatToRotvec(delta, error[3], error[4], error[5], &previous_error_);
|
|
329
|
+
previous_error_ = error;
|
|
330
|
+
for (int i = 3; i < 6; ++i)
|
|
331
|
+
geometric_error[i] = error[i];
|
|
332
|
+
if (!finite6(geometric_error))
|
|
333
|
+
return reject();
|
|
334
|
+
for (int i = 0; i < 6; ++i) {
|
|
335
|
+
const double proposed = gain_ * geometric_error[i];
|
|
336
|
+
if (!std::isfinite(proposed))
|
|
337
|
+
return reject();
|
|
338
|
+
goal[i] = stoppingLimitedGoal(i, proposed, geometric_error[i]);
|
|
339
|
+
}
|
|
340
|
+
if (!finite6(goal) || !tracker_.step(goal))
|
|
341
|
+
return reject();
|
|
342
|
+
const auto &displacement = tracker_.displacement_;
|
|
343
|
+
const auto &velocity = tracker_.sent_;
|
|
344
|
+
|
|
345
|
+
vec6 next = last_cmd_;
|
|
346
|
+
for (int i = 0; i < 3; ++i)
|
|
347
|
+
next[i] += displacement[i];
|
|
348
|
+
// Spatial angular velocity uses left quaternion multiplication. This is
|
|
349
|
+
// a local spatial-twist integration; non-commuting multi-axis error and
|
|
350
|
+
// SO(3) branch continuity remain explicit offline acceptance gates.
|
|
351
|
+
auto next_orientation =
|
|
352
|
+
quatMul(rotvecToQuat(displacement[3], displacement[4], displacement[5]), orientation_);
|
|
353
|
+
const double qnorm = std::hypot(std::hypot(next_orientation.w_, next_orientation.x_),
|
|
354
|
+
std::hypot(next_orientation.y_, next_orientation.z_));
|
|
355
|
+
if (!std::isfinite(qnorm) || qnorm < 1e-12)
|
|
356
|
+
return reject();
|
|
357
|
+
next_orientation.w_ /= qnorm;
|
|
358
|
+
next_orientation.x_ /= qnorm;
|
|
359
|
+
next_orientation.y_ /= qnorm;
|
|
360
|
+
next_orientation.z_ /= qnorm;
|
|
361
|
+
quatToRotvec(next_orientation, next[3], next[4], next[5], &last_cmd_);
|
|
362
|
+
if (!plausiblePose(next))
|
|
363
|
+
return reject();
|
|
364
|
+
last_cmd_ = next;
|
|
365
|
+
last_vel_ = velocity;
|
|
366
|
+
orientation_ = next_orientation;
|
|
367
|
+
return last_cmd_;
|
|
368
|
+
}
|
|
369
|
+
};
|