Given a set of desired joint configuration waypoints and a torque-controlled manipulator, this example shows how to implement the computed-torque controller using the inverseDynamics
function. The controller enables the robot to follow the given configuration waypoints along a smooth trajectory.
Spawn an LBR robot in Gazebo Simulator. Follow steps in the Get Started with Gazebo and a Simulated TurtleBot (ROS Toolbox) to launch the Gazebo LBR Simulator
from the Ubuntu® virtual machine desktop.
Gazebo LBR Simulator
brings up a barebone KUKA Light Weight Robot (LBR) manipulator with no default position, velocity or safety controllers. The only way to move the robot is through joint torques. Once the simulation starts running, the LBR arm will fall onto the ground due to no joint torque input.
In your MATLAB instance on the host computer, run the following commands to initialize ROS global node in MATLAB and connect to the ROS master in the virtual machine (where Gazebo is running) through its IP address. Replace ipaddress
with the IP address of your virtual machine.
ipaddress = '192.168.233.133';
rosinit(ipaddress,11311);
Initializing global node /matlab_global_node_56016 with NodeURI http://192.168.233.1:55980/
rigidBodyTree
Object from URDFlbr = importrobot('iiwa14.urdf'); lbr.DataFormat = 'row';
Set the gravity to be the same as that in Gazebo.
lbr.Gravity = [0 0 -9.80];
Show home configuration in a MATLAB figure.
show(lbr); view([150 12]); axis([-0.6 0.6 -0.6 0.6 0 1.35]); camva(9); daspect([1 1 1]);
Load joint configuration waypoints. This gives the key frames for the desired motion of the robot.
load lbr_waypoints.mat
cdt
is the planned control stepsize. We use it to populate a set of time points where the trajectory needs to be evaluated and store it in vector tt
.
cdt = 0.001; tt = 0:cdt:5;
Generate desired motion trajectory for each joint. exampleHelperJointTrajectoryGeneration
generates joint trajectories from given time and joint configuration waypoints.
The trajectories are generated using pchip
so that the interpolated joint position does not violate joint limits as long as the waypoints do not.
[qDesired, qdotDesired, qddotDesired, tt] = exampleHelperJointTrajectoryGeneration(tWaypoints, qWaypoints, tt);
Pre-compute feed-forward torques that ideally would realize the desired motion (assuming no disturbances or any kind of errors) using inverseDynamics
. The following for
loop takes some time to run. To accelerate, consider used generated code for inverseDynamics
. See the last section for details on how to do it.
n = size(qDesired,1); tauFeedForward = zeros(n,7); for i = 1:n tauFeedForward(i,:) = inverseDynamics(lbr, qDesired(i,:), qdotDesired(i,:), qddotDesired(i,:)); end
Gazebo provides two ROS services /gazebo/get_joint_properties
and /gazebo/apply_joint_effort
that can be used to get joint state and set joint torques. However, the services are too slow to close the torque control loop. Therefore, a customized Gazebo plug-in is used so that the joint state/torques in Gazebo can be read/written at a much faster rate through the plain ROS topics (publisher and subscriber). The customized Gazebo plug-in is already brought up together with Gazebo LBR Simulator
.
[jointTauPub, jtMsg] = rospublisher('/iiwa_matlab_plugin/iiwa_matlab_joint_effort'); jointStateSub = rossubscriber('/iiwa_matlab_plugin/iiwa_matlab_joint_state');
Use Gazebo-provided service to reset the robot to its home configuration. For details on how to work with ROS service in MATLAB, see Call and Provide ROS Services (ROS Toolbox).
mdlConfigClient = rossvcclient('gazebo/set_model_configuration');
Compose the required service message. It includes the joint names and corresponding joint positions to send to Gazebo. Call the service using this message.
msg = rosmessage(mdlConfigClient); msg.ModelName = 'mw_iiwa'; msg.UrdfParamName = 'robot_description'; msg.JointNames = {'mw_iiwa_joint_1', 'mw_iiwa_joint_2', 'mw_iiwa_joint_3',... 'mw_iiwa_joint_4', 'mw_iiwa_joint_5', 'mw_iiwa_joint_6', 'mw_iiwa_joint_7'}; msg.JointPositions = homeConfiguration(lbr); call(mdlConfigClient, msg)
ans = ROS SetModelConfigurationResponse message with properties: MessageType: 'gazebo_msgs/SetModelConfigurationResponse' Success: 1 StatusMessage: 'SetModelConfiguration: success' Use showdetails to show the contents of the message
Specify PD gains.
weights = [0.3,0.8,0.6, 0.6,0.3,0.2,0.1]; Kp = 100*weights; Kd = 2* weights; once = 1;
Prepare for data logging.
feedForwardTorque = zeros(n, 7); pdTorque = zeros(n, 7); timePoints = zeros(n,1); Q = zeros(n,7); QDesired = zeros(n,7);
Computed torque control is implemented in the for
loop below. As soon as MATLAB receives a new joint state from Gazebo, it looks up in the pre-generated tauFeedForward
and finds the feed-forward torque corresponding to the time stamp. It also computes a PD torque to compensate for the errors in joint position and velocities [1].
With default settings in Gazebo, the /iiwa_matlab_plugin/iiwa_matlab_joint_state
topic is updated at around 1 kHz (Gazebo sim time) with a typical 0.6 real time factor. And the torque control loop below can typically run at around 200 Hz (Gazebo sim time).
for i = 1:n % Get joint state from Gazebo. jsMsg = receive(jointStateSub); data = jsMsg.Data; % Parse the received message. % The Data in jsMsg is a 1-by-15 vector. % 1:7 - joint positions % 8:14 - joint velocities % 15 - time (Gazebo sim time) when the joint state is updated q = double(data(1:7))'; qdot = double(data(8:14))'; t = double(data(15)); % Set the start time. if once tStart = t; once = 0; end % Find the corresponding index h in tauFeedForward vector for joint % state time stamp t. h = ceil((t - tStart + 1e-8)/cdt); if h>n break end % Log joint positions data. Q(i,:) = q'; QDesired(i,:) = qDesired(h,:); % Inquire feed-forward torque at the time when the joint state is % updated (Gazebo sim time). tau1 = tauFeedForward(h,:); % Log feed-forward torque. feedForwardTorque(i,:) = tau1; % Compute PD compensation torque based on joint position and velocity % errors. tau2 = Kp.*(qDesired(h,:) - q) + Kd.*(qdotDesired(h,:) - qdot); % Log PD torque. pdTorque(i,:) = tau2'; % Combine the two torques. tau = tau1 + tau2; % Log the time. timePoints(i) = t-tStart; % Send torque to Gazebo. jtMsg.Data = tau; send(jointTauPub,jtMsg); end
With the joint torques sent, the LBR robot should follow the trajectory. This image shows snapshots of the robot overlaid throughout the trajectory.
Plot and inspect the actual joint torques and positions versus the desired values. Note that with the feed-forward torque, the PD torques should oscillate around zero.
exampleHelperLBRPlot(i-1, timePoints, feedForwardTorque, pdTorque, Q, QDesired )
Disconnect from the robot and shutdown the ROS network.
rosshutdown
Shutting down global node /matlab_global_node_56016 with NodeURI http://192.168.233.1:55980/
To speed up torque calculation in a loop, generate code for the inverseDynamics
function.
Create a function called invDyn
. Note exampleHelperMwIiwa14
is a codegen-compatible function that re-creates the same rigidBodyTree
object as that returned by importrobot('iiwa14.urdf')
.
function tau = invDyn( q, qdot, qddot ) %#codegen persistent robot if isempty(robot) robot = exampleHelperMwIiwa14; end tau = robot.inverseDynamics(q, qdot, qddot); end
Then use the following codegen
command
codegen invDyn.m -args {zeros(1,7), zeros(1,7), zeros(1,7)}
Finally, with the generated invDyn_mex
file, you can replace the inverseDynamics
call in the for
loop
tauFeedForward(i,:) = inverseDynamics(lbr, qDesired(i,:), qdotDesired(i,:), qddotDesired(i,:));
with
tauFeedForward(i,:) = invDyn_mex(qDesired(i,:), qdotDesired(i,:), qddotDesired(i,:));
[1] B. Sicilano, L. Sciavicco, L. Villani, G. Oriolo, "Robotics: Modelling, Planning and Control", Springer, 2009