mlinks matlab,RTBPlot.m · octopus/robotics-toolbox-matlab - Gitee.com

%RTBPlot Plot utilities for Robotics Toolbox

% Copyright (C) 1993-2017, by Peter I. Corke

%

% This file is part of The Robotics Toolbox for MATLAB (RTB).

%

% RTB is free software: you can redistribute it and/or modify

% it under the terms of the GNU Lesser General Public License as published by

% the Free Software Foundation, either version 3 of the License, or

% (at your option) any later version.

%

% RTB is distributed in the hope that it will be useful,

% but WITHOUT ANY WARRANTY; without even the implied warranty of

% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

% GNU Lesser General Public License for more details.

%

% You should have received a copy of the GNU Leser General Public License

% along with RTB. If not, see .

%

% http://www.petercorke.com

classdef RTBPlot

methods (Static)

function th = install_teach_panel(name, robot, q, opt)

%-------------------------------

% parameters for teach panel

bgcol = [135 206 250]/255; % background color

height = 0.06; % height of slider rows

%-------------------------------

%---- install the panel at the side of the figure

% find the right figure to put it in

c = findobj(gca, 'Tag', name); % check the current axes

if isempty(c)

% doesn't exist in current axes, look wider

c = findobj(0, 'Tag', name); % check all figures

if ~isempty(c)

ax = get(c(1), 'Parent'); % get first axis holding the robot

else

error('RTB:RTBPlot:install_teach_panel', 'no window found');

end

else

% found it in current axes

ax = gca;

end

teachhandles.fig = get(ax, 'Parent'); % get the figure that holds the axis

% shrink the current axes to make room

% [l b w h]

if opt.d_2d

ax.ZColor = 'none';

ax.Color = 'none';

ax.Position = [0.22 0.05 0.8 1];

else

ax.Position = [0.3 0 0.7 1];

end

teachhandles.curax = ax;

% create the panel itself

panel = uipanel(teachhandles.fig, ...

'Title', 'Teach', ...

'BackGroundColor', bgcol,...

'Position', [0 0 0.25 1]);

panel.Units = 'pixels'; % stop automatic resizing

teachhandles.panel = panel;

set(teachhandles.fig, 'Units', 'pixels');

set(teachhandles.fig, 'ResizeFcn', @(src,event) RTBPlot.resize_callback(robot, teachhandles));

%---- get the current robot state

% if isempty(q)

% % check to see if there are any graphical robots of this name

% rhandles = findobj('Tag', robot.name);

%

% % find the graphical element of this name

% assert(~isempty(rhandles), 'RTB:teach:badarg', 'No graphical robot of this name found');

%

% % get the info from its Userdata

% info = get(rhandles(1), 'UserData');

%

% % the handle contains current joint angles (set by plot)

% if ~isempty(info.q)

% q = info.q;

% end

% else

% robot.plot(q);

% end

teachhandles.q = q;

T6 = robot.fkine(q);

if isa(T6, 'SE2')

T6 = T6.SE3;

end

T6 = T6.T;

% we need to have qlim set to finite values for a prismatic joint

if isa(robot, 'SerialLink')

qlim = robot.qlim;

assert(~any(isinf(qlim(:))), 'RTB:teach:badarg', 'Must define joint coordinate limits for prismatic axes, set qlim properties for prismatic Links');

% set up scale factor, from actual limits in radians/metres to display units

qscale = ones(robot.n,1);

for j=1:robot.n

L=robot.links(j);

if opt.deg && L.isrevolute

qscale(j) = 180/pi;

end

end

else

% for an ETS*

for i=1:robot.n

if robot(i).isprismatic

qlim(i,:) = [0 2*robot(i).param];

else

qlim(i,:) = pi*[-1 1];

end

end

% set up scale factor, from actual limits in radians/metres to display units

qscale = ones(robot.n,1);

for j=1:robot.n

if ~robot(i).isprismatic && opt.deg

qscale(j) = 180/pi;

else

qscale(j) = 1;

end

end

end

teachhandles.qscale = qscale;

