Skip to main content

Online Trajectory Generation. Real-time. Jerk-constrained. Time-optimal.

Project description

Ruckig

Online Trajectory Generation. Real-time. Jerk-constrained. Time-optimal.

CI Issues Releases MIT

Ruckig calculates a time-optimal trajectory to a target waypoint with position, velocity, and acceleration starting from any initial state limited by velocity, acceleration, and jerk constraints. Ruckig is a more powerful and open-source alternative to the Reflexxes Type IV library. In fact, Ruckig is the first Type V trajectory generator for arbitrary target states and even supports directional velocity and acceleration limits, while also being faster on top. For robotics and machining applications, Ruckig allows both instant reactions to unforeseen events as well as simple offline trajectory planning.

More information can be found in the corresponding paper Jerk-limited Real-time Trajectory Generation with Arbitrary Target States, accepted for the Robotics: Science and Systems (RSS), 2021 conference.

Installation

Ruckig has no dependencies (except for testing). To build Ruckig using CMake, just run

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

To install Ruckig in a system-wide directory, use (sudo) make install. An example of using Ruckig in your CMake project is given by examples/CMakeLists.txt. However, you can also include Ruckig as a directory within your project and call add_subdirectory(ruckig) in your parent CMakeLists.txt.

Ruckig is also available as a Python module, in particular for development or debugging purposes. It can be installed from PyPI via

pip install ruckig

When using CMake, the Python module can be built using the BUILD_PYTHON_MODULE flag. If you're only interested in the Python module (and not in the C++ library), you can build and install Ruckig via pip install ..

Tutorial

Furthermore, we will explain the basics to get started with online generated trajectories within your application. Some working examples can be found in the examples directory, for example position.cpp for C++. A time-optimal trajectory for a single degree of freedom is shown in the figure below.

Trajectory Profile

Waypoint-based Trajectory Generation

Ruckig provides three main interface classes: the Ruckig, the InputParameter, and the OutputParameter class.

First, you'll need to create a Ruckig instance with the number of DoFs as a template parameter, and the control cycle (e.g. in seconds) in the constructor.

Ruckig<6> ruckig {0.001}; // Number DoFs; control cycle in [s]

The input type has 3 blocks of data: the current state, the target state and the corresponding kinematic limits.

InputParameter<6> input; // Number DoFs
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; // Number DoFs

Given all input and output resources, we can iterate over the trajectory at each discrete time step. For most applications, this loop must run within a real-time thread and controls the actual hardware.

while (ruckig.update(input, output) == Result::Working) {
  // Make use of the new state here!

  input.current_position = output.new_position;
  input.current_velocity = output.new_velocity;
  input.current_acceleration = output.new_acceleration;
}

During the update step, you'll need to copy the new kinematic state into the current state. If the current state is not the expected, pre-calculated trajectory, ruckig will calculate a new trajectory with the novel input. When the trajectory has reached the target state, the update function will return Result::Finished.

Input Parameter

To go into more detail, the InputParameter type has following members:

using Vector = std::array<double, DOFs>; // By default

Vector current_position;
Vector current_velocity; // Initialized to zero
Vector current_acceleration; // Initialized to zero

Vector target_position;
Vector target_velocity; // Initialized to zero
Vector target_acceleration; // Initialized to zero

Vector max_velocity;
Vector max_acceleration;
Vector max_jerk;

std::optional<Vector> min_velocity; // If not given, the negative maximum velocity will be used.
std::optional<Vector> min_acceleration; // If not given, the negative maximum acceleration will be used.

std::array<bool, DOFs> enabled; // Initialized to true
std::optional<double> minimum_duration;

ControlInterface control_interface; // The default position interface controls the full kinematic state.
Synchronization synchronization; // Synchronization behavior of multiple DoFs
DurationDiscretization duration_discretization; // Whether the duration should be a discrete multiple of the control cycle (off by default)

On top of the current state, target state, and constraints, Ruckig allows for a few more advanced settings:

  • A minimum velocity and acceleration can be specified - these should be a negative number. If they are not given, the negative maximum velocity or acceleration will be used (similar to the jerk limit). For example, this might be useful in human robot collaboration settings with a different velocity limit towards a human. Or, when switching between different moving coordinate frames like picking from a conveyer belt.
  • If a DoF is not enabled, it will be ignored in the calculation. Ruckig will output a trajectory with constant acceleration for those DoFs.
  • A minimum duration can be optionally given. Note that Ruckig can not guarantee an exact, but only a minimum duration of the trajectory.
  • The control interface (position or velocity control) can be switched easily. For example, a stop trajectory or visual servoing can be easily implemented with the velocity interface.
  • Different synchronization behaviors (i.a. phase, time, or no synchonization) are implemented.
  • The trajectory duration might be constrained to a multiple of the control cycle. This way, the exact state can be reached at a control loop execution.

