Skip to content

File syn_motor_ctrl.h

FileList > motor > syn_motor_ctrl.h

Go to the source code of this file

Closed-loop motor controller — generic feedback + PID + motor. More...

  • #include "../common/syn_defs.h"
  • #include "../control/syn_pid.h"
  • #include "../log/syn_datalog.h"
  • #include "../motor/syn_motor_output.h"
  • #include "../port/syn_port_system.h"
  • #include "../system/syn_errlog.h"
  • #include "../util/syn_ramp.h"
  • #include "../util/syn_scurve.h"
  • #include <stdbool.h>
  • #include <stdint.h>

Classes

Type Name
struct SYN_MotorCtrl
Motor controller instance (opaque — use API to access).
struct SYN_MotorCtrl_Config
Motor controller configuration (passed to init, copied internally).
struct SYN_MotorCtrl_Metrics
Accumulated metrics for a single move/trajectory.
struct SYN_MotorCtrl_Sample
One sample of control-loop telemetry.
struct SYN_MotorCtrl_Trajectory
Trajectory setpoint for feedforward control.

Public Types

Type Name
enum SYN_MotorCtrl_Mode
Control loop operating mode.
typedef int32_t(* SYN_MotorCtrl_ReadPos
Position feedback function.
typedef void(* SYN_MotorCtrl_StallCallback
Callback invoked when a motor stall is detected.
enum SYN_MotorCtrl_State
Motor controller runtime state.
typedef void(* SYN_MotorCtrl_TargetCallback
Callback invoked when position reaches the target deadband.

Public Functions

Type Name
void syn_motor_ctrl_clear_stall (SYN_MotorCtrl * ctrl)
Clear a stall condition and return to idle.
void syn_motor_ctrl_estop (SYN_MotorCtrl * ctrl)
Emergency stop — immediate brake/coast.
SYN_Status syn_motor_ctrl_init (SYN_MotorCtrl * ctrl, const SYN_MotorCtrl_Config * cfg)
Initialize the closed-loop controller.
void syn_motor_ctrl_move_to (SYN_MotorCtrl * ctrl, int32_t target, int32_t max_velocity, int32_t acceleration)
Move to position with a built-in trapezoidal velocity profile.
void syn_motor_ctrl_move_to_scurve (SYN_MotorCtrl * ctrl, int32_t target, int32_t max_velocity, int32_t max_accel, int32_t max_jerk)
Move to position with a built-in jerk-limited S-curve profile.
void syn_motor_ctrl_on_stall (SYN_MotorCtrl * ctrl, SYN_MotorCtrl_StallCallback cb, void * ctx)
Register stall callback.
void syn_motor_ctrl_on_target (SYN_MotorCtrl * ctrl, SYN_MotorCtrl_TargetCallback cb, void * ctx)
Register on-target callback (position mode).
void syn_motor_ctrl_reset_metrics (SYN_MotorCtrl * ctrl)
Reset move metrics. Call before starting a move you want to measure.
int32_t syn_motor_ctrl_rms_error (const SYN_MotorCtrl * ctrl)
Compute RMS tracking error from accumulated metrics.
void syn_motor_ctrl_set_datalog (SYN_MotorCtrl * ctrl, SYN_DataLog * log)
Attach a datalog for tuning capture.
void syn_motor_ctrl_set_ff_gains (SYN_MotorCtrl * ctrl, int32_t ff_kv, int32_t ff_ka)
Update feedforward gains at runtime.
void syn_motor_ctrl_set_gains (SYN_MotorCtrl * ctrl, int32_t kp, int32_t ki, int32_t kd)
Update PID gains at runtime.
void syn_motor_ctrl_set_output (SYN_MotorCtrl * ctrl, int32_t output)
Drive the motor at a fixed output level (open-loop).
void syn_motor_ctrl_set_position (SYN_MotorCtrl * ctrl, int32_t target)
Set position target (absolute feedback units).
void syn_motor_ctrl_set_trajectory (SYN_MotorCtrl * ctrl, const SYN_MotorCtrl_Trajectory * traj)
Feed a trajectory point for feedforward + PID tracking.
void syn_motor_ctrl_set_velocity (SYN_MotorCtrl * ctrl, int32_t units_per_sec)
Set velocity target (units/second).
void syn_motor_ctrl_stop (SYN_MotorCtrl * ctrl)
Stop the motor and enter idle mode.
SYN_MotorCtrl_State syn_motor_ctrl_update (SYN_MotorCtrl * ctrl)
Run the control loop. Call at cfg.update_hz rate.

Public Static Functions

Type Name
int32_t syn_motor_ctrl_error (const SYN_MotorCtrl * ctrl)
Get last position error.
int32_t syn_motor_ctrl_ff_output (const SYN_MotorCtrl * ctrl)
Get last feedforward output component.
const SYN_MotorCtrl_Metrics * syn_motor_ctrl_get_metrics (const SYN_MotorCtrl * ctrl)
Get accumulated move metrics.
SYN_MotorCtrl_Mode syn_motor_ctrl_mode (const SYN_MotorCtrl * ctrl)
Get the current operating mode.
uint32_t syn_motor_ctrl_move_duration (const SYN_MotorCtrl * ctrl)
Get move duration in milliseconds.
int32_t syn_motor_ctrl_output (const SYN_MotorCtrl * ctrl)
Get total output (PID + feedforward).
int32_t syn_motor_ctrl_pid_output (const SYN_MotorCtrl * ctrl)
Get last PID output component.
int32_t syn_motor_ctrl_position (const SYN_MotorCtrl * ctrl)
Get measured position.
uint32_t syn_motor_ctrl_settle_time (const SYN_MotorCtrl * ctrl)
Get settling time in milliseconds (0 if never settled).
SYN_MotorCtrl_State syn_motor_ctrl_state (const SYN_MotorCtrl * ctrl)
Get the current controller state.
int32_t syn_motor_ctrl_velocity (const SYN_MotorCtrl * ctrl)
Get measured velocity.

Macros

Type Name
define SYN_MCTRL_DATALOG_ID 0x4D43 /\* 'MC' \*/
define SYN_MOTOR_CTRL_DEFAULTS (motor_out, read_fn, read_ctx, hz, out_max) /* multi line expression */
Convenience macro for a motor controller config with sane defaults.

Detailed Description

Combines any position/velocity feedback source with PID control to drive DC or stepper motors in closed loop. The feedback source is a function pointer, so it works equally well with: * Rotary encoder (syn_encoder) * Potentiometer / linear pot on ADC (syn_adc) * Hall sensor, absolute encoder, or any other position source

** **

  • Velocity: PID maintains target units/second
  • Position: PID drives to target position value
  • Trajectory: PID + feedforward tracks a trajectory from an external generator (syn_ramp, syn_scurve, or application code)

** **

// Feedback: read encoder position
int32_t encoder_feedback(void *ctx) {
    return syn_encoder_position((SYN_Encoder *)ctx);
}

SYN_MotorCtrl ctrl;
SYN_MotorCtrl_Config cfg = {
    .motor       = syn_dc_motor_output(&my_dc),
    .read_pos    = encoder_feedback,
    .read_pos_ctx = &my_encoder,
    .pid_kp = 200, .pid_ki = 50, .pid_kd = 10, .pid_scale = 8,
    .update_hz     = 1000,
    .output_min  = -1000, .output_max = 1000,
};
syn_motor_ctrl_init(&ctrl, &cfg);
syn_motor_ctrl_set_velocity(&ctrl, 500);  // 500 counts/sec

** **

SYN_MotorCtrl_Config cfg = {
    // ... feedback, motor, PID as above ...
    .ff_kv    = 128,   // velocity feedforward gain
    .ff_ka    = 64,    // acceleration feedforward gain
    .ff_scale = 8,     // feedforward divisor = 1 << 8 = 256
};
syn_motor_ctrl_init(&ctrl, &cfg);

// In your control loop, feed trajectory from any generator:
SYN_MotorCtrl_Trajectory traj = {
    .position     = profile_position,
    .velocity     = profile_velocity,
    .acceleration = profile_acceleration,
};
syn_motor_ctrl_set_trajectory(&ctrl, &traj);
syn_motor_ctrl_update(&ctrl);

Public Types Documentation

enum SYN_MotorCtrl_Mode

Control loop operating mode.

enum SYN_MotorCtrl_Mode {
    SYN_MCTRL_MODE_IDLE = 0,
    SYN_MCTRL_MODE_VELOCITY = 1,
    SYN_MCTRL_MODE_POSITION = 2,
    SYN_MCTRL_MODE_OPEN_LOOP = 3
};


typedef SYN_MotorCtrl_ReadPos

Position feedback function.

typedef int32_t(* SYN_MotorCtrl_ReadPos) (void *ctx);

Returns the current position in whatever units the sensor provides (encoder counts, ADC ticks, millivolts — the PID doesn't care). The same units must be used for all targets and deadbands.

Parameters:

  • ctx User context.

Returns:

Current position in feedback units.


typedef SYN_MotorCtrl_StallCallback

Callback invoked when a motor stall is detected.

typedef void(* SYN_MotorCtrl_StallCallback) (struct SYN_MotorCtrl *ctrl, void *ctx);

Parameters:

  • ctrl Controller that detected the stall.
  • ctx User context.

enum SYN_MotorCtrl_State

Motor controller runtime state.

enum SYN_MotorCtrl_State {
    SYN_MCTRL_STOPPED = 0,
    SYN_MCTRL_RUNNING = 1,
    SYN_MCTRL_ON_TARGET = 2,
    SYN_MCTRL_STALLED = 3,
    SYN_MCTRL_LIMIT = 4
};


typedef SYN_MotorCtrl_TargetCallback

Callback invoked when position reaches the target deadband.

typedef void(* SYN_MotorCtrl_TargetCallback) (struct SYN_MotorCtrl *ctrl, void *ctx);

Parameters:

  • ctrl Controller that reached target.
  • ctx User context.

Public Functions Documentation

function syn_motor_ctrl_clear_stall

Clear a stall condition and return to idle.

void syn_motor_ctrl_clear_stall (
    SYN_MotorCtrl * ctrl
) 

After a stall, the controller locks in STALLED state until this is called.

Parameters:

  • ctrl Controller instance.

function syn_motor_ctrl_estop

Emergency stop — immediate brake/coast.

void syn_motor_ctrl_estop (
    SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller instance.

function syn_motor_ctrl_init

Initialize the closed-loop controller.

SYN_Status syn_motor_ctrl_init (
    SYN_MotorCtrl * ctrl,
    const SYN_MotorCtrl_Config * cfg
) 

Parameters:

  • ctrl Controller instance to initialize.
  • cfg Configuration (copied internally).

Returns:

SYN_OK on success.


function syn_motor_ctrl_move_to

Move to position with a built-in trapezoidal velocity profile.

void syn_motor_ctrl_move_to (
    SYN_MotorCtrl * ctrl,
    int32_t target,
    int32_t max_velocity,
    int32_t acceleration
) 

This is the "batteries included" path — no external profile generator needed. The controller internally generates a trapezoidal ramp (accelerate → cruise → decelerate) and feeds it through the normal trajectory/feedforward path each update() call.

Equivalent to manually calling syn_ramp_set_target_trapezoid() and feeding the output into set_trajectory() each tick, but with zero user-side wiring.

Parameters:

  • ctrl Controller instance.
  • target Target position in feedback units.
  • max_velocity Maximum velocity (units/second).
  • acceleration Acceleration (units/second²).

function syn_motor_ctrl_move_to_scurve

Move to position with a built-in jerk-limited S-curve profile.

void syn_motor_ctrl_move_to_scurve (
    SYN_MotorCtrl * ctrl,
    int32_t target,
    int32_t max_velocity,
    int32_t max_accel,
    int32_t max_jerk
) 

Like move_to(), but uses a 7-phase S-curve trajectory that bounds jerk for the smoothest possible motion. The S-curve natively provides position, velocity, and acceleration — all three are fed into the trajectory/feedforward path.

All kinematic limits are in per-second units: * max_velocity: units/second * max_accel: units/second² * max_jerk: units/second³

Parameters:

  • ctrl Controller instance.
  • target Target position in feedback units.
  • max_velocity Maximum velocity (units/second).
  • max_accel Maximum acceleration (units/second²).
  • max_jerk Maximum jerk (units/second³).

function syn_motor_ctrl_on_stall

Register stall callback.

void syn_motor_ctrl_on_stall (
    SYN_MotorCtrl * ctrl,
    SYN_MotorCtrl_StallCallback cb,
    void * ctx
) 

Parameters:

  • ctrl Controller instance.
  • cb Callback function, or NULL to unregister.
  • ctx User context passed to callback.

function syn_motor_ctrl_on_target

Register on-target callback (position mode).

void syn_motor_ctrl_on_target (
    SYN_MotorCtrl * ctrl,
    SYN_MotorCtrl_TargetCallback cb,
    void * ctx
) 

Parameters:

  • ctrl Controller instance.
  • cb Callback function, or NULL to unregister.
  • ctx User context passed to callback.

function syn_motor_ctrl_reset_metrics

Reset move metrics. Call before starting a move you want to measure.

void syn_motor_ctrl_reset_metrics (
    SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller instance.

function syn_motor_ctrl_rms_error

Compute RMS tracking error from accumulated metrics.

int32_t syn_motor_ctrl_rms_error (
    const SYN_MotorCtrl * ctrl
) 

Returns the integer square root of (error_sq_sum / sample_count). Only valid if sample_count > 0.

Parameters:

  • ctrl Controller instance.

Returns:

RMS tracking error in feedback units.


function syn_motor_ctrl_set_datalog

Attach a datalog for tuning capture.

void syn_motor_ctrl_set_datalog (
    SYN_MotorCtrl * ctrl,
    SYN_DataLog * log
) 

When attached, every update() writes a SYN_MotorCtrl_Sample frame to the datalog at full control-loop rate.

Parameters:

  • ctrl Controller.
  • log Datalog instance (caller-owned), or NULL to detach.

function syn_motor_ctrl_set_ff_gains

Update feedforward gains at runtime.

void syn_motor_ctrl_set_ff_gains (
    SYN_MotorCtrl * ctrl,
    int32_t ff_kv,
    int32_t ff_ka
) 

Parameters:

  • ctrl Controller instance.
  • ff_kv Velocity feedforward gain (÷ 1 << ff_scale).
  • ff_ka Acceleration feedforward gain (÷ 1 << ff_scale).

function syn_motor_ctrl_set_gains

Update PID gains at runtime.

void syn_motor_ctrl_set_gains (
    SYN_MotorCtrl * ctrl,
    int32_t kp,
    int32_t ki,
    int32_t kd
) 

Parameters:

  • ctrl Controller instance.
  • kp Proportional gain (÷ 1 << pid_scale).
  • ki Integral gain (÷ 1 << pid_scale).
  • kd Derivative gain (÷ 1 << pid_scale).

function syn_motor_ctrl_set_output

Drive the motor at a fixed output level (open-loop).

void syn_motor_ctrl_set_output (
    SYN_MotorCtrl * ctrl,
    int32_t output
) 

Bypasses PID — the output value is passed directly to the motor. Useful for manual jogging, testing motor wiring, or when simple open-loop control is sufficient. Position feedback is still read (for soft limits and stall detection) but not used for control.

Parameters:

  • ctrl Controller instance.
  • output Output level (clamped to [output_min, output_max]).

function syn_motor_ctrl_set_position

Set position target (absolute feedback units).

void syn_motor_ctrl_set_position (
    SYN_MotorCtrl * ctrl,
    int32_t target
) 

Switches to position mode. Controller drives toward target and enters ON_TARGET state when within deadband.

Parameters:

  • ctrl Controller instance.
  • target Absolute position in feedback units.

function syn_motor_ctrl_set_trajectory

Feed a trajectory point for feedforward + PID tracking.

void syn_motor_ctrl_set_trajectory (
    SYN_MotorCtrl * ctrl,
    const SYN_MotorCtrl_Trajectory * traj
) 

Call this each update with the current output of your trajectory generator (syn_ramp, syn_scurve, or application code). The controller uses PID on position error plus feedforward from the trajectory's velocity and acceleration.

Parameters:

  • ctrl Controller instance.
  • traj Trajectory point (position, velocity, acceleration).

function syn_motor_ctrl_set_velocity

Set velocity target (units/second).

void syn_motor_ctrl_set_velocity (
    SYN_MotorCtrl * ctrl,
    int32_t units_per_sec
) 

Switches to velocity mode. Positive = forward, negative = reverse.

Parameters:

  • ctrl Controller instance.
  • units_per_sec Target velocity in feedback units per second.

function syn_motor_ctrl_stop

Stop the motor and enter idle mode.

void syn_motor_ctrl_stop (
    SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller instance.

function syn_motor_ctrl_update

Run the control loop. Call at cfg.update_hz rate.

SYN_MotorCtrl_State syn_motor_ctrl_update (
    SYN_MotorCtrl * ctrl
) 

Reads feedback, computes PID, drives motor output. If a datalog is attached, writes a SYN_MotorCtrl_Sample each call.

Parameters:

  • ctrl Controller instance.

Returns:

Current controller state.


Public Static Functions Documentation

function syn_motor_ctrl_error

Get last position error.

static inline int32_t syn_motor_ctrl_error (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Position error in feedback units.


function syn_motor_ctrl_ff_output

Get last feedforward output component.

static inline int32_t syn_motor_ctrl_ff_output (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Feedforward output.


function syn_motor_ctrl_get_metrics

Get accumulated move metrics.

static inline const SYN_MotorCtrl_Metrics * syn_motor_ctrl_get_metrics (
    const SYN_MotorCtrl * ctrl
) 

Read this after a move completes to evaluate performance. Contains max error, RMS error data, overshoot, peak output, and timing — everything needed for tuning, in a single read.

Parameters:

  • ctrl Controller.

Returns:

Pointer to metrics struct.


function syn_motor_ctrl_mode

Get the current operating mode.

static inline SYN_MotorCtrl_Mode syn_motor_ctrl_mode (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Operating mode.


function syn_motor_ctrl_move_duration

Get move duration in milliseconds.

static inline uint32_t syn_motor_ctrl_move_duration (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Duration in ms, or 0 if no move started.


function syn_motor_ctrl_output

Get total output (PID + feedforward).

static inline int32_t syn_motor_ctrl_output (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Combined output.


function syn_motor_ctrl_pid_output

Get last PID output component.

static inline int32_t syn_motor_ctrl_pid_output (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

PID output.


function syn_motor_ctrl_position

Get measured position.

static inline int32_t syn_motor_ctrl_position (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Position in feedback units.


function syn_motor_ctrl_settle_time

Get settling time in milliseconds (0 if never settled).

static inline uint32_t syn_motor_ctrl_settle_time (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Settle time in ms.


function syn_motor_ctrl_state

Get the current controller state.

static inline SYN_MotorCtrl_State syn_motor_ctrl_state (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Runtime state.


function syn_motor_ctrl_velocity

Get measured velocity.

static inline int32_t syn_motor_ctrl_velocity (
    const SYN_MotorCtrl * ctrl
) 

Parameters:

  • ctrl Controller.

Returns:

Velocity in feedback units/second.


Macro Definition Documentation

define SYN_MCTRL_DATALOG_ID

#define SYN_MCTRL_DATALOG_ID `0x4D43 /* 'MC' */`

Datalog stream ID for motor controller telemetry.


define SYN_MOTOR_CTRL_DEFAULTS

Convenience macro for a motor controller config with sane defaults.

#define SYN_MOTOR_CTRL_DEFAULTS (
    motor_out,
    read_fn,
    read_ctx,
    hz,
    out_max
) `/* multi line expression */`

Provides a conservative P-only configuration that is stable on most systems. Override individual fields after initialization to tune.

Parameters:

  • motor_out SYN_MotorOutput (e.g., syn_dc_motor_output(&motor)).
  • read_fn Position read function.
  • read_ctx Context for read_fn (NULL if unused).
  • hz Control loop frequency (e.g., 1000).
  • out_max Maximum output magnitude (symmetric ±out_max).

** **

SYN_MotorCtrl_Config cfg = SYN_MOTOR_CTRL_DEFAULTS(
    syn_dc_motor_output(&motor), encoder_read, NULL, 1000, 1000
);
cfg.pid_ki = 500;  // add integral if needed
syn_motor_ctrl_init(&ctrl, &cfg);


The documentation for this class was generated from the following file src/syntropic/motor/syn_motor_ctrl.h