This repository contains the src code of an Orocos Component that can be used to control the Kinova Gen3 Ultra lightweight robot.
Before using it, it is recommended to read carefully the User Guide of the robot.
To install and run this package, you need to have the following dependencies installed in computer with Ubuntu 16.04 or Ubuntu 18.04
- OROCOS Toolchain version 2.9.0
- gcc version >=5.4.0
- CMake version >=3.10
Other versions might as well work, but haven't been tested.
To easily install the driver, ROS CATKIN is used. Installation with standard cmake/make is also possible but has not been tested (might be necessary to slightly modify the CMakeLists.txt files).
cd <Your installation directory>
git clone https://gitlab.mech.kuleuven.be/r0742557/kinova_driver.git
After cloning this repository, please download the Kinova Kortex C++ API 2.2.0. Uncompress the file and copy the two folders that you find there (include and lib). Then paste them into the directory kinova_driver/include/kortex_api/
Then go to the root of your catkin workspace and run:
catkin_make
Note: The installation directory should be in the src folder of a catkin workspace
In this guide we are going to use the LUA scripting language for deploying our Orocos application. For more information, please check out the Orocos LUA Cookbook and the API documentation.
1. Load the Orocos libraries and activate the printing in colors functionality (Looks prettier in the terminal!).
require "rttlib"
require "rttros"
require "utils"
rttlib.color=true
2. Load the driver component
tc=rtt.getTC()
depl=tc:getPeer("Deployer")
depl:import("kinova_driver")
depl:loadComponent("kin", "kinova_gen3")
kin = depl:getPeer("kin")
For observing the available ports, properties and operations (and a short documentation):
print(kin)
3. You can connect the ports to the port of another component in order to obtain sensor information. However, for this example, we are going to create a port in the deployer:
cp=rtt.Variable("ConnPolicy")
sensor_port = rtt.InputPort("array")
sensor_port:connect(kin:getPort("sensor_joint_angles"),cp)
fs, data =sensor_port:read()
print(data)
4. Setup a non-periodic activity for the driver. The internal code of the driver will make the periodicity of the component to be at 1kHz. This is because the control loop of the robot works at 1 kHz (consult the user guide for more information). Do not setup a periodicity manually or you will get an error.
depl:setActivity("kin", 0, 0, rtt.globals.ORO_SCHED_RT)
5. Setup the properties of the driver.
kin:getProperty("setpoints_frequency"):set(motion_freq)
kin:getProperty("ip_address"):set("192.168.1.10")
The first line sets the frequency in which you generate the setpoints. If the frequency is less than 1kHz, then the driver will linearly interpolate between the setpoints to keep a smooth motion at 1kHz. If the frequency is >= 1kHz, then the driver will not interpolate (however, it doesn't make sense to send setpoints at a higher frequency than 1kHz to the driver). The second line is not necessary in case the ip_address is the default one (192.168.1.10). 6. Configure and start the execution of the driver
kin:configure() --Runs the configureHook() in the source code
kin:start() -- Runs the startHook() and starts the periodic execution of updateHook() (called by the Orocos execution engine)
For testing on the fly, you can print all values of the sensors in a JSON string format using the get_all_sensor_jsonstring() operation:
print(kin:get_all_sensor_jsonstring())
There are three servoing modes in which you can configure the driver. This is setup with an operation as follows:
kin:set_servoing_mode(Value) --See the table for valid Values
Mode | Value | Interface |
---|---|---|
HIGH_LEVEL | 0 | Orocos Operations |
JOINT_VEL_LOW_LEVEL | 1 | Port: control_joint_velocities |
JOINT_POS_LOW_LEVEL | 2 | Port: control_joint_positions |
HIGH_LEVEL
This mode allows you to send a joint position or a cartesian pose command to the robot (once), and the robot will reach it (using the kinematic library of Kinova).
You can use the following operations when in HIGH_LEVEL mode:
- To change the aperture of the gripper (0 for fully opened, 1 for fully closed). Currently, the gripper supported is the ROBOTIQ 2F-85 adaptive gripper.
kin:set_servoing_mode(0) -- Execute this once
kin:change_gripper_aperture(0.5) --halfway opened
- To reach a position in the jointspace:
jvals_tab = {0,0,0,0,0,0,0} -- Sets all joints to 0 radians
jvals=rtt.Variable("array")
jvals:fromtab(jvals_tab)
kin:reach_joint_angles(jvals)
- To reach a position in the jointspace:
pose_tab = {x,y,z,roll,pitch,yaw} --Use your own values
pose=rtt.Variable("array")
pose:fromtab(pose_tab)
kin:reach_cartesian_pose(pose)
When the robot or the gripper reaches the desired position, you can get events (string notifications) through the event_port. This can be used when programming a finite state machine (as it is done in one of the examples using rFSM).
JOINT_VEL_LOW_LEVEL
This mode allows you to continuously send joint velocities at a frequency of 1 kHz (the control loop rate of the robot). You can also send the velocity setpoints at a lower rate, will linearly interpolate based on the setpoints_frequency you defined.
For using this mode you must first connect the port "control_joint_velocities" to the port of your trajectory generating component.
kin:set_servoing_mode(1) --Sets the servoing mode to low level velocity mode
kin:start_sending_setpoints() -- Starts sending the setpoints it gets from the port to the robot
Each time the set_servoing_mode() operation is called, it automatically stops sending the setpoints to the robot (for low level mode). Thus, start_sending_setpoints() should be called each time after you call set_servoing_mode() to set a low level mode.
If your trajectory generating components needs the initial joint angles (probably it would!) you can use the following before start sending the setpoints:
initial = kin:get_joint_angles() -- Obtain the joint angles in an array (rtt.Variable('array'))
JOINT_POS_LOW_LEVEL
This mode allows you to continuously send joint angles at a frequency of 1 kHz (the control loop rate of the robot). You can also send the velocity setpoints at a lower rate, will linearly interpolate based on the setpoints_frequency you defined
For using this mode you must first connect the port "control_joint_positions" to the port of your trajectory generating component.
kin:set_servoing_mode(1) --Sets the servoing mode to low level velocity mode
kin:start_sending_setpoints() -- Starts sending the setpoints it gets from the port to the robot
Each time the set_servoing_mode() operation is called, it automatically stops sending the setpoints to the robot (for low level mode). Thus, start_sending_setpoints() should be called each time once after you call set_servoing_mode() to set a low level mode.
If your trajectory generating components needs the initial joint angles (probably it would!) you can use the following before start sending the setpoints:
initial = kin:get_joint_angles() -- Obtain the joint angles in an array (rtt.Variable('array'))
In all servoing modes you can access different sensor information though ports (the refresh rate of the port is the same as the frequency assigned to the kinova_driver activity).
Port | Description |
---|---|
sensor_joint_angles | 7 elements with angles [rad] |
sensor_joint_velocities | 7 elements with velocities [rad/s] |
sensor_joint_torques | 7 elements with torques [Nm] |
tool_pose | 3 elements with position [m] and 3 with RPY angles [rad] |
tool_twist | 3 elements with linear velocity [m/s] and 3 with angular velocity [rad/s] |
tool_external_wrench* | 3 elements with forces [N] and 3 with torques [Nm] (estimation by measuring actuator torques) |
tool_imu | 3 IMU linear accelerations [m/s^2] and 3 IMU angular velocities [rad/s] |
gripper_feedback | position [0-1], velocity [0-1] and current [mA] (first two are dimensionless) |
In addition, there is a special port called "event_port", which generates string events when the robot reaches a position commanded while in high level servoing mode (except for gripper_done_no_obj and gripper_done_with_obj, which also work in low level mode).
Event | Description |
---|---|
cart_done | Send when robot reaches a specified cartesian position (after reach_cartesian_pose()) |
joints_done | Send when robot reaches a specified joint position (after reach_joint_angles()) |
gripper_done_no_obj | Send when gripper reaches a specified aperture (after change_gripper_aperture() or change_gripper_aperture_low_level()) |
gripper_done_with_obj*** | Send when the gripper cannot reach the specified aperture because it grasped an object (after change_gripper_aperture() or change_gripper_aperture_low_level()) |
cart_aborted | Send when a specified cartesian position is aborted**** |
joints_aborted | Send when a specified joint position is aborted**** |
gripper_aborted | Send when a specified aperture is aborted**** |
* Currently not supported by the Kortex API
*** It supports internal and external object grasping
**** One way it can be aborted is by pushung RB button in the Xbox controller
You can configure the following properties to change the default behaviour of the driver:
Property | Description |
---|---|
ip_address | ip address of the kinova robot. It is by default set to the factory IP |
setpoints_frequency | Frequency at which the low level setpoints are sent |
debug_mode | If set to true before calling configureHook, additional ports are created to stream data related to timings |
velocity_limits | Velocity limits of each joint, used for safety purposes [rad/s]. Set by default to the 'general limits' specified in the user manual |
acceleration_limits | Acceleration limits of each joint, used for safety purposes [rad/s^2]. Set by default to the 'hard limits' specified in the user manual. Only checked when sending velocity setpoints (it's estimation from position is pretty bad). |
torque_limits | Torque limits of each joint, used for safety purposes [Nm]. Set by default to the 'soft limits' specified in the user manual |
force_gripper | Percentage of force applied by the gripper when grasping an object. Value from 0 to 100. Used when using change_gripper_aperture_low_level operation. |
In the directory kinova_driver/examples there are some examples (in different folders) that you can consult and use as a starting point for deploying your own applications.
- To run the high level example, type in the terminal:
cd kinova_driver/examples/example_high_level
rttlua -i deploy_kinova_driver.lua
Finite state machines can be implemented in rFSM by using the events obtained in the event_port of the driver. An event is sent throught the port after the robot completes the action commanded with the corresponding HIGH_LEVEL operation.
In this particular example, a simple pick and place task was programmed:
- The robot opens the gripper
- The robots goes to a position defined in the jointspace.
- The robot keeps closing and opening the gripper until someone handles it an object (internal or external grasping).
- The robot goes to a pose defined in the cartesian space.
- The robot releases the object (e.g. if it is internal grasping it closes the gripper).
- The robot returns to the same position defined in the jointspace and repeats
- To run the low level position example, type in the terminal:
cd kinova_driver/examples/example_low_level_position
rttlua -i deploy_kinova_driver.lua
- To run the low level velocity example, type in the terminal:
cd kinova_driver/examples/example_low_level_velocity
rttlua -i deploy_kinova_driver.lua