updated firmware
This commit is contained in:
parent
9891b8ed44
commit
f17ca0c820
11 changed files with 273 additions and 56 deletions
|
|
@ -46,7 +46,7 @@ float MT6835Encoder::read_abs_angle() {
|
|||
return raw_angle * RAW_TO_ANGLE;
|
||||
}
|
||||
|
||||
int32_t MT6835Encoder::read_abs_angle_raw() {
|
||||
MT6835Encoder::AbsRawAngleType MT6835Encoder::read_abs_angle_raw() {
|
||||
uint8_t data[6] = {0};
|
||||
data[0] = MT6835_OP_ANGLE << 4;
|
||||
data[1] = MT6835_REG_ANGLE1;
|
||||
|
|
@ -70,6 +70,10 @@ int32_t MT6835Encoder::read_abs_angle_raw() {
|
|||
return update_abs_raw_angle(raw_angle);
|
||||
}
|
||||
|
||||
MT6835Encoder::AbsRawAngleType MT6835Encoder::get_last_abs_raw_angle() {
|
||||
return abs_raw_angle;
|
||||
}
|
||||
|
||||
int32_t MT6835Encoder::get_rawcounts_per_rev() {
|
||||
return MT6835_CPR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ class MT6835Encoder {
|
|||
|
||||
float read_abs_angle(); // returns the absolute angle in radians
|
||||
AbsRawAngleType read_abs_angle_raw(); // returns the absolute angle in raw counts
|
||||
AbsRawAngleType get_last_abs_raw_angle(); // returns the last read abs raw angle
|
||||
|
||||
int32_t get_rawcounts_per_rev(); // returns the number of raw counts per revolution
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,14 @@
|
|||
#include "path_planner.h"
|
||||
#include "utilities/logging.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
PathPlanner::PathPlanner(IKinemtaicModel* kinematic_model, float time_step) {
|
||||
segment_time_step = time_step;
|
||||
kinematic_model = kinematic_model;
|
||||
|
||||
junction_deviation.linear = 0.0001; // mm
|
||||
junction_deviation.angular = 0.001; // rad
|
||||
}
|
||||
|
||||
PathPlanner::~PathPlanner() {
|
||||
|
|
@ -23,9 +28,6 @@ bool PathPlanner::add_cartesian_path_segment(const CartesianPathSegment& path_se
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO: do look ahead planning of queue
|
||||
new_segment->compute_motion_profile(); // for testing
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -34,9 +36,15 @@ void PathPlanner::process(bool disable_interrupts_for_queue_update) {
|
|||
// the segment stays in the queue until it is completed
|
||||
if(segment_generator == nullptr && ct_path_segment_queue.empty() == false) {
|
||||
auto* current_segment = ct_path_segment_queue.peek();
|
||||
|
||||
// compute motion profile ( active segment can not change anymore )
|
||||
current_segment->compute_motion_profile();
|
||||
|
||||
// create path segment generator
|
||||
segment_generator = new JointSpacePathSegmentGenerator(current_segment,
|
||||
kinematic_model,
|
||||
segment_time_step);
|
||||
|
||||
/*LOG_INFO("Starting segment: duration=%fs, (%f, %f, %f)->(%f, %f, %f) | queue size: %i",
|
||||
current_segment->get_duration(),
|
||||
current_segment->start_pose.translation.x,
|
||||
|
|
@ -97,5 +105,111 @@ int PathPlanner::input_queue_size() {
|
|||
}
|
||||
|
||||
void PathPlanner::run_look_ahead_planning() {
|
||||
if(ct_path_segment_queue.empty())
|
||||
return;
|
||||
|
||||
int n = ct_path_segment_queue.size();
|
||||
auto* last_segment = ct_path_segment_queue.get(n - 1);
|
||||
if(segment_generator != nullptr && last_segment == segment_generator->get_path_segment())
|
||||
return;
|
||||
|
||||
// --- Reverse pass ---
|
||||
// Start from the last segment, set its end velocity to zero (or target)
|
||||
last_segment->end_velocity = LinearAngular{0, 0};
|
||||
|
||||
// Propagate backward the feasible start velocities
|
||||
for (int i = n - 2; i >= 0; i--) {
|
||||
CartesianPathSegment* s1 = ct_path_segment_queue.get(i);
|
||||
CartesianPathSegment* s2 = ct_path_segment_queue.get(i + 1);
|
||||
|
||||
// dont change active segment
|
||||
if(segment_generator != nullptr && s1 == segment_generator->get_path_segment()) {
|
||||
s2->start_velocity = s1->end_velocity;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get minimum acceleration capability at junction
|
||||
float acc = std::min(s1->max_acceleration.linear, s2->max_acceleration.linear);
|
||||
|
||||
// Compute max junction velocity feasible at junction between s1 and s2
|
||||
float max_junction_velocity_linear = compute_max_junction_velocity(
|
||||
s1->translation_delta_normalized,
|
||||
s2->translation_delta_normalized,
|
||||
acc,
|
||||
junction_deviation.linear
|
||||
);
|
||||
|
||||
// no angular junction velocity limit yet
|
||||
float max_junction_velocity_angular = 1e10;
|
||||
|
||||
// compute max velocity delta
|
||||
float v_start_max_linear = std::sqrt(std::max(0.0f, powf(s1->end_velocity.linear, 2.0f) + 2 * s1->max_acceleration.linear * s1->travel_distance.linear));
|
||||
float v_start_max_angular = std::sqrt(std::max(0.0f, powf(s1->end_velocity.angular, 2.0f) + 2 * s1->max_acceleration.angular * s1->travel_distance.angular));
|
||||
|
||||
// compute final junction velocity
|
||||
LinearAngular junction_velocity;
|
||||
junction_velocity.linear = std::min(max_junction_velocity_linear, std::min(s2->target_velocity.linear, v_start_max_linear));
|
||||
junction_velocity.angular = std::min(max_junction_velocity_angular, std::min(s2->target_velocity.angular, v_start_max_angular));
|
||||
|
||||
// set calculated velocity to both path segments
|
||||
s1->end_velocity = junction_velocity;
|
||||
s2->start_velocity = junction_velocity;
|
||||
}
|
||||
|
||||
// --- Forward pass ---
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
CartesianPathSegment* s1 = ct_path_segment_queue.get(i);
|
||||
CartesianPathSegment* s2 = ct_path_segment_queue.get(i + 1);
|
||||
|
||||
// dont change active segment
|
||||
if(segment_generator != nullptr && s1 == segment_generator->get_path_segment()) {
|
||||
s2->start_velocity = s1->end_velocity;
|
||||
continue;
|
||||
}
|
||||
|
||||
float v_start = s1->start_velocity.linear;
|
||||
float v_end_max_linear = std::sqrt(std::max(0.0f, powf(s1->start_velocity.linear, 2.0f) + 2 * s1->max_acceleration.linear * s1->travel_distance.linear));
|
||||
float v_end_max_angular = std::sqrt(std::max(0.0f, powf(s1->start_velocity.angular, 2.0f) + 2 * s1->max_acceleration.angular * s1->travel_distance.angular));
|
||||
|
||||
LinearAngular junction_velocity;
|
||||
if(s1->end_velocity.linear > v_end_max_linear) s1->end_velocity.linear = v_end_max_linear;
|
||||
if(s1->end_velocity.angular > v_end_max_angular) s1->end_velocity.angular = v_end_max_angular;
|
||||
|
||||
s2->start_velocity = s1->end_velocity;
|
||||
}
|
||||
|
||||
// debug
|
||||
// print_cartesian_path_segments();
|
||||
}
|
||||
|
||||
float PathPlanner::compute_max_junction_velocity(const Vec3F& dir_in_normalized, const Vec3F& dir_out_normalized, float acceleration, float junction_deviation) {
|
||||
const float EPSILON = 1e-6f;
|
||||
const float COS_NEAR_STRAIGHT = 0.9999f;
|
||||
const float COS_NEAR_OPPOSITE = -0.9999f;
|
||||
|
||||
// Compute the cosine of the angle between the directions (negative dot product)
|
||||
float cos_theta = -dir_in_normalized.dot(dir_out_normalized);
|
||||
|
||||
// Compute sin(θ/2) using half-angle identity: sin²(θ/2) = (1 - cosθ) / 2
|
||||
float sin_theta_d2 = std::sqrt(std::max(0.0f, 0.5f * (1.0f - cos_theta)));
|
||||
|
||||
// Compute vmax using classical junction deviation formula
|
||||
float denom = std::max(1.0f - sin_theta_d2, EPSILON);
|
||||
float vmax = std::sqrt(acceleration * junction_deviation * sin_theta_d2 / denom);
|
||||
|
||||
return vmax;
|
||||
}
|
||||
|
||||
void PathPlanner::print_cartesian_path_segments() {
|
||||
LOG_INFO("Cartesian Path Segment Info");
|
||||
int n = ct_path_segment_queue.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
CartesianPathSegment* s = ct_path_segment_queue.get(i);
|
||||
LOG_INFO(" Segment %02i: [%f, %f, %f]->[%f, %f, %f] l=%f vs=%fmm/s ve=%fmm/s", i,
|
||||
s->start_pose.translation.x, s->start_pose.translation.y, s->start_pose.translation.z,
|
||||
s->end_pose.translation.x, s->end_pose.translation.y, s->end_pose.translation.z,
|
||||
s->travel_distance.linear,
|
||||
s->start_velocity.linear, s->end_velocity.linear);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class IKinemtaicModel;
|
|||
|
||||
class PathPlanner {
|
||||
public:
|
||||
static constexpr int CT_QUEUE_SIZE = 64;
|
||||
static constexpr int CT_QUEUE_SIZE = 32;
|
||||
static constexpr int JS_QUEUE_SIZE = 32;
|
||||
|
||||
public:
|
||||
|
|
@ -26,6 +26,10 @@ class PathPlanner {
|
|||
// adds a new cartesian space path segment to the planner queue
|
||||
bool add_cartesian_path_segment(const CartesianPathSegment& path_segment);
|
||||
|
||||
// runs look ahead path planning. call this everytime after one or more cartesian
|
||||
// path segments have been added
|
||||
void run_look_ahead_planning();
|
||||
|
||||
// Retrieves the next joint space path segment from the queue, returns false
|
||||
// if queue is empty.
|
||||
bool pop_js_path_segment(JointSpacePathSegment& segment);
|
||||
|
|
@ -44,7 +48,13 @@ class PathPlanner {
|
|||
int input_queue_size();
|
||||
|
||||
private:
|
||||
void run_look_ahead_planning();
|
||||
float compute_max_junction_velocity(
|
||||
const Vec3F& dir_in_normalized,
|
||||
const Vec3F& dir_out_normalized,
|
||||
float acceleration,
|
||||
float junction_deviation);
|
||||
|
||||
void print_cartesian_path_segments();
|
||||
|
||||
private:
|
||||
RingBuffer<CartesianPathSegment, CT_QUEUE_SIZE> ct_path_segment_queue;
|
||||
|
|
@ -53,5 +63,6 @@ class PathPlanner {
|
|||
IKinemtaicModel* kinematic_model;
|
||||
JointSpacePathSegmentGenerator* segment_generator = nullptr;
|
||||
float segment_time_step;
|
||||
LinearAngular junction_deviation;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -21,17 +21,15 @@ MotionProfileConstAcc::MotionProfileConstAcc(
|
|||
float max_velocity,
|
||||
float max_acceleration)
|
||||
{
|
||||
MotionProfileConstAcc::acceleration = max_acceleration;
|
||||
MotionProfileConstAcc::v_start = v_start;
|
||||
MotionProfileConstAcc::v_end = v_end;
|
||||
|
||||
if (distance <= 1e-7f) {
|
||||
MotionProfileConstAcc::t1 = 0.0f;
|
||||
MotionProfileConstAcc::t2 = 0.0f;
|
||||
MotionProfileConstAcc::t3 = 0.0f;
|
||||
MotionProfileConstAcc::d1 = 0.0f;
|
||||
MotionProfileConstAcc::d2 = 1.0f;
|
||||
MotionProfileConstAcc::v_start = 0.0;
|
||||
MotionProfileConstAcc::v_peak = 0.0f;
|
||||
MotionProfileConstAcc::v_end = 0.0;
|
||||
MotionProfileConstAcc::acceleration = 0.0f;
|
||||
} else {
|
||||
const float inv_max_acceleration = 1.0f / max_acceleration;
|
||||
|
|
@ -43,40 +41,44 @@ MotionProfileConstAcc::MotionProfileConstAcc(
|
|||
// Distances covered during accel/decel
|
||||
float d_accel = 0.5f * (v_start + max_velocity) * t_accel;
|
||||
float d_decel = 0.5f * (max_velocity + v_end) * t_decel;
|
||||
|
||||
float d_cruise = distance - (d_accel + d_decel);
|
||||
float v_peak = 0.0;
|
||||
|
||||
if (d_cruise >= 0.0f) {
|
||||
// Trapezoidal velocity profile
|
||||
MotionProfileConstAcc::t1 = t_accel;
|
||||
MotionProfileConstAcc::t2 = t1 + d_cruise / max_velocity;
|
||||
MotionProfileConstAcc::t3 = t2 + t_decel;
|
||||
MotionProfileConstAcc::v_peak = max_velocity;
|
||||
v_peak = max_velocity;
|
||||
} else {
|
||||
// Triangular velocity profile: recompute peak velocity v_peak
|
||||
float v_peak_sq = max_acceleration * distance + 0.5f * (v_start * v_start + v_end * v_end);
|
||||
float v_peak = std::sqrt(std::max(0.0f, v_peak_sq));
|
||||
v_peak = std::sqrt(std::max(0.0f, v_peak_sq));
|
||||
|
||||
MotionProfileConstAcc::t1 = (v_peak - v_start) * inv_max_acceleration;
|
||||
MotionProfileConstAcc::t2 = t1 + 0.0f;
|
||||
MotionProfileConstAcc::t3 = t2 + (v_peak - v_end) * inv_max_acceleration;
|
||||
MotionProfileConstAcc::v_peak = v_peak;
|
||||
}
|
||||
|
||||
// normalize velocity and acceleration to interpolator range (0..1)
|
||||
const float inv_distance = 1.0f/distance;
|
||||
max_acceleration *= inv_distance;
|
||||
v_start *= inv_distance;
|
||||
v_end *= inv_distance;
|
||||
v_peak *= inv_distance;
|
||||
|
||||
// assign values
|
||||
MotionProfileConstAcc::d1 = 0.5f * (v_start + v_peak) * t1;
|
||||
MotionProfileConstAcc::d2 = d1 + v_peak * (t2-t1);
|
||||
MotionProfileConstAcc::v_start = v_start;
|
||||
MotionProfileConstAcc::v_end = v_end;
|
||||
MotionProfileConstAcc::v_peak = v_peak;
|
||||
MotionProfileConstAcc::acceleration = max_acceleration;
|
||||
}
|
||||
|
||||
// normalize velocity and acceleration to interpolator range (0..1)
|
||||
const float inv_distance = 1.0f/distance;
|
||||
v_start *= inv_distance;
|
||||
v_end *= inv_distance;
|
||||
v_peak *= inv_distance;
|
||||
acceleration *= inv_distance;
|
||||
|
||||
// precompute some values for faster evaluation
|
||||
MotionProfileConstAcc::d1 = 0.5f * (v_start + v_peak) * t1;
|
||||
MotionProfileConstAcc::d2 = d1 + v_peak * (t2-t1);
|
||||
|
||||
//LOG_INFO("d1=%f, d2=%f, d3=%f", d1, d2, distance);
|
||||
//LOG_INFO("t1=%f, t2=%f, t3=%f", t1, t2, t3);
|
||||
// LOG_INFO("d1=%f, d2=%f, d3=%f", MotionProfileConstAcc::d1, MotionProfileConstAcc::d2, distance);
|
||||
// LOG_INFO("t1=%f, t2=%f, t3=%f", MotionProfileConstAcc::t1, MotionProfileConstAcc::t2, MotionProfileConstAcc::t3);
|
||||
// LOG_INFO("vs=%f, vp=%f, ve=%f", MotionProfileConstAcc::v_start, MotionProfileConstAcc::v_peak, MotionProfileConstAcc::v_end);
|
||||
}
|
||||
|
||||
float MotionProfileConstAcc::evaluate(float time) const {
|
||||
|
|
@ -119,8 +121,19 @@ CartesianPathSegment::CartesianPathSegment(const Pose6DF& start_pose,
|
|||
CartesianPathSegment::end_velocity = LinearAngular(0.0f, 0.0f);
|
||||
CartesianPathSegment::max_acceleration = max_acceleration;
|
||||
|
||||
travel_distance.linear = (end_pose.translation - start_pose.translation).length();
|
||||
Vec3F translation_delta = end_pose.translation - start_pose.translation;
|
||||
travel_distance.linear = (translation_delta).length();
|
||||
travel_distance.angular = (start_pose.rotation.normalized_inverse() * end_pose.rotation).angle();
|
||||
|
||||
translation_delta_normalized = translation_delta.normalized();
|
||||
|
||||
|
||||
/*
|
||||
QuaternionF rotation_delta = (end_pose.rotation * start_pose.rotation.normalized_inverse());
|
||||
Vec3F axis;
|
||||
float angle;
|
||||
rotation_delta.to_axis_angle(axis, angle);
|
||||
rotation_delta_axis = axis; */
|
||||
}
|
||||
|
||||
CartesianPathSegment::CartesianPathSegment(const Pose6DF& pose, float dwell_time)
|
||||
|
|
@ -144,9 +157,9 @@ void CartesianPathSegment::compute_motion_profile() {
|
|||
if(dwell_time > 0.0f) {
|
||||
motion_profile = MotionProfileConstAcc(dwell_time);
|
||||
} else {
|
||||
MotionProfileConstAcc linear_profile(travel_distance.linear,start_velocity.linear,
|
||||
end_velocity.linear, target_velocity.linear,
|
||||
max_acceleration.linear);
|
||||
MotionProfileConstAcc linear_profile(travel_distance.linear, start_velocity.linear,
|
||||
end_velocity.linear, target_velocity.linear,
|
||||
max_acceleration.linear);
|
||||
|
||||
MotionProfileConstAcc angular_profile(travel_distance.angular, start_velocity.angular,
|
||||
end_velocity.angular, target_velocity.angular,
|
||||
|
|
@ -164,6 +177,7 @@ void CartesianPathSegment::compute_motion_profile() {
|
|||
void CartesianPathSegment::evaluate(float time, Pose6DF& pose) const {
|
||||
// evaluate motion profile
|
||||
float t = motion_profile.evaluate(time);
|
||||
// LOG_INFO(">t_x [mm]: %f", t);
|
||||
|
||||
// interpolate pose
|
||||
pose = Pose6DF::lerp(start_pose, end_pose, t);
|
||||
|
|
@ -265,6 +279,7 @@ bool JointSpacePathSegmentGenerator::generate_next(JointSpacePathSegment& js_pat
|
|||
// evaluate path to get new end position
|
||||
Pose6DF seg_end_pose;
|
||||
path_segment->evaluate(current_time, seg_end_pose);
|
||||
// LOG_INFO(">pos_x [mm]: %f", seg_end_pose.translation.x);
|
||||
|
||||
// evaluate inverse kinematic model here
|
||||
float next_joint_pos[NUM_JOINTS];
|
||||
|
|
@ -279,4 +294,8 @@ bool JointSpacePathSegmentGenerator::generate_next(JointSpacePathSegment& js_pat
|
|||
current_joint_pos[i] = next_joint_pos[i];
|
||||
|
||||
return end_reached;
|
||||
}
|
||||
}
|
||||
|
||||
const CartesianPathSegment* JointSpacePathSegmentGenerator::get_path_segment() const {
|
||||
return path_segment;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ class CartesianPathSegment {
|
|||
|
||||
void evaluate(float time, Pose6DF& pose) const;
|
||||
float get_duration() const;
|
||||
|
||||
void compute_motion_profile();
|
||||
|
||||
public:
|
||||
|
|
@ -76,6 +75,10 @@ class CartesianPathSegment {
|
|||
LinearAngular end_velocity;
|
||||
LinearAngular max_acceleration;
|
||||
|
||||
LinearAngular max_velocity_delta;
|
||||
Vec3F translation_delta_normalized;
|
||||
//Vec3F rotation_delta_axis;
|
||||
|
||||
LinearAngular travel_distance;
|
||||
MotionProfileConstAcc motion_profile;
|
||||
|
||||
|
|
@ -121,8 +124,9 @@ class JointSpacePathSegmentGenerator {
|
|||
float time_step
|
||||
);
|
||||
|
||||
void reset();
|
||||
bool generate_next(JointSpacePathSegment& js_path_segment);
|
||||
void reset();
|
||||
bool generate_next(JointSpacePathSegment& js_path_segment);
|
||||
const CartesianPathSegment* get_path_segment() const;
|
||||
|
||||
private:
|
||||
float delta_time; // time step size
|
||||
|
|
|
|||
|
|
@ -88,8 +88,9 @@ Robot::Robot(float path_segment_time_step) :
|
|||
|
||||
command_parser.set_command_processor(this);
|
||||
|
||||
current_feedrate = LinearAngular(10.0f, 1.0f);
|
||||
max_acceleration = LinearAngular(500.0f, 50.0f);
|
||||
path_buffering_time_us = 100*1000;
|
||||
path_buffering_time_us = 50*1e3;
|
||||
|
||||
state = ERobotState::IDLE;
|
||||
}
|
||||
|
|
@ -183,6 +184,13 @@ void Robot::update_command_parser() {
|
|||
|
||||
// update command parse which will queue command to the path planner
|
||||
command_parser.update();
|
||||
|
||||
// TESTING:
|
||||
//sleep_ms(10);
|
||||
//float pos_error = joints[1]->servo_controller->get_position_error();
|
||||
//LOG_INFO(">pos_error [µrad]: %f\n", pos_error*1e6);
|
||||
//Serial.printf(">pos_error [µrad]: %f\n", pos_error*1e6);#
|
||||
//LOG_INFO(">pos_x [mm]: %f", joints[1]->position);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,15 +202,14 @@ void Robot::update_path_planner() {
|
|||
// check if buffering starts
|
||||
uint64_t time = time_us_64();
|
||||
if(state == ERobotState::IDLE && path_planner.input_queue_size() > 0) {
|
||||
state = ERobotState::BUFFERING_PAH;
|
||||
state = ERobotState::BUFFERING_PATH;
|
||||
path_buffering_start_time = time;
|
||||
}
|
||||
|
||||
// check if execution starts
|
||||
uint64_t buffering_time = time-path_buffering_start_time;
|
||||
if(state == ERobotState::BUFFERING_PAH && buffering_time > path_buffering_time_us) {
|
||||
if(state == ERobotState::BUFFERING_PATH && buffering_time > path_buffering_time_us) {
|
||||
state = ERobotState::EXECUTING_PATH;
|
||||
path_buffering_start_time = time_us_64();
|
||||
}
|
||||
|
||||
// execute path
|
||||
|
|
@ -287,6 +294,7 @@ void Robot::send_reply(const char* str) {
|
|||
|
||||
void Robot::process_command(const GCodeCommand& cmd, std::string& reply) {
|
||||
if(cmd.get_command() == "G0") process_motion_command(cmd, reply);
|
||||
else if(cmd.get_command() == "G1") process_motion_command(cmd, reply);
|
||||
else if(cmd.get_command() == "G4") process_dwell_command(cmd, reply);
|
||||
else if(startswith(cmd.get_command(), "M")) process_machine_command(cmd, reply);
|
||||
else reply="error: unknown command\n";
|
||||
|
|
@ -295,9 +303,17 @@ void Robot::process_command(const GCodeCommand& cmd, std::string& reply) {
|
|||
void Robot::process_motion_command(const GCodeCommand& cmd, std::string& reply) {
|
||||
Pose6DF end_pose;
|
||||
|
||||
if(path_planner.input_queue_full()) {
|
||||
reply = "error: input queue full\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// read feed rate
|
||||
float feed_linear = cmd.get_value('F', 10.0f);
|
||||
float feed_angular = cmd.get_value('R', 1.0f);
|
||||
current_feedrate.linear = cmd.get_value('F', current_feedrate.linear);
|
||||
current_feedrate.angular = cmd.get_value('R', current_feedrate.angular);
|
||||
|
||||
if(cmd.has_word('I'))
|
||||
state = ERobotState::EXECUTING_PATH;
|
||||
|
||||
// read translation
|
||||
end_pose.translation.x = cmd.get_value('X', current_pose.translation.x);
|
||||
|
|
@ -314,33 +330,54 @@ void Robot::process_motion_command(const GCodeCommand& cmd, std::string& reply)
|
|||
|
||||
// create path segment
|
||||
CartesianPathSegment path_segment(current_pose, end_pose,
|
||||
LinearAngular(feed_linear, feed_angular),
|
||||
current_feedrate,
|
||||
max_acceleration);
|
||||
|
||||
path_planner.add_cartesian_path_segment(path_segment);
|
||||
current_pose = end_pose;
|
||||
|
||||
reply = "ok\n";
|
||||
bool ok = path_planner.add_cartesian_path_segment(path_segment);
|
||||
if(ok) {
|
||||
path_planner.run_look_ahead_planning();
|
||||
current_pose = end_pose;
|
||||
reply = "ok\n";
|
||||
} else {
|
||||
reply = "error\n";
|
||||
}
|
||||
}
|
||||
|
||||
void Robot::process_machine_command(const GCodeCommand& cmd, std::string& reply) {
|
||||
reply = "";
|
||||
|
||||
if(cmd.get_command() == "M50") {
|
||||
reply = "Current Position: ";
|
||||
reply += "Current Position: ";
|
||||
reply += std::string(" X") + std::to_string(current_pose.translation.x);
|
||||
reply += std::string(" Y") + std::to_string(current_pose.translation.y);
|
||||
reply += std::string(" Z") + std::to_string(current_pose.translation.z);
|
||||
reply += "\n";
|
||||
reply = "ok\n";
|
||||
}
|
||||
if(cmd.get_command() == "M51") {
|
||||
uint32_t servo_loop_freq = servo_loop_frequency_counter.get();
|
||||
uint32_t mcontroler_freq = motion_controller_frequency_counter.get();
|
||||
reply += std::string("Servo Loop: ") + std::to_string(servo_loop_freq/1000) + "kHz\n";
|
||||
reply += std::string("Motion Controler: ") + std::to_string(mcontroler_freq/1000) + "kHz\n";
|
||||
reply += "ok\n";
|
||||
}
|
||||
if(cmd.get_command() == "M52") {
|
||||
int s = path_planner.input_queue_size();
|
||||
reply += std::string("Queue Size: ") + std::to_string(s) + "\n";
|
||||
reply += "ok\n";
|
||||
}
|
||||
if(cmd.get_command() == "M53") {
|
||||
bool f = path_planner.all_finished();
|
||||
reply += f ? "1\n" : "0\n";
|
||||
reply += "ok\n";
|
||||
}
|
||||
if(cmd.get_command() == "M55") {
|
||||
process_set_servo_parameter_command(cmd, reply);
|
||||
}
|
||||
if(cmd.get_command() == "M204") {
|
||||
|
||||
if(cmd.has_word('L')) max_acceleration.linear = cmd.get_value('L');
|
||||
if(cmd.has_word('A')) max_acceleration.angular = cmd.get_value('A');
|
||||
reply += "ok\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -353,8 +390,26 @@ void Robot::process_dwell_command(const GCodeCommand& cmd, std::string& reply) {
|
|||
// create path segment
|
||||
CartesianPathSegment path_segment(current_pose, dwell_time);
|
||||
path_planner.add_cartesian_path_segment(path_segment);
|
||||
path_planner.run_look_ahead_planning();
|
||||
|
||||
reply = "ok\n";
|
||||
}
|
||||
|
||||
void Robot::process_set_servo_parameter_command(const GCodeCommand& cmd, std::string& reply) {
|
||||
// example: M55 A150 B50000 C0.2 D100 E F0.0025
|
||||
bool has_all = cmd.has_word('A') && cmd.has_word('B') && cmd.has_word('C') &&
|
||||
cmd.has_word('D') && cmd.has_word('F');
|
||||
|
||||
if(has_all == false)
|
||||
reply = "error: not all parameters given (A,B,C,D,F expected)\n";
|
||||
|
||||
for(int i=0; i<NUM_JOINTS; i++) {
|
||||
|
||||
joints[i]->servo_controller->velocity_lowpass.set_time_constant(cmd.get_value('F'));
|
||||
joints[i]->servo_controller->pos_controller.set_parameter(cmd.get_value('A'), cmd.get_value('B'), 0.0f, Constants::PI_F*2.0F, Constants::PI_F*0.5F);
|
||||
joints[i]->servo_controller->velocity_controller.set_parameter(cmd.get_value('C'), cmd.get_value('D'), 0.0f, Constants::PI_F*0.45f, Constants::PI_F*0.45f);
|
||||
}
|
||||
|
||||
reply = "ok\n";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class Robot;
|
|||
|
||||
enum class ERobotState {
|
||||
IDLE = 0,
|
||||
BUFFERING_PAH = 1,
|
||||
BUFFERING_PATH = 1,
|
||||
EXECUTING_PATH = 2,
|
||||
ERROR = 3
|
||||
};
|
||||
|
|
@ -84,6 +84,8 @@ class Robot : public ICommandProcessor {
|
|||
void process_motion_command(const GCodeCommand& cmd, std::string& reply);
|
||||
void process_dwell_command(const GCodeCommand& cmd, std::string& reply);
|
||||
void process_machine_command(const GCodeCommand& cmd, std::string& reply);
|
||||
void process_set_pose_command(const GCodeCommand& cmd, std::string& reply);
|
||||
void process_set_servo_parameter_command(const GCodeCommand& cmd, std::string& reply);
|
||||
|
||||
protected:
|
||||
static bool update_motion_controller_isr(repeating_timer_t* timer); // called from update timer
|
||||
|
|
@ -102,6 +104,7 @@ class Robot : public ICommandProcessor {
|
|||
|
||||
LinearAngular max_acceleration;
|
||||
Pose6DF current_pose;
|
||||
LinearAngular current_feedrate;
|
||||
|
||||
SharedData shared_data;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ void ServoController::init(float max_motor_amplitude) {
|
|||
sleep_ms(1);
|
||||
}
|
||||
|
||||
velocity_lowpass.set_time_constant(0.0025f);
|
||||
pos_controller.set_parameter(150.0f, 50000.0f, 0.0f, Constants::PI_F*2.0F, Constants::PI_F*0.5F);
|
||||
velocity_controller.set_parameter(0.2f, 100.0f, 0.0f, Constants::PI_F*0.45f, Constants::PI_F*0.45f);
|
||||
velocity_lowpass.set_time_constant(0.004f);
|
||||
pos_controller.set_parameter(75.0f, 50000.0f, 0.0f, Constants::PI_F*2.0F, Constants::PI_F*0.5F);
|
||||
velocity_controller.set_parameter(0.2f, 150.0f, 0.0f, Constants::PI_F*0.45f, Constants::PI_F*0.45f);
|
||||
}
|
||||
|
||||
void ServoController::set_encoder_lut(LookupTable& enc_to_pos_lut) {
|
||||
|
|
|
|||
|
|
@ -50,15 +50,16 @@ class ServoController {
|
|||
float motor_pos_to_field_angle(float motor_pos);
|
||||
float motor_velocity_to_field_velocity(float v);
|
||||
|
||||
public:
|
||||
LowpassFilter velocity_lowpass;
|
||||
PIDController pos_controller;
|
||||
PIDController velocity_controller;
|
||||
|
||||
private:
|
||||
ENCODER_TYPE& encoder;
|
||||
MOTOR_DRIVER_TYPE& motor_driver;
|
||||
LookupTable enc_to_pos_lut;
|
||||
|
||||
LowpassFilter velocity_lowpass;
|
||||
PIDController pos_controller;
|
||||
PIDController velocity_controller;
|
||||
|
||||
float motor_pos = 0; // current motor position
|
||||
float motor_pos_prev = 0; // previous motor position
|
||||
float pos_error = 0; // current position error as computed by upate()
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ class RingBuffer {
|
|||
return &(buffer[tail]);
|
||||
}
|
||||
|
||||
T* get(int i) {
|
||||
if (count == 0) return nullptr;
|
||||
return &(buffer[(tail + i) % N]);
|
||||
}
|
||||
|
||||
bool empty() const { return count == 0; }
|
||||
bool full() const { return count == N; }
|
||||
int size() const { return count; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue