Ruckig

repository·main·Indexed 23 days ago

https://github.com/pantor/ruckig

A library for instantaneous, jerk-limited motion generation for robots and machines. Ruckig calculates time-optimal trajectories to target waypoints (position, velocity, and acceleration) while respecting physical constraints. It supports both real-time online generation and offline trajectory calculation, with a Python module available for development and debugging.

Tokens
3.9K
Snippets
12
Records
15
Agent score
30%

What's inside ruckig

  1. Compare Ruckig performance and capabilities

    main

    Ruckig is designed for high-performance trajectory generation with the following characteristics:

    • Speed: It is more than twice as fast as Reflexxes Type IV for state-to-state motions and is suitable for control cycles as low as 250 microseconds.
    • Type V Capabilities: Unlike Reflexxes Type IV, Ruckig is a Type V trajectory generator capable of handling arbitrary target states and supporting directional velocity and acceleration limits.
    • Waypoint Trajectories: When using intermediate waypoints, Ruckig calculates path planning and time parametrization jointly. This typically improves trajectory duration by approximately 10% compared to libraries like Toppra, while remaining real-time capable and supporting jerk constraints.
  2. Use the Tracking Interface (Pro Version)

    main

    The Tracking Interface is designed to follow an arbitrary signal (position, velocity, acceleration) closely in time-optimal ways by predicting ahead. This is useful for robot servoing or signal tracking where standard target-setting would cause lag.

    To use it online:

    1. Construct a Trackig<DOFs> instance with the control cycle.
    2. Set the current state and kinematic constraints in InputParameter.
    3. In the control loop, call trackig.update(target_state, input, output) where target_state is the signal value at time t.
    4. Use output.new_position for control and call output.pass_to_input(input).

    To use it offline: Call smooth_trajectory = trackig.calculate_trajectory(target_states, input), where target_states is a std::vector of target states.

    // Online tracking example
    Trackig<1> trackig {0.01};
    input.current_position = {0.0};
    input.current_velocity = {0.0};
    input.current_acceleration = {0.0};
    input.max_velocity = {0.8};
    input.max_acceleration = {2.0};
    input.max_jerk = {5.0};
    
    for (double t = 0; t < 10.0; t += trackig.delta_time) {
      auto target_state = signal(t); 
      auto res = trackig.update(target_state, input, output);
      // Use output.new_position
      output.pass_to_input(input);
    }
    
    // Offline tracking
    smooth_trajectory = trackig.calculate_trajectory(target_states, input);
  3. How waypoint-based trajectory generation works

    main

    Ruckig uses three main interface classes: Ruckig, InputParameter, and OutputParameter.

    To generate trajectories online (e.g., in a real-time control loop), you initialize a Ruckig instance with the number of Degrees of Freedom (DoFs) and the control cycle duration. You then populate the InputParameter with the current state, target state, and kinematic limits.

    In each control cycle, call ruckig.update(input, output). If the result is Result::Working, you use the values in output to control your hardware. Crucially, you must call output.pass_to_input(input) at the end of every loop to copy the new kinematic state back into the input for the next iteration. The loop terminates when update returns Result::Finished.

    Ruckig<6> ruckig {0.001}; // Number DoFs; control cycle in [s]
    
    InputParameter<6> input;
    input.current_position = {0.2, ...};
    input.current_velocity = {0.1, ...};
    input.current_acceleration = {0.1, ...};
    input.target_position = {0.5, ...};
    input.target_velocity = {-0.1, ...};
    input.target_acceleration = {0.2, ...};
    input.max_velocity = {0.4, ...};
    input.max_acceleration = {1.0, ...};
    input.max_jerk = {4.0, ...};
    
    OutputParameter<6> output;
    
    while (ruckig.update(input, output) == Result::Working) {
      // Make use of the new state here!
      // e.g. robot->setJointPositions(output.new_position);
    
      output.pass_to_input(input); // Don't forget this!
    }
  4. Install the Ruckig Python module

    main

    The Ruckig Community Version is available as a Python module, which is useful for development and debugging. You can install it directly from PyPI:

    pip install ruckig

    If you are building Ruckig from source and only want the Python module (without the C++ library), you can install it using:

    pip install .

    When building from source with CMake, ensure you enable the BUILD_PYTHON_MODULE flag to include the Python module in the build process.

    pip install ruckig
  5. Ensure numerical stability and precision

    main

    Ruckig's numerical exactness is maintained within specific absolute error tolerances: final position and velocity within 1e-8, final acceleration within 1e-10, and velocity, acceleration, and jerk limits within 1e-12.

    To ensure stability, follow these guidelines:

    • Scale your inputs: Use units that align with these absolute tolerances. For most real-world systems, using meters [m] instead of millimeters [mm] is recommended, as 1e-8m provides sufficient precision.
    • Kinematic Limits: Keep all kinematic limits below 1e9.
    • Trajectory Duration: The maximal supported trajectory duration is 7e3. While Ruckig can output values outside this range, correctness is not guaranteed.

    If you are using the Ruckig Pro version, you can use position_scale and time_scale in the Calculator class to adjust the internal representation of parameters without affecting the resulting trajectory.

    Ruckig<1> ruckig;  // Works also for Trackig
    
    ruckig.calculator.position_scale = 1e2;  // Scales all positions in the input parameters
    ruckig.calculator.time_scale = 1e3;  // Scale all times in the input parameters
  6. Using intermediate waypoints

    main

    The Ruckig Community Version supports intermediate waypoints via a cloud API. To use them, you must pre-allocate memory by passing the maximum number of waypoints to the Ruckig, InputParameter, and OutputParameter constructors.

    Once allocated, you can set waypoints using input.intermediate_positions.

    Note: Using intermediate positions switches the calculation to a non-real-time capable cloud API. For real-time local calculation, the Ruckig Pro Version is required. It is recommended to filter waypoints using ruckig.filter_intermediate_positions to ensure they meet a minimum distance threshold.

    Ruckig<6> ruckig {0.001, 8};
    InputParameter<6> input {8};
    OutputParameter<6> output {8};
    
    input.intermediate_positions = {
      {0.2, ...},
      {0.8, ...},
    };
    
    // Recommended: filter waypoints
    input.intermediate_positions = ruckig.filter_intermediate_positions(input.intermediate_positions, {0.1, ...});
  7. Integrate Ruckig into a CMake project

    main

    You can integrate Ruckig into your existing CMake project in two ways:

    1. As a subdirectory: Include the Ruckig directory in your project and add it using add_subdirectory(ruckig) in your parent CMakeLists.txt.
    2. As a pre-built library: Refer to examples/CMakeLists.txt in the Ruckig repository for a reference implementation of how to link against it.
  8. Install Ruckig via CMake

    main

    Ruckig has no dependencies. To build it from source using CMake, create a build directory and run the following commands:

    mkdir -p build
    cd build
    cmake -DCMAKE_BUILD_TYPE=Release ..
    make

    To install it system-wide, use (sudo) make install or generate and install a Debian package using cpack:

    cpack
    sudo dpkg -i ruckig*.deb
    mkdir -p build
    cd build
    cmake -DCMAKE_BUILD_TYPE=Release ..
    make
  9. Use custom vector types (e.g., Eigen)

    main

    Ruckig allows you to use custom vector types to simplify integration with your existing math libraries.

    Eigen Integration: Include <Eigen/Core> before Ruckig and pass ruckig::EigenVector as a template parameter.

    Custom Types: You can provide a custom template template parameter that implements a minimal interface: operator[] (getter and setter), size(), operator==, and resize(size_t) (required for DynamicDOFs).

    // Eigen Example
    #include <Eigen/Core>
    #include <ruckig/ruckig.hpp>
    
    Ruckig<6, EigenVector> ruckig {0.001};
    InputParameter<6, EigenVector> input;
    OutputParameter<6, EigenVector> output;
  10. Perform offline trajectory calculation

    main

    If you do not need real-time updates, you can calculate a full trajectory at once using ruckig.calculate(input, trajectory).

    When using only the offline approach, the Ruckig constructor does not require a delta_time (control cycle) argument. If a delta_time was provided, you can still step through the resulting trajectory using ruckig.update(trajectory, output) starting from the current output.time (Ruckig Pro only).

    result = ruckig.calculate(input, trajectory);
  11. Validate input before calculation

    main

    Use ruckig.validate_input to check if a trajectory can be generated before calling update. This prevents runtime errors during real-time execution.

    • ruckig.validate_input(input, check_current_state_within_limits=false, check_target_state_within_limits=true): Returns true or throws an error with a detailed reason.
    • ruckig.validate_input<false>(...): Returns a boolean true/false instead of throwing.

    Setting both boolean arguments to true guarantees the trajectory will stay within kinematic limits throughout its duration.

    ruckig.validate_input(input, check_current_state_within_limits=false, check_target_state_within_limits=true);
  12. Use Dynamic Degrees of Freedom

    main

    If the number of DoFs is not known at compile-time, use the ruckig::DynamicDOFs template parameter. This switches the internal vector type from std::array to std::vector.

    Note: Using dynamic DoFs has a small performance penalty and requires manual memory allocation for all vectors beforehand. It is recommended to use fixed DoFs via template parameters whenever possible for better performance and real-time safety.

    Ruckig<DynamicDOFs> ruckig {6, 0.001};
    InputParameter<DynamicDOFs> input {6};
    OutputParameter<DynamicDOFs> output {6};