teachhandles.robot = robot;

teachhandles.q = q;

teachhandles.orientation = opt.orientation;

teachhandles.opt = opt;

%---- now make the sliders

n = robot.n;

for j=1:n

% slider label

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0 height*(n-j+2) 0.15 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.5, ...

'String', sprintf('q%d', j));

% slider itself

q(j) = max( qlim(j,1), min( qlim(j,2), q(j) ) ); % clip to range

teachhandles.slider(j) = uicontrol(panel, 'Style', 'slider', ...

'Units', 'normalized', ...

'Position', [0.15 height*(n-j+2) 0.65 height], ...

'Min', qlim(j,1), ...

'Max', qlim(j,2), ...

'Value', q(j), ...

'TooltipString', sprintf('Joint %d value', j), ...

'Tag', sprintf('Slider%d', j));

% text box showing slider value, also editable

teachhandles.edit(j) = uicontrol(panel, 'Style', 'edit', ...

'Units', 'normalized', ...

'Position', [0.80 height*(n-j+2)+.01 0.20 0.9*height], ...

'BackgroundColor', bgcol, ...

'String', num2str(qscale(j)*q(j), 3), ...

'HorizontalAlignment', 'left', ...

'FontUnits', 'normalized', ...

'FontSize', 0.4, ...

'Tag', sprintf('Edit%d', j));

end

%---- set up the position display box

% X

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', 'x:');

teachhandles.t6.t(1) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'String', sprintf('%.3f', T6(1,4)), ...

'TooltipString', 'End-effector x-coordinate', ...

'Tag', 'T6');

% Y

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-2*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', 'y:');

teachhandles.t6.t(2) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-2*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', 'End-effector y-coordinate', ...

'String', sprintf('%.3f', T6(2,4)));

if ~opt.d_2d

% Z

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-3*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', 'z:');

teachhandles.t6.t(3) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-3*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', 'End-effector z-coordinate', ...

'String', sprintf('%.3f', T6(3,4)));

end

% Orientation

switch opt.orientation

case 'approach'

labels = {'ax:', 'ay:', 'az:'};

tips = {'Approach vector - x component', 'Approach vector - y component', 'Approach vector - z component'};

case 'eul'

labels = {[char(hex2dec('3c6')) ':'], [char(hex2dec('3b8')) ':'], [char(hex2dec('3c8')) ':']}; % phi theta psi

tips = {'Euler angle phi (about Z)', 'Euler angle theta (about Y)', 'Euler angle psi (about Z)'};

case {'rpy', 'rpy/xyz'}

labels = {'R:', 'P:', 'Y:'};

tips = {'Roll angle (about Z)', 'Pitch angle (about Y)', 'Yaw angle (about X)'};

case 'rpy/zyx'

labels = {'R:', 'P:', 'Y:'};

tips = {'Roll angle (about X)', 'Pitch angle (about Y)', 'Yaw angle (about Z)'};

end

%---- set up the orientation display box

if opt.d_2d

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-5*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', 'Yaw:');

teachhandles.t6.r(1) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-5*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', 'Yaw angle (about Z)', ...

'String', sprintf('%.3f', 0));

else

% AX

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-5*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', labels(1));

teachhandles.t6.r(1) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-5*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', tips{1}, ...

'String', sprintf('%.3f', 0));

% AY

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-6*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', labels(2));

teachhandles.t6.r(2) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-6*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', tips{2}, ...

'String', sprintf('%.3f', 0));

% AZ

uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'BackgroundColor', bgcol, ...

'Position', [0.05 1-7*height 0.2 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.9, ...

'HorizontalAlignment', 'left', ...

'String', labels(3));

