Can anyone tell me the attribute to set to label the units on a LinePlot in ReportLab? Also, if you know how to set a title, that would be ultimately super helpful.
drawing = Drawing(50,50)
data = [(tuple(zip(matfile['chan'].item([6],matfile['chan'].item()[7].item()[0])))]
lp = LinePlot()
lp.data = data
lp.????? = (str(matfile['chan'].item()[3]), str(matfile['chan'].item()[2]))
drawing.add(lp)
elements.append(drawing)
This is actually going to be inside a loop - I load a .mat file and there's about 50 channels, and I am going to plot almost all of them. Separately. But first I need to get a handle on assigning the labels (title text, which will be the same as the channel, then the units for the axes...) X Axis label should always be 'Seconds', Y axis label will vary... sometimes a %, sometimes a pressure or temperature or speed, etc.
I have no idea how to do THAT, but I ended up using framing tables and I cobbled together something.I did not succeed in rotating the text for the y axis label.
for channel in channels:
drawing = Drawing(0,0)
data = [(tuple(zip(matfile[channel].item()[6],matfile[channel].item()[7].item()[0])))]
lp = LinePlot()
lp.data = data
lp.width = 6*inch
lp.height = 3.25*inch
stylesheet = getSampleStyleSheet()
y_label = Paragraph(str(matfile[channel].item()[2]), stylesheet['Normal'])
drawing.add(lp)
plot_table = [['',str(channel)],
[y_label, drawing],
['',matfile[channel].item()[3]]]
t_framing_table = Table(plot_table)
t_framing_table._argH[1] = lp.height + .5*inch
t_framing_table._argW[1] = lp.width
elements.append(t_framing_table)
if break_page:
elements.append(PageBreak())
break_page = False
else:
break_page = True
Related
What do I want to do?
I work with a Franka Emika Panda and use the "cartesian_impedance_example_controller" with its "equilibrium_pose" topic to move the panda arm.
I want to use a command to rotate the arm along its axes of the "panda_rightfinger" joint axes (axis of interactive marker seen in picture). The roation only happens around the axis and happens by pressing a specific button.
(Right finger frame with the interactive marker around it and panda_link0 frame on the left)
How do I do it?
The rotation quaternion gets created by a function that uses following script:
axis = {
"roll": 0,
"pitch": 0,
"yaw": 0
}
def pyr_producer(self, gesture_msg):
global axis
axis[gesture_msg.cls] += 1 * 0.01
return list(axis.values())
def get_quaternion(self, gesture_msg):
roll, pitch, yaw = pyr_producer(gesture_msg)
q_rot = tf.transformations.quaternion_from_euler(roll, pitch, yaw)
return Quaternion(*q_rot)
Afterwards, this rotation quaterion will be used by another script and gets published to the corresponding equilibrium_pose topic.
This part of the script calculates the rotation:
eq_pose: the new pose that will be used for the topic
current_goal_pose: the pose that contains the actual rotation
last_goal_pose: the pose that contains the last rotation
eq_pose.pose.position = last_goal_pose.pose.position
eq_pose.pose.orientation = orientation_producer.get_quaternion(goal_pose.gesture)
# calculate the relative quaternion from the last pose to the new pose
# (see http://wiki.ros.org/tf2/Tutorials/Quaternions)
# add relative rotation quaternion to the new equilibrium orientation by multiplying
q_equilibrium = [eq_pose.pose.orientation.x, eq_pose.pose.orientation.y,
eq_pose.pose.orientation.z, eq_pose.pose.orientation.w]
q_2 = [current_goal_pose.pose.orientation.x, current_goal_pose.pose.orientation.y,
current_goal_pose.pose.orientation.z, current_goal_pose.pose.orientation.w]
# Negate w value for inverse
q_1_inv = [last_goal_pose.pose.orientation.x, last_goal_pose.pose.orientation.y,
last_goal_pose.pose.orientation.z, (-1)*last_goal_pose.pose.orientation.w]
q_relative = tf.transformations.quaternion_multiply(q_2, q_1_inv)
q_equilibrium = tf.transformations.quaternion_multiply(q_relative, q_equilibrium)
eq_pose.pose.orientation.x = q_equilibrium[0]
eq_pose.pose.orientation.y = q_equilibrium[1]
eq_pose.pose.orientation.z = q_equilibrium[2]
eq_pose.pose.orientation.w = q_equilibrium[3]
# update last pose
last_goal_pose = current_goal_pose
# Only publish poses when there is an interaction
eq_publisher.publish(eq_pose)
The eq_pose gets generated by this part:
def franka_state_callback(msg):
global eq_pose
global initial_eq_pose_found
# the initial pose has to be retrieved only once
if initial_eq_pose_found:
return
initial_quaternion = \
tf.transformations.quaternion_from_matrix(
np.transpose(np.reshape(msg.O_T_EE,
(4, 4))))
initial_quaternion = initial_quaternion / np.linalg.norm(initial_quaternion)
eq_pose.pose.orientation.x = initial_quaternion[0]
eq_pose.pose.orientation.y = initial_quaternion[1]
eq_pose.pose.orientation.z = initial_quaternion[2]
eq_pose.pose.orientation.w = initial_quaternion[3]
eq_pose.pose.position.x = msg.O_T_EE[12]
eq_pose.pose.position.y = msg.O_T_EE[13]
eq_pose.pose.position.z = msg.O_T_EE[14]
initial_eq_pose_found = True
rospy.loginfo("Initial panda pose found: " + str(initial_eq_pose_found))
rospy.loginfo("Initial panda pose: " + str(eq_pose))
if __name__ == "__main__":
state_sub = rospy.Subscriber("/panda/franka_state_controller/franka_states", FrankaState, franka_state_callback)
while not initial_eq_pose_found:
rospy.sleep(1)
state_sub.unregister()
What actually happens
The rotation itself works, but only happens around the "panda_link0" axis, which is the fixed position of the panda foot. The rotation should be the same like the one around the interactive marker in the interactive marker example.
Final Question
So I want to know, how to calculate the quaternions for this rotation?
I am quite new to robotics and hope my description was clear.
Okay, I just found my mistake, as expected, it was very easy:
The multiplication of quaternions is not cummutative. With respect to that, I just had to change the calculation of the quaternion from
q_equilibrium = tf.transformations.quaternion_multiply(q_relative, q_equilibrium)
to
q_equilibrium = tf.transformations.quaternion_multiply(q_equilibrium,q_relative)
I am using ChartJsLineChart in ChartJs.Blazor nuget, and I am stuck at sending data to ChartJsLineChart.
All the examples including this one
http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4363/Adding-Charts-To-Server-Side-Blazor-Using-ChartJs-Blazor.aspx
uses TimeTuple as a way to add data to dataset
_WeightDataSet = new LineDataset<TimeTuple<int>>
{
BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.White),
BorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Red),
Label = "Weight per Day",
Fill = false,
BorderWidth = 2,
PointRadius = 2,
PointBorderWidth = 2,
SteppedLine = SteppedLine.False,
Hidden = false
};
I am wondering what would be an alternative to TimeTuple. I tried using Tuple but that resulted with a blank chart ie. no YValues where not recognized.
You can use any reference type supported by ChartJs.Blazor to add data to the chart. I identified 3 main categories of types of data that are used with line charts.
1) If you simply want to use a (double/int) value type along both x-axis and y-axis then you can use the ChartJs.Blazor.ChartJS.Common.Point data type. For e.g.
_WeightDataSet = new LineDataset<ChartJs.Blazor.ChartJS.Common.Point>
{
BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.White),
BorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Red),
Label = "Weight per Day",
Fill = false,
BorderWidth = 2,
PointRadius = 2,
PointBorderWidth = 2,
SteppedLine = SteppedLine.False,
Hidden = false
};
You might also have to configure the xAxes in the config to a LinearCartesianAxis object for this to work.
2) If you want the x-axis to have a time value whereas the y-axis to have a double value then you need to use TimeTuple as mentioned in the example quoted by you. You might also have to configure the xAxes object in the config to be a TimeAxis object.
3) If you want double (or int) value to be plotted along y-axis against string labels on the x-axis then you need to use Wrappers provided in the ChartJs.Blazor.ChartJS.Common.Wrappers namespace and configure the xAxes to a CategoryAxis object in the config. An example of this is provided in the code hosted on https://github.com/LearnC0de/BlazorAppLineChart/blob/master/BlazorAppLineChart/BlazorAppLineChart/Pages/LineChart.razor
I am trying to preform a simple contrast stretch with python skimage, on the image opened with gdal as array of type float32. I first calculate the percentile with:
p2, p98 = np.percentile(arrayF, (P1, P2))
and then try to perform the stretch with:
img_rescale = exposure.rescale_intensity(arrayF, in_range=(p2, p98))
The returned image written to .tiff with GDAL contains only 'ones' and no data.
The cause of the problem might be in data range. For this arrayF it is between 0,0352989 and 1,03559. The script works fine when stretching the array with values 0 - 255.
Here is the function:
def contrastStrecher(Raster1, p1, p2, OutDir, OutName1):
fileNameR1 = Raster1
P1 = p1
P2 =p2
outputPath = OutDir
outputName = OutName1
extension = os.path.splitext(fileNameR1)[1]
raster1 = gdal.Open(fileNameR1, GA_ReadOnly)
colsR1 = raster1.RasterXSize
rowsR1 = raster1.RasterYSize
bandsR1 = raster1.RasterCount
driverR1 = raster1.GetDriver().ShortName
geotransformR1 = raster1.GetGeoTransform()
proj1 = raster1.GetProjection()
bandF = raster1.GetRasterBand(1)
nodataF = bandF.GetNoDataValue()
newnodata = -1.
arrayF = bandF.ReadAsArray().astype("float32")
nodatamaskF = arrayF == nodataF
arrayF[nodatamaskF] = newnodata
p2, p98 = np.percentile(arrayF, (P1, P2))
img_rescale = exposure.rescale_intensity(arrayF, in_range=(p2, p98))
del arrayF
img_rescale[nodatamaskF] = newnodata
driver = gdal.GetDriverByName(driverR1)
outraster = driver.Create(outputPath + outputName + extension, colsR1, rowsR1, 1, gdal.GDT_Float32)
outraster.SetGeoTransform(geotransformR1)
outraster.SetProjection(proj1)
outband = outraster.GetRasterBand(1)
outband.WriteArray(img_rescale)
del img_rescale
outband.FlushCache()
outband.SetNoDataValue(newnodata)
del outraster, outband
I figured out that value of newnodata interferes with the calculation. Previously I assigned a velue of -9999.9 to it and the results were as described above. Now with -1. it seems that the function outputs correct results however I'm not entirely sure of that as the nodata or newnodata value should not be included in calculation.
There is a concept called percentile stretching where everything passed the bottom and top percentile designated will be mapped to 0 and 255 respectively and all pixel values in between will be stretched to improve contrast. I am not sure that is what you want but I believe that is what's done here in this example with downloadable code: https://scikit-image.org/docs/0.9.x/auto_examples/plot_equalize.html
But you many not want for some images any mapping to 0,255 so maybe use argparse() to be able to enter these as parameters or use np.amax and np.amin to designate cut-off points, try writing the images to file and build an algorithm that suits your needs.
I'm new to programming and now I want to give custom label on categoryAxies in line chart.
But that label should be regular(like 0,50,100,150).
This is my format of data.
59.0472,0.0318
69.071,0.0271
72.0913,0.0271
81.0674,0.0334
81.0734,0.0382
83.0596,0.0717
83.0894,0.0605
85.0817,0.035
85.1053,0.0287
86.0675,0.0318
87.1001,0.0287
90.6657,0.0382
I want x-axis as 0, 50 , 60, etc.
Also i have mentioned the category axis code in below
var categoryAxis = chart.categoryAxis;
categoryAxis.parseDates = false;
categoryAxis.autoGridCount =false;
categoryAxis.dashLength = 1;
categoryAxis.gridCount = 6;
categoryAxis.gridAlpha = 0.15;
categoryAxis.minorGridEnabled = true;
categoryAxis.axisColor = "#DADADA";
If you X axis should be numeric, than I would recommend you using XY chart instead of Serial, for example: http://www.amcharts.com/javascript-charts/scatter/
I'm having a difficult time understanding the paradigm of Matlab classes vs compared to c++. I wrote code the other day, and I thought it should work. It did not... until I added
<handle
after the classdef.
So I have two classes, landmarks and robot, both are called from within the simulation class. This is the main loop of obj.simulation.animate() and it works, until I try to plot two things at once.
DATA.path is a record of all the places a robot has been on the map, and it's updated every time the position is updated.
When I try to plot it, by uncommenting the two marked lines below, I get this error:
??? Error using ==> set
Invalid handle object.
Error in ==> simulation>simulation.animate at 45
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
%INITIALIZE GLOBALS
global DATA XX
XX = [obj.robot.x ; obj.robot.y];
DATA.i=1;
DATA.path = XX;
%Setup Plots
fig=figure;
xlabel('meters'), ylabel('meters')
set(fig, 'name', 'Phil''s AWESOME 80''s Robot Simulator')
xymax = obj.landmarks.mapSize*3;
xymin = -(obj.landmarks.mapSize*3);
l.lm=scatter([0],[0],'b+');
%"UNCOMMENT ME"l.pth= plot(0,0,'k.','markersize',2,'erasemode','background'); % vehicle path
axis([xymin xymax xymin xymax]);
%Simulation Loop
for n = 1:720,
%Calculate and Set Heading/Location
XX = [obj.robot.x;obj.robot.y];
store_data(XX);
if n == 120,
DATA.path
end
%Update Position
headingChange = navigate(n);
obj.robot.updatePosition(headingChange);
obj.landmarks.updatePerspective(obj.robot.heading, obj.robot.x, obj.robot.y);
%Animate
%"UNCOMMENT ME" set(l.pth, 'xdata', DATA.path(1,1:DATA.i), 'ydata', DATA.path(2,1:DATA.i));
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
rectangle('Position',[-2,-2,4,4]);
drawnow
This is the classdef for landmarks
classdef landmarks <handle
properties
fixedPositions; %# positions in a fixed coordinate system. [ x, y ]
mapSize; %Map Size. Value is side of square
x;
y;
heading;
headingChange;
end
properties (Dependent)
apparentPositions
end
methods
function obj = landmarks(mapSize, numberOfTrees)
obj.mapSize = mapSize;
obj.fixedPositions = obj.mapSize * rand([numberOfTrees, 2]) .* sign(rand([numberOfTrees, 2]) - 0.5);
end
function apparent = get.apparentPositions(obj)
currentPosition = [obj.x ; obj.y];
apparent = bsxfun(#minus,(obj.fixedPositions)',currentPosition)';
apparent = ([cosd(obj.heading) -sind(obj.heading) ; sind(obj.heading) cosd(obj.heading)] * (apparent)')';
end
function updatePerspective(obj,tempHeading,tempX,tempY)
obj.heading = tempHeading;
obj.x = tempX;
obj.y = tempY;
end
end
end
To me, this is how I understand things. I created a figure l.lm that has about 100 xy points. I can rotate this figure by using
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
When I do that, things work. When I try to plot a second group of XY points, stored in DATA.path, it craps out and I can't figure out why.
I need to plot the robots path, stored in DATA.path, AND the landmarks positions. Ideas on how to do that?
Jonas:
I'm not saying you're wrong, because I don't know the answer, but I have code from another application that plots this way without calling axes('NextPlot','add');
if dtsum==0 & ~isempty(z) % plots related to observations
set(h.xf, 'xdata', XX(4:2:end), 'ydata', XX(5:2:end))
plines= make_laser_lines (z,XX(1:3));
set(h.obs, 'xdata', plines(1,:), 'ydata', plines(2,:))
pfcov= make_feature_covariance_ellipses(XX,PX);
set(h.fcov, 'xdata', pfcov(1,:), 'ydata', pfcov(2,:))
end
drawnow
The above works on the other code, but not mine. I'll try implementing your suggestion and let you know.
When you call plot multiple times on the same figure, the previous plot is by default erased, and the handle to the previous plot points to nothing. Thus the error.
To fix this, you need to set the NextPlot property of the axes to add. You can do this by calling hold on (that's what you'd do if you were plotting from command line), or you can write
fig=figure;
%# create a set of axes where additional plots will be added on top of each other
%# without erasing
axes('NextPlot','add');
If you want, you can store the axes handle as well, and use plot(ah,x,y,...) to make sure that you plot into the right set of axes and not somewhere strange if you happen to click on a different figure window between the time the figure is opened and the plot command is issued.