Kinematic Mode

Kinematic mode is one of the means of controlling an ORCA motor’s position. Kinematic motions are parametrized motions which can be stored in your motor to be executed at a later time. They are well suited for applications in which you want the motor to smoothly reach a position target without particularly caring about fine control over that motion. In this tutorial we will set up an app that sets up and periodically triggers a small set of kinematic motions.

Prerequisites

  • Modes of Operation

  • (Optional) We recommend reading the ORCA Series Reference Manual’s section “Controllers -> Kinematic Controller”. The reference manual can be found on our downloads page.


We’ll start with the following code as our baseline:

#include <iostream>
#include "actuator.h"

using namespace orcaSDK ;

int main() {
	Actuator motor;

	int serial_port;
	std::cout << "Please input the serial port number of your connected motor. ";
	std::cin >> serial_port;

	motor.open_serial_port(serial_port);

	while(true)
	{
		std::cout << "Current Position: " << motor.get_position_um().value << "          \r";
	}

	return 0;
}
from pyorcasdk import Actuator

motor = Actuator()

serial_port = int(input("Please input the serial port number of your connected motor. "))

motor.open_serial_port(serial_port)

while True:
	print("Current Position: " + str(motor.get_position_um().value), end="        \r")

Set up the Kinematic Motions

First we will set up our kinematic motions as we desire. We will define two motions with one linking into the other. This is the code for setting up these motions:

...
motor.set_kinematic_motion(0, 50000, 1000, 0, 1, true, 1);
motor.set_kinematic_motion(1, 10000, 1000, 0, 1, false);
...
...
motor.set_kinematic_motion(0, 50000, 1000, 0, 1, True, 1)
motor.set_kinematic_motion(1, 10000, 1000, 0, 1, False)
...

That is a lot of parameters. Within those numbers is the definition for an ORCA kinematic motion. Let’s break down each parameter for the first motion:

motor.set_kinematic_motion(
        0,     // The motion ID to be updated
        50000, // The position in micrometers that this motion should move to
        1000,  // The time in milliseconds that the motion should take
        0,     // The delay in milliseconds after completing this motion before starting the next
        1,     // The motion shape (minimized power vs jerk)
        true,  // Whether this motion should link into another automatically
        1      // The motion ID that should be automatically linked to
);
motor.set_kinematic_motion(
        0,     # The motion ID to be updated
        50000, # The position in micrometers that this motion should move to
        1000,  # The time in milliseconds that the motion should take
        0,     # The delay in milliseconds after completing this motion before starting the next
        1,     # The motion shape (minimized power vs jerk)
        True,  # Whether this motion should link into another automatically
        1      # The motion ID that should be automatically linked to
)

Now to describe this function call in english. We edit motion with ID 0, to move to position 50000um in 1000ms, then without delay begin motion 1. The second statement is similar, but moves to position 10000um and doesn’t link to another motion.

Next we need to switch to kinematic mode. Make sure that your motor has room to move.

...
motor.set_mode(MotorMode::KinematicMode);
...
from pyorcasdk import MotorMode
...
motor.set_mode(MotorMode.KinematicMode)
...

This command switches the mode of operation. Switching to kinematic mode automatically executes the home motion of the motor. Try running the code and see what it does. If your motor’s home motion matches the default, it should execute the motions that we just defined.

Repeat the Kinematic Motions / Detect Motion Completion

There are two ways to execute a kinematic motion on an ORCA motor. We’ve demonstrated the first way already: switching to kinematic mode. Additionally motions can be triggered by calling an Actuator object’s trigger_kinematic_motion() method.

...
motor.trigger_kinematic_motion(0);
...
...
motor.trigger_kinematic_motion(0)
...

Note that this method only works if the motor is already in kinematic mode. Calling it in any other mode will result in no action being taken.