We refer to the API documentation of the enumerations within the ruckig namespace for all available options.

Input Validation

Note that there are range constraints of the input due to numerical reasons, see below for more details. To check the input before a calculation step,

ruckig.validate_input(input); // returns boolean

returns false if an input is not valid. Of course, the target state needs to be within the given kinematic limits. Additionally, the target acceleration needs to fulfil

Abs(target_acceleration) <= Sqrt(2 * max_jerk * (max_velocity - Abs(target_velocity)))

Result Type

The update function of the Ruckig class returns a Result type that indicates the current state of the algorithm. This can either be working, finished if the trajectory has finished, or an error type if something went wrong during calculation. The result type can be compared as a standard integer.

State Error Code
Working 0
Finished 1
Error -1
ErrorInvalidInput -100
ErrorTrajectoryDuration -101
ErrorExecutionTimeCalculation -110
ErrorSynchronizationCalculation -111

Output Parameter

The output class includes the new kinematic state and the overall trajectory.

Vector new_position;
Vector new_velocity;
Vector new_acceleration;

bool new_calculation; // Whether a new calculation was performed in the last cycle
double calculation_duration; // Duration of the calculation in the last cycle [µs]

Trajectory trajectory; // The current trajectory
double time; // The current, auto-incremented time. Reset to 0 at a new calculation.

Moreover, the trajectory class has a range of useful parameters and methods.

double duration; // Duration of the trajectory
std::array<double, DOFs> independent_min_durations; // Time-optimal profile for each independent DoF

<...> at_time(double time); // Get the kinematic state of the trajectory at a given time
<...> get_position_extrema(); // Returns information about the position extrema and their times

Again, we refer to the API documentation for the exact signatures.

Dynamic Number of Degrees of Freedom

So far, we have told Ruckig the number of DoFs as a template parameter. If you don't know the number of DoFs at compile-time, you can set the template parameter to DynamicDOFs and pass the DoFs to the constructor:

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

However, we recommend to keep the template parameter when possible: First, it has a performance benefit of a few percent. Second, it is convenient for real-time programming due to its easier handling of memory allocations. When using dynamic degrees of freedom, make sure to allocate the memory of all vectors beforehand.

Offline Calculation

Ruckig also supports an offline approach for calculating a trajectory:

result = ruckig.calculate(input, trajectory);

When only using this method, the Ruckig constructor does not need a control cycle as an argument.

Tests and Numerical Stability

The current test suite validates over 5.000.000.000 random trajectories. The numerical exactness is tested for the final position and final velocity to be within 1e-8, for the final acceleration to be within 1e-10, and for the velocity, acceleration and jerk limit to be within of a numerical error of 1e-12. These are absolute values - we suggest to scale your input so that these correspond to your required precision of the system. For example, for most real-world systems we suggest to use input values in [m] (instead of e.g. [mm]), as 1e-8m is sufficient precise for practical trajectory generation. Furthermore, all kinematic limits should be below 1e12. The maximal supported trajectory duration is 7e3, which again should suffice for most applications seeking for time-optimality. Note that Ruckig will also output values outside of this range, there is however no guarantee for correctness.

Benchmark

We find that Ruckig is more than twice as fast as Reflexxes Type IV and well-suited for control cycles as low as half a millisecond.

Benchmark

Development

Ruckig is written in C++17. It is continuously tested on ubuntu-latest, macos-latest, and windows-latest against following versions

  • Doctest v2.4 (only for testing)
  • Pybind11 v2.6 (only for python wrapper)

If you still need to use C++11, you can patch Ruckig by calling sh patch-c++11.sh. Note that this will result in a performance drop of a few percent. Moreover, the Python module is not supported.

Users

Ruckig is used by:

  • CoppeliaSim in their upcoming release.
  • MoveIt 2 for trajectory smoothing.
  • Struckig, a port of Ruckig to Restructered Text for usage on PLCs.
  • Frankx for controlling the Franka Emika robot arm.
  • and others!