teachhandles.t6.r(3) = uicontrol(panel, 'Style', 'text', ...

'Units', 'normalized', ...

'Position', [0.3 1-7*height 0.6 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.8, ...

'TooltipString', tips{3}, ...

'String', sprintf('%.3f', 0));

end

%---- add buttons

uicontrol(panel, 'Style', 'pushbutton', ...

'Units', 'normalized', ...

'Position', [0.80 height*(0)+.01 0.15 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.7, ...

'CallBack', @(src,event) RTBPlot.quit_callback(robot, teachhandles), ...

'BackgroundColor', 'white', ...

'ForegroundColor', 'red', ...

'TooltipString', 'Quit', ...

'String', 'X');

% the record button

teachhandles.record = [];

if isfield(opt, 'record') && ~isempty(opt.record)

uicontrol(panel, 'Style', 'pushbutton', ...

'Units', 'normalized', ...

'Position', [0.1 height*(0)+.01 0.30 height], ...

'FontUnits', 'normalized', ...

'FontSize', 0.6, ...

'CallBack', @(src,event) RTBPlot.record_callback(robot, teachhandles), ...

'BackgroundColor', 'red', ...

'ForegroundColor', 'white', ...

'String', 'REC');

end

teachhandles.callback = opt.callback;

%---- now assign the callbacks

for j=1:n

% text edit box

set(teachhandles.edit(j), ...

'Interruptible', 'off', ...

'Callback', @(src,event)RTBPlot.teach_callback(src, name, j, teachhandles));

% slider

set(teachhandles.slider(j), ...

'Interruptible', 'off', ...

'BusyAction', 'queue' );

% ask for continuous callbacks

addlistener(teachhandles.slider(j), 'ContinuousValueChange', ...

@(src,event)RTBPlot.teach_callback(src, name, j, teachhandles) );

end

% refresh the display

RTBPlot.teach_callback([], name, [], teachhandles);

if nargout > 0

th = teachhandles;

end

end

function teach_callback(src, name, j, teachhandles)

% called on changes to a slider or to the edit box showing joint coordinate

%

% src the object that caused the event

% name name of the robot

% j the joint index concerned (1..N)

% slider true if the

qscale = teachhandles.qscale;

if ~isempty(src)

switch get(src, 'Style')

case 'slider'

% slider changed, get value and reflect it to edit box

newval = get(src, 'Value');

set(teachhandles.edit(j), 'String', num2str(qscale(j)*newval, 3));

case 'edit'

% edit box changed, get value and reflect it to slider

newval = str2double(get(src, 'String')) / qscale(j);

set(teachhandles.slider(j), 'Value', newval);

end

end

%fprintf('newval %d %f\n', j, newval);

% find all graphical objects tagged with the robot name, this is the

% instances of that robot across all figures

h = findobj('Tag', name);

% find the graphical element of this name

if isempty(h)

error('RTB:teach:badarg', 'No graphical robot of this name found');

end

% get the info from its Userdata

info = get(h(1), 'UserData');

if ~isempty(j)

% update the stored joint coordinates

info.q(j) = newval;

% and save it back to the graphical object

set(h(1), 'UserData', info);

end

% update all robots of this name

animate(teachhandles.robot, info.q);

% compute the robot tool pose

T6 = teachhandles.robot.fkine(info.q);

if isa(T6, 'SE2')

T6 = T6.SE3;

end

T6 = T6.T;

% convert orientation to desired format

switch teachhandles.orientation

case 'approach'

orient = T6(:,3); % approach vector

case 'eul'

orient = tr2eul(T6, 'setopt', teachhandles.opt);

case {'rpy','rpy/xyz'}

orient = tr2rpy(T6, 'xyz', 'setopt', teachhandles.opt);

case'rpy/zyx'

orient = tr2rpy(T6, 'zyx', 'setopt', teachhandles.opt);

end

% update the display in the teach window

if teachhandles.opt.d_2d

set(teachhandles.t6.t(1), 'String', sprintf('%.3f', T6(1,4)));

set(teachhandles.t6.t(2), 'String', sprintf('%.3f', T6(2,4)));

set(teachhandles.t6.r(1), 'String', sprintf('%.3f', orient(1)));

else

for i=1:3

set(teachhandles.t6.t(i), 'String', sprintf('%.3f', T6(i,4)));

set(teachhandles.t6.r(i), 'String', sprintf('%.3f', orient(i)));

end

end

if isfield(teachhandles, 'callback') && ~isempty(teachhandles.callback)

teachhandles.callback(teachhandles.robot, info.q);

end

%notify(handles.robot, 'Moved');

end

function record_callback(robot, handles)

if ~isempty(handles.callback)

handles.record(h.q);

end

end

function quit_callback(robot, handles)

set(handles.fig, 'ResizeFcn', '');

delete(handles.panel);

set(handles.curax, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1])

end

function resize_callback(robot, handles)

% come here on figure resize events

fig = gcbo; % this figure (whose callback is executing)

fs = get(fig, 'Position'); % get size of figure

ps = get(handles.panel, 'Position'); % get position of the panel

% update dimensions of the axis area

set(handles.curax, 'Units', 'pixels', ...

'OuterPosition', [ps(3) 0 fs(3)-ps(3) fs(4)]);

% keep the panel anchored to the top left corner

set(handles.panel, 'Position', [1 fs(4)-ps(4) ps(3:4)]);

end

function cyl(ax, r, extent, color, offset, varargin)

%RTBPlot.cyl Draw a cylinder

%

% CYL(AX, R, EXTENT, COLOR, OFFSET, OPTIONS) draws a cylinder parallel to

% axis AX ('x', 'y' or 'z') of radius R between EXTENT(1) and EXTENT(2).

%

% OPTIONS are passed through to surf.

%

% See also surf, RTBPlot.box.

n = 20;

theta = (0:n)/n*2*pi;

RTBPlot.draw_shape(ax, r, extent, color, offset, theta, varargin{:});

end

function box(ax, r, extent, color, offset, varargin)

%RTBPlot.box Draw a box

%

% BPX(AX, R, EXTENT, COLOR, OFFSET, OPTIONS) draws a cylinder parallel to

% axis AX ('x', 'y' or 'z') of side length R between EXTENT(1) and EXTENT(2).

theta = [1 3 5 7 9]/4*pi;

RTBPlot.draw_shape(ax, r, extent, color, offset, theta, varargin{:});

end

function draw_shape(ax, r, extent, color, offset, theta, varargin)

% draw nothing if extent range is zero

if abs(extent(1) - extent(2)) < eps

return

end

% default value for offset

if isempty(offset)

offset = [0 0 0];

end

r = [r;r];

n = length(theta)-1;

switch ax

case 'x'

y = r * cos(theta);

z = r * sin(theta);

x = extent(:) * ones(1,n+1);

case 'y'

x = r * cos(theta);

z = r * sin(theta);

y = extent(:) * ones(1,n+1);

case 'z'

y = r * cos(theta);

x = r * sin(theta);

z = extent(:) * ones(1,n+1);

end

x = x + offset(1);

y = y + offset(2);

z = z + offset(3);

% walls of the shape

surf(x,y,z, 'FaceColor', color, 'EdgeColor', 'none', varargin{:})

% put the ends on

patch(x', y', z', color, 'EdgeColor', 'none', varargin{:});

end

function create_floor(opt)

if ~isempty(opt.floorimage)

RTBPlot.create_image_floor(opt)

else

RTBPlot.create_tiled_floor(opt)

end

end

function create_image_floor(opt)

xmin = opt.workspace(1);

xmax = opt.workspace(2);

ymin = opt.workspace(3);

ymax = opt.workspace(4);

[X,Y] = meshgrid([xmin, xmax], [ymin ymax]);

Z = opt.floorlevel*ones(2,2);

C = repmat(opt.floorimage, 1, 1, 3);

surface(X, Y, Z, C, ...

'FaceColor','texturemap',...

'EdgeColor','none',...

'SpecularStrength', 0, ...

'CDataMapping','direct');

end

% draw a tiled floor in the current axes

function create_tiled_floor(opt)

if ~opt.tiles

return

end

xmin = opt.workspace(1);

xmax = opt.workspace(2);

ymin = opt.workspace(3);

ymax = opt.workspace(4);

% create a colored tiled floor

xt = xmin:opt.tilesize:xmax;

yt = ymin:opt.tilesize:ymax;

Z = opt.floorlevel*ones( numel(yt), numel(xt));

C = zeros(size(Z));

[r,c] = ind2sub(size(C), 1:numel(C));

C = bitand(r+c,1);

C = reshape(C, size(Z));

C = cat(3, opt.tile1color(1)*C+opt.tile2color(1)*(1-C), ...

opt.tile1color(2)*C+opt.tile2color(2)*(1-C), ...

opt.tile1color(3)*C+opt.tile2color(3)*(1-C));

[X,Y] = meshgrid(xt, yt);

surface(X, Y, Z, C, ...

'FaceColor','texturemap',...

'EdgeColor','none',...

'SpecularStrength', 0, ...

'CDataMapping','direct');

end

function opt = plot_options(robot, optin)

opt.deg = false;

% timing/looping

opt.delay = 0.1;

opt.fps = [];

opt.loop = false;

opt.raise = false;

% general appearance

opt.scale = 1;

opt.zoom = 1;

opt.trail = [];

opt.workspace = [];

opt.reach = [];

opt.name = true;

opt.projection = {'ortho', 'perspective'};

opt.view = [];

opt.top = false;

% 3D rendering

opt.shading = true;

opt.lightpos = [1 1 20];

% tiled floor

opt.tiles = true;

opt.tile1color = [0.5 1 0.5]; % light green

opt.tile2color = [1 1 1]; % white

opt.floorlevel = [];

opt.tilesize = [];

opt.floorimage = [];

% shadow on the floor

opt.shadow = true;

opt.shadowcolor = [0.5 0.5 0.5];

opt.shadowwidth = 6;

% the base or pedestal

opt.base = true;

opt.basewidth = 3;

opt.basecolor = 'k';

% wrist

opt.wrist = true;

opt.wristlabel = {'xyz', 'noa'};

opt.arrow = true;

opt.wristlen = 16;

% joint rotation axes

opt.jaxes = false;

opt.jvec = false;

% joint cylinders

opt.joints = true;

opt.jointdiam = 1.5;

opt.jointlen = 3;

opt.jointcolor = [0.7 0 0];

opt.pjointcolor = [0.4 1 0.03];

% links

opt.linkcolor = 'b';

opt.toolcolor = 'r';

% misc

opt.movie = [];

% build a list of options from all sources

% 1. the M-file plotbotopt if it exists

% 2. robot.plotopt

% 3. command line arguments

options = optin;

if ~isempty(robot)

options = [robot.plotopt options];

end

if exist('plotbotopt', 'file') == 2

options = [plotbotopt options];

end

% parse the options

[opt,args] = tb_optparse(opt, options);

if ~isempty(args)

error(['unknown option: ' args{1}]);

end

if opt.top

opt.view = 'top';

end

if ~isempty(opt.projection)

opt.projection = 'ortho';

end

if ~isempty(opt.floorimage)

opt.tiles = false;

end

% figure the size of the figure

if ~isempty(opt.reach)

reach = opt.reach;

else

% reach is not specified use a simple heuristic to figure the maximum reach of the robot

assert(~isempty(robot), 'RTB:RTBPlot:plot_options', 'robot must be defined to estimate reach');

L = robot.links;

reach = 0;

for j=1:robot.n

if L(j).isrevolute

% revolute, add the link length and offset

reach = reach + abs(L(j).a) + abs(L(j).d);

else

% prismatic

if ~isempty(L(j).qlim)

% use the maximum joint value

assert(all(L(j).qlim >= 0), 'RTB:RTBPlot:plot_options', 'prismatic joint %d qlim values cannot be negative', j)

reach = reach + abs(L(j).a) + max(abs(L(j).qlim));

else

% prismatic joint has no maximum length provided

if isempty(opt.workspace)

% workspace was given so don't complain, but we still need to compute reach

% mark it as NaN and compute it later from workspace

error('RTB:RTBPlot:plot_options', 'prismatic joint %d has no qlim parameter set, set it or specify ''workspace'' plot option', j);

else

reach = NaN;

end

end

end

end

reach = reach + sum(abs(robot.tool.t)); % add the tool length

end

if isnan(reach)

% reach couldn't be estimated because a prismatic qlim was missing,

% estimate it from workspace

reach = max( colnorm( reshape(opt.workspace, [2 3]) ) ) / 2;

end

if isempty(opt.workspace)

% now create a 3D volume based on this reach

opt.workspace = [-reach reach -reach reach -reach reach];

end

if opt.tiles && isempty(opt.tilesize)

d = max(opt.workspace(2)-opt.workspace(1), opt.workspace(4)-opt.workspace(3));

ts = d / 20; % go for 20 tiles to span the distance as first guess

scale = 10^floor(log10(ts)); % figure a scale factor 1,2,5 * 10^N

ts = ts/scale;

allowed = [1 2 5 10];

k = find(ts >= allowed);

opt.tilesize = allowed(k(end)) * scale;

end

if opt.wrist

% the wrist axes add to the maximum reach, we need to scale the wrist

% length to keep within bounds

f = (1 + opt.wristlen/40);

reach = reach * f;

opt.wristlen = 40*(1-1/f);

end

reach = reach/opt.zoom;

% if we have a floor, quantize the reach to a tile size

if opt.tiles

reach = opt.tilesize * ceil(reach/opt.tilesize);

end

% figure out where the floor is

if isempty(opt.workspace)

% if a floorlevel has been given, ammend the 3D volume

if ~isempty(opt.floorlevel)

opt.workspace(5) = opt.floorlevel;

else

opt.floorlevel = -reach;

end

else

if opt.tiles

% set xy limits to be integer multiple of tilesize

opt.workspace(1:4) = opt.tilesize * round(opt.workspace(1:4)/opt.tilesize);

end

opt.floorlevel = opt.workspace(5);

end

% update the fundamental scale factor (given by the user as a multiplier) by a length derived from

% the overall workspace dimension

% we need that a lot when creating the robot model

if reach > 0

opt.scale = opt.scale * reach/40;

else

opt.scale = opt.scale / 40;

end

if ~isempty(opt.fps)

opt.delay = 1/opt.fps;

end

end

end

end

一键复制

编辑

Web IDE

原始数据

按行查看

历史

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ceph是一个开源的分布式存储系统,它提供了高性能、高可靠性和可扩展性的存储解决方案。Ceph的元数据服务器(Metadata Server,简称MDS)是Ceph文件系统(CephFS)的一部分,它负责管理文件系统的元数据信息。 要安装Ceph MDS,首先需要安装Ceph软件包。以下是在Ubuntu系统上安装Ceph MDS的步骤: 1. 添加Ceph软件包的源: ``` $ wget -q -O- 'https://download.ceph.com/keys/release.asc' | sudo apt-key add - $ echo deb https://download.ceph.com/debian-{ceph-release}/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list ``` 请将`{ceph-release}`替换为您要安装的Ceph版本,例如`nautilus`或`octopus`。 2. 更新软件包列表并安装Ceph软件包: ``` $ sudo apt update $ sudo apt install ceph-mds ``` 3. 配置Ceph MDS: 编辑`/etc/ceph/ceph.conf`文件,添加以下内容: ``` [mds] mds data = /var/lib/ceph/mds/$cluster-$id mds standby for name = <active_mds_name> ``` 将`<active_mds_name>`替换为活动MDS的名称。 4. 创建MDS数据目录: ``` $ sudo mkdir -p /var/lib/ceph/mds/ceph-<mds_id> $ sudo chown ceph:ceph /var/lib/ceph/mds/ceph-<mds_id> ``` 将`<mds_id>`替换为MDS的ID。 5. 启动Ceph MDS服务: ``` $ sudo systemctl enable ceph-mds@<mds_id>.service $ sudo systemctl start ceph-mds@<mds_id>.service ``` 将`<mds_id>`替换为MDS的ID。 完成上述步骤后,您应该成功安装和配置了Ceph MDS。您可以使用Ceph命令行工具或Ceph Dashboard来管理和监控您的Ceph集群。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值