Now we can’t simply call this function in a loop. The kinematic controller is designed to complete its full action after a single trigger. To repeatedly trigger the same motion would be to rapidly interrupt the kinematic controller as it tries to perform the motion. Before we trigger the next motion, we must first wait for it to complete its current motion. Fortunately the motor exposes some information which allows us to detect if it’s mid-motion.

The motor stores information about its current kinematic motions in its ‘Kinematic Status’ register. We can access it through the read_register_blocking() method:

...
uint16_t kin_status = motor.read_register_blocking(ORCAReg::KINEMATIC_STATUS).value;
...
import pyorcasdk.orca_registers as orca_reg
...
kin_status = motor.read_register_blocking(orca_reg.KINEMATIC_STATUS).value
...

The Kinematic Status register is a bit field. In this register, the smallest 15 bits represent the ID of the currently running kinematic motion. The highest bit on the other hand is simply 1 if a kinematic motion is currently executing, and 0 otherwise. That highest bit is the one that we’re interested in. A small bitwise operation allows us to isolate it from the rest of the register.

...
int motion_is_running = kin_status >> ORCAReg::KINEMATIC_STATUS_Values::RUNNING_Shift;
...
...
motion_is_running = kin_status >> orca_reg.KINEMATIC_STATUS_RUNNING_Shift
...

Finally let’s combine these concepts to make the program wait until the current motion is finished, and repeat the motion when it finishes.

...
while(true)
{
        uint16_t kin_status = motor.read_register_blocking(ORCAReg::KINEMATIC_STATUS).value;
        if (!(kin_status >> ORCAReg::KINEMATIC_STATUS_Values::RUNNING_Shift))
        {
                motor.trigger_kinematic_motion(0);
        }

        std::cout << "Current Position: " << motor.get_position_um().value << "          \r";
}
...
...
while True:
        kin_status = motor.read_register_blocking(orca_reg.KINEMATIC_STATUS).value
        if (not (kin_status >> orca_reg.KINEMATIC_STATUS_RUNNING_Shift)):
                motor.trigger_kinematic_motion(0)

        print("Current Position: " + str(motor.get_position_um().value), end="        \r")
...

Complete Example

#include <iostream>
#include "actuator.h"

using namespace orcaSDK;

int main() {
	Actuator motor;

	int serial_port;
	std::cout << "Please input the serial port number of your connected motor. ";
	std::cin >> serial_port;

	motor.open_serial_port(serial_port);

	//Motion that moves to position 50000 in 1000 milliseconds, then triggers motion 1
	motor.set_kinematic_motion(0, 50000, 1000, 0, 1, true, 1);
	//Motion that moves to position 10000 in 1000 milliseconds
	motor.set_kinematic_motion(1, 10000, 1000, 0, 1, false);

	motor.set_mode(MotorMode::KinematicMode);

	while(true)
	{
		uint16_t kin_status = motor.read_register_blocking(ORCAReg::KINEMATIC_STATUS).value;
		if (!(kin_status >> ORCAReg::KINEMATIC_STATUS_Values::RUNNING_Shift))
		{
			motor.trigger_kinematic_motion(0);
		}
		
		std::cout << "Current Position: " << motor.get_position_um().value << "          \r";
	}

	return 0;
}
from pyorcasdk import Actuator, MotorMode 
import pyorcasdk.orca_registers as orca_reg

motor = Actuator()

serial_port = int(input("Please input the serial port number of your connected motor. "))

motor.open_serial_port(serial_port)

motor.set_kinematic_motion(0, 50000, 1000, 0, 1, True, 1)
motor.set_kinematic_motion(1, 10000, 1000, 0, 1, False)

motor.set_mode(MotorMode.KinematicMode)

while True:
	kin_status = motor.read_register_blocking(orca_reg.KINEMATIC_STATUS).value
	if (not (kin_status >> orca_reg.KINEMATIC_STATUS_RUNNING_Shift)):
		motor.trigger_kinematic_motion(0)

	print("Current Position: " + str(motor.get_position_um().value), end="        \r")