Citation

@article{berscheid2021jerk,
  title={Jerk-limited Real-time Trajectory Generation with Arbitrary Target States},
  author={Berscheid, Lars and Kr{\"o}ger, Torsten},
  journal={Robotics: Science and Systems XVII},
  year={2021}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ruckig-0.4.0.tar.gz (45.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ruckig-0.4.0-cp39-cp39-win_amd64.whl (243.0 kB view details)

Uploaded CPython 3.9Windows x86-64

ruckig-0.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (307.5 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.12+ x86-64

ruckig-0.4.0-cp39-cp39-macosx_10_15_x86_64.whl (232.4 kB view details)

Uploaded CPython 3.9macOS 10.15+ x86-64

ruckig-0.4.0-cp38-cp38-win_amd64.whl (243.1 kB view details)

Uploaded CPython 3.8Windows x86-64

ruckig-0.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (307.1 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.12+ x86-64

ruckig-0.4.0-cp38-cp38-macosx_10_15_x86_64.whl (232.3 kB view details)

Uploaded CPython 3.8macOS 10.15+ x86-64

ruckig-0.4.0-cp37-cp37m-win_amd64.whl (242.3 kB view details)

Uploaded CPython 3.7mWindows x86-64

ruckig-0.4.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (308.5 kB view details)

Uploaded CPython 3.7mmanylinux: glibc 2.12+ x86-64

ruckig-0.4.0-cp37-cp37m-macosx_10_15_x86_64.whl (227.8 kB view details)

Uploaded CPython 3.7mmacOS 10.15+ x86-64

ruckig-0.4.0-cp36-cp36m-win_amd64.whl (242.3 kB view details)

Uploaded CPython 3.6mWindows x86-64

ruckig-0.4.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (308.4 kB view details)

Uploaded CPython 3.6mmanylinux: glibc 2.12+ x86-64

ruckig-0.4.0-cp36-cp36m-macosx_10_15_x86_64.whl (227.8 kB view details)

Uploaded CPython 3.6mmacOS 10.15+ x86-64

File details

Details for the file ruckig-0.4.0.tar.gz.

File metadata

  • Download URL: ruckig-0.4.0.tar.gz
  • Upload date:
  • Size: 45.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.8.11

File hashes

Hashes for ruckig-0.4.0.tar.gz
Algorithm Hash digest
SHA256 892f36ed63da70d22a673fe69c29c3051fcd8de79e1afc48c448e2f7d3fbc222
MD5 786df5fb419747b6713057ba8670a345
BLAKE2b-256 685bfbbd0600d2e54762e75c06a62f78234f6f82f2f0cd6c5282a7e9c29bdcf9

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 243.0 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.9.6

File hashes

Hashes for ruckig-0.4.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 6664abbe2ee78cd623cbfd95855b74fa7e3c013249ee942642aeaaefa36a9c43
MD5 6e2c83ef5b9088eb1e97854bedb1ac24
BLAKE2b-256 9783937c2f771812a9fca7e65bfc07ec96c096114ce930c93655d8cecf8b1251

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
  • Upload date:
  • Size: 307.5 kB
  • Tags: CPython 3.9, manylinux: glibc 2.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.8.11

File hashes

Hashes for ruckig-0.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 7d3fc2e4b189c0dddea40a614d73cbba6b882a5e685f047c699776d264d09a55
MD5 79650689b1ee46543f85cf4e366c55f4
BLAKE2b-256 ed7900c1d79b5a5f6fe15e14e10eb5bc94177d40f24a679a9e346d04e179da42

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp39-cp39-macosx_10_15_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp39-cp39-macosx_10_15_x86_64.whl
  • Upload date:
  • Size: 232.4 kB
  • Tags: CPython 3.9, macOS 10.15+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.9.6

File hashes

Hashes for ruckig-0.4.0-cp39-cp39-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 fc538b91947d7d4985320b8fde411fc86f854f88ffdf11f25d4c0d7bde93f1f1
MD5 5c04da85aa715930575c3e1a870314b7
BLAKE2b-256 dc4f2205ab40dd5b6a6352b4d7c6a7ab9d5950e6bd5477151d4bd255249702a0

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 243.1 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.8.10

File hashes

Hashes for ruckig-0.4.0-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 5fb0d09480c6528a726a43499c9ae07f05110ca083abb532c3d434cfda6b2c40
MD5 f86fc6c21302edab90219f45c548266d
BLAKE2b-256 666a42519a6662bbe39e2a71044b78709b3ab236f57280a8dc2199362c1aa817

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
  • Upload date:
  • Size: 307.1 kB
  • Tags: CPython 3.8, manylinux: glibc 2.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.8.11

File hashes

Hashes for ruckig-0.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 a66d4c616d1ba7c82c75c993804678753a9b65cc385854fefbbcada291160964
MD5 e2eb8ce5911652726e1759d15b593c67
BLAKE2b-256 30d6c4c86b4cc4b3f1d4c84a762e3eb9c26f1608d026cc53f543ec2db667685d

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp38-cp38-macosx_10_15_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp38-cp38-macosx_10_15_x86_64.whl
  • Upload date:
  • Size: 232.3 kB
  • Tags: CPython 3.8, macOS 10.15+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.8.11

File hashes

Hashes for ruckig-0.4.0-cp38-cp38-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 3b8bd47c8c0e6d376455653654ffff1bf29411f444e77a6591818082fc169626
MD5 7448ec5e6563255d70395bdaf6b21729
BLAKE2b-256 995a7880e85eab8194e29dc02d0a1d2451a09209ea7289544039de89b4753975

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 242.3 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.7.9

File hashes

Hashes for ruckig-0.4.0-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 76ef491f086719896143b4a6b7305dd2cdebd74a3b9eb5f67314a91454e2f539
MD5 cdc429de05bfd2169c02bc7fb7a73937
BLAKE2b-256 f5af4e6e40058fda73182696029f0cca07a4ead1990aa7941f041f96db547dc1

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

File hashes

Hashes for ruckig-0.4.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 ec936e645ab60da5583fd6acdd298f265553d2adcec15fabb824be69e3119929
MD5 d538f1c869cd27dc5c7ef32038c22039
BLAKE2b-256 16afc410a847ae4f5a77b6be2de7ff02d1c3052868f0d96cba8f3ce4d99f9505

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp37-cp37m-macosx_10_15_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp37-cp37m-macosx_10_15_x86_64.whl
  • Upload date:
  • Size: 227.8 kB
  • Tags: CPython 3.7m, macOS 10.15+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.7.11

File hashes

Hashes for ruckig-0.4.0-cp37-cp37m-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 a3b0ffce98e7054b89c5558f000e8c2fe4f54f2baabf5dad1cd5e69ffdd6ca9b
MD5 67002ee36cc2ea3440fa96a6861c9b86
BLAKE2b-256 4c7a8eda566a377423980dc4245f23ae929d11c831dc7a22cc5e632920942e9f

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp36-cp36m-win_amd64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp36-cp36m-win_amd64.whl
  • Upload date:
  • Size: 242.3 kB
  • Tags: CPython 3.6m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.6.8

File hashes

Hashes for ruckig-0.4.0-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 a8c0da094c29271303d432dfe01d778a1f82164efaa6a6805562433da26f0368
MD5 c09200038772f264d0bff0efd6b61dd1
BLAKE2b-256 00e8f51a04c4721629255b30cb389107ca7b56790f92025cbc7a76c7326bfe84

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

File hashes

Hashes for ruckig-0.4.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 6396ed7a6ef78357d9445a863c6fc7f80cc3382be327cdfbd295352bda713059
MD5 7a5e5a2224499194bc3d06380adfbab3
BLAKE2b-256 c8d41f6b31a76c39c9168f8b4aaba5e7b0ef696741fa8e62a24876f368caf0dd

See more details on using hashes here.

File details

Details for the file ruckig-0.4.0-cp36-cp36m-macosx_10_15_x86_64.whl.

File metadata

  • Download URL: ruckig-0.4.0-cp36-cp36m-macosx_10_15_x86_64.whl
  • Upload date:
  • Size: 227.8 kB
  • Tags: CPython 3.6m, macOS 10.15+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.4 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.1 CPython/3.6.14

File hashes

Hashes for ruckig-0.4.0-cp36-cp36m-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 e5ffb30d07a6866aaeebbc6094b1021964d9cfa79e1f9c4fc8ce9990c41d3cd4
MD5 e0ca9306a897932ab2abfb3c2ec9611f
BLAKE2b-256 771682ab9770bbec5c33072dad18f9e2789747176c243b2cfbe13abdfe91ba70

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page