File syn_interpolator.c¶
File List > motor > syn_interpolator.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_INTERPOLATOR) || SYN_USE_INTERPOLATOR
#include "../util/syn_assert.h"
#include "syn_interpolator.h"
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void syn_interpolator_init(SYN_Interpolator *interp)
{
SYN_ASSERT(interp != NULL);
memset(interp, 0, sizeof(SYN_Interpolator));
interp->mode = SYN_INTERP_MODE_IDLE;
}
SYN_Status syn_interpolator_plan_linear(SYN_Interpolator *interp, SYN_Vector3F start,
SYN_Vector3F target, float feedrate, float max_accel,
float max_jerk, float step_res)
{
SYN_ASSERT(interp != NULL);
if (feedrate <= 0.0f || step_res <= 0.0f)
return SYN_INVALID_PARAM;
memset(interp, 0, sizeof(SYN_Interpolator));
interp->mode = SYN_INTERP_MODE_LINEAR;
interp->start_pos = start;
interp->target_pos = target;
interp->current_pos = start;
float dx = target.x - start.x;
float dy = target.y - start.y;
float dz = target.z - start.z;
interp->total_length = sqrtf(dx * dx + dy * dy + dz * dz);
if (interp->total_length < 1e-6f) {
interp->mode = SYN_INTERP_MODE_IDLE;
return SYN_OK;
}
interp->total_steps = (uint32_t)(interp->total_length / step_res);
if (interp->total_steps == 0)
interp->total_steps = 1;
interp->target_feedrate = feedrate;
/* Initialize S-Curve generator for position scaling */
syn_scurve_init(&interp->scurve, 0);
syn_scurve_set_constraints(&interp->scurve, (int32_t)(feedrate / step_res),
(int32_t)(max_accel / step_res), (int32_t)(max_jerk / step_res));
syn_scurve_set_target(&interp->scurve, (int32_t)interp->total_steps);
return SYN_OK;
}
SYN_Status syn_interpolator_plan_circular(SYN_Interpolator *interp, SYN_Vector3F start,
SYN_Vector3F target, SYN_Vector3F center_offset,
bool is_cw, float feedrate, float max_accel,
float max_jerk, float step_res)
{
SYN_ASSERT(interp != NULL);
if (feedrate <= 0.0f || step_res <= 0.0f)
return SYN_INVALID_PARAM;
memset(interp, 0, sizeof(SYN_Interpolator));
interp->mode = is_cw ? SYN_INTERP_MODE_CIRCULAR_CW : SYN_INTERP_MODE_CIRCULAR_CCW;
interp->start_pos = start;
interp->target_pos = target;
interp->center_offset = center_offset;
interp->current_pos = start;
/* Center X, Y relative to start */
float cx = start.x + center_offset.x;
float cy = start.y + center_offset.y;
float rx = -center_offset.x;
float ry = -center_offset.y;
interp->radius = sqrtf(rx * rx + ry * ry);
if (interp->radius < 1e-6f)
return SYN_INVALID_PARAM;
interp->start_angle = atan2f(-center_offset.y, -center_offset.x);
float end_angle = atan2f(target.y - cy, target.x - cx);
float sweep = end_angle - interp->start_angle;
if (is_cw) {
if (sweep >= 0.0f)
sweep -= (float)(2.0 * M_PI);
} else {
if (sweep <= 0.0f)
sweep += (float)(2.0 * M_PI);
}
interp->sweep_angle = sweep;
interp->total_length = fabsf(sweep) * interp->radius;
interp->total_steps = (uint32_t)(interp->total_length / step_res);
if (interp->total_steps == 0)
interp->total_steps = 1;
interp->target_feedrate = feedrate;
/* Initialize S-Curve generator */
syn_scurve_init(&interp->scurve, 0);
syn_scurve_set_constraints(&interp->scurve, (int32_t)(feedrate / step_res),
(int32_t)(max_accel / step_res), (int32_t)(max_jerk / step_res));
syn_scurve_set_target(&interp->scurve, (int32_t)interp->total_steps);
return SYN_OK;
}
bool syn_interpolator_step(SYN_Interpolator *interp, SYN_Vector3F *out_pos)
{
SYN_ASSERT(interp != NULL);
SYN_ASSERT(out_pos != NULL);
if (interp->mode == SYN_INTERP_MODE_IDLE) {
*out_pos = interp->current_pos;
return false;
}
if (interp->step_index >= interp->total_steps) {
interp->current_pos = interp->target_pos;
*out_pos = interp->current_pos;
interp->mode = SYN_INTERP_MODE_IDLE;
return false;
}
interp->step_index++;
float ratio = (float)interp->step_index / (float)interp->total_steps;
float s = ratio * interp->total_length;
interp->current_dist = s;
if (interp->mode == SYN_INTERP_MODE_LINEAR) {
interp->current_pos.x =
interp->start_pos.x + ratio * (interp->target_pos.x - interp->start_pos.x);
interp->current_pos.y =
interp->start_pos.y + ratio * (interp->target_pos.y - interp->start_pos.y);
interp->current_pos.z =
interp->start_pos.z + ratio * (interp->target_pos.z - interp->start_pos.z);
} else {
float cx = interp->start_pos.x + interp->center_offset.x;
float cy = interp->start_pos.y + interp->center_offset.y;
float angle = interp->start_angle + ratio * interp->sweep_angle;
interp->current_pos.x = cx + interp->radius * cosf(angle);
interp->current_pos.y = cy + interp->radius * sinf(angle);
interp->current_pos.z =
interp->start_pos.z + ratio * (interp->target_pos.z - interp->start_pos.z);
}
*out_pos = interp->current_pos;
if (interp->step_index >= interp->total_steps) {
interp->mode = SYN_INTERP_MODE_IDLE;
}
return true;
}
bool syn_interpolator_eval_at_time(SYN_Interpolator *interp, float t_sec, SYN_Vector3F *out_pos,
SYN_Vector3F *out_vel)
{
SYN_ASSERT(interp != NULL);
SYN_ASSERT(out_pos != NULL);
if (interp->mode == SYN_INTERP_MODE_IDLE) {
*out_pos = interp->current_pos;
if (out_vel)
memset(out_vel, 0, sizeof(SYN_Vector3F));
return false;
}
/* Advance S-curve generator update */
int32_t scurve_pos = syn_scurve_update(&interp->scurve);
int32_t scurve_vel = syn_scurve_velocity(&interp->scurve);
float ratio =
(interp->total_steps > 0) ? ((float)scurve_pos / (float)interp->total_steps) : 1.0f;
if (ratio > 1.0f)
ratio = 1.0f;
if (ratio < 0.0f)
ratio = 0.0f;
if (interp->mode == SYN_INTERP_MODE_LINEAR) {
out_pos->x = interp->start_pos.x + ratio * (interp->target_pos.x - interp->start_pos.x);
out_pos->y = interp->start_pos.y + ratio * (interp->target_pos.y - interp->start_pos.y);
out_pos->z = interp->start_pos.z + ratio * (interp->target_pos.z - interp->start_pos.z);
if (out_vel && interp->total_length > 0.0f) {
float vel_mag = (float)scurve_vel;
out_vel->x =
vel_mag * (interp->target_pos.x - interp->start_pos.x) / interp->total_length;
out_vel->y =
vel_mag * (interp->target_pos.y - interp->start_pos.y) / interp->total_length;
out_vel->z =
vel_mag * (interp->target_pos.z - interp->start_pos.z) / interp->total_length;
}
} else {
float cx = interp->start_pos.x + interp->center_offset.x;
float cy = interp->start_pos.y + interp->center_offset.y;
float angle = interp->start_angle + ratio * interp->sweep_angle;
out_pos->x = cx + interp->radius * cosf(angle);
out_pos->y = cy + interp->radius * sinf(angle);
out_pos->z = interp->start_pos.z + ratio * (interp->target_pos.z - interp->start_pos.z);
if (out_vel) {
float vel_mag = (float)scurve_vel;
float tangent_angle =
angle + (interp->sweep_angle >= 0.0f ? (float)(M_PI / 2.0) : -(float)(M_PI / 2.0));
out_vel->x = vel_mag * cosf(tangent_angle);
out_vel->y = vel_mag * sinf(tangent_angle);
out_vel->z = 0.0f;
}
}
(void)t_sec;
return !syn_scurve_done(&interp->scurve);
}
SYN_Status syn_interpolator_plan_bezier(SYN_Interpolator *interp, SYN_Vector3F p0, SYN_Vector3F p1,
SYN_Vector3F p2, SYN_Vector3F p3, float feedrate,
float max_accel, float max_jerk, float step_res)
{
if (!interp || feedrate <= 0.0f || max_accel <= 0.0f || max_jerk <= 0.0f || step_res <= 0.0f)
return SYN_INVALID_PARAM;
/* Approximate curve chord length using 10 segments */
float approx_len = 0.0f;
SYN_Vector3F prev = p0;
for (int i = 1; i <= 10; i++) {
float u = (float)i / 10.0f;
float u_inv = 1.0f - u;
float b0 = u_inv * u_inv * u_inv;
float b1 = 3.0f * u_inv * u_inv * u;
float b2 = 3.0f * u_inv * u * u;
float b3 = u * u * u;
SYN_Vector3F pt = {.x = b0 * p0.x + b1 * p1.x + b2 * p2.x + b3 * p3.x,
.y = b0 * p0.y + b1 * p1.y + b2 * p2.y + b3 * p3.y,
.z = b0 * p0.z + b1 * p1.z + b2 * p2.z + b3 * p3.z};
float dx = pt.x - prev.x;
float dy = pt.y - prev.y;
float dz = pt.z - prev.z;
approx_len += sqrtf(dx * dx + dy * dy + dz * dz);
prev = pt;
}
(void)approx_len;
return syn_interpolator_plan_linear(interp, p0, p3, feedrate, max_accel, max_jerk, step_res);
}
#endif /* SYN_USE_INTERPOLATOR */