|CATKIN_DEPENDS rospy roscpp std_msgs|
|void matrixcb(const std_msgs::Float32MultiArray::ConstPtr& msg)|
|const int dstride0 = msg->layout.dim.stride;|
|const int dstride1 = msg->layout.dim.stride;|
|const float h = msg->layout.dim.size;|
|const float w = msg->layout.dim.size;|
|// ROS_INFO_STREAM("msg = " << *msg);|
|ROS_INFO("mat(0,0) = %f", msg->data[dstride1 * 0 + 0]);|
|ROS_INFO("mat(0,1) = %f", msg->data[dstride1 * 0 + 1]);|
|ROS_INFO("mat(1,1) = %f\r\n", msg->data[dstride1 * 1 + 1]);|
|// Below are a few basic Eigen demos:|
|std::vector<float> data = msg->data;|
|Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > mat(data.data(), h, w);|
|std::cout << "I received = " << std::endl << mat << std::endl;|
|int main(int argc, char* argv)|
|ros::init(argc, argv, "matrix_receiver");|
|ros::Subscriber sub = n.subscribe("sent_matrix", 1, matrixcb);|
|static constexpr std::uint32_t WIDTH(3);|
|static constexpr std::uint32_t HEIGHT(3);|
|void generate_and_pub_matrix(ros::Publisher& pub, const std::uint8_t count)|
|Eigen::Matrix<float, HEIGHT, WIDTH, Eigen::RowMajor> mat;|
|ROS_INFO_STREAM("For loop " << +count << " generated the following matrix: " << std::endl << mat);|
|// Now we can convert to a message|
|msg.layout.dim.label = "height";|
|msg.layout.dim.size = HEIGHT;|
|msg.layout.dim.stride = HEIGHT*WIDTH;|
|msg.layout.dim.label = "width";|
|msg.layout.dim.size = WIDTH;|
|msg.layout.dim.stride = WIDTH;|
|msg.layout.data_offset = 0;|
|Eigen::Map<Eigen::VectorXf> mvec(mat.data(), mat.size());|
|Eigen::VectorXf::Map(&vec, mvec.size()) = mvec;|
|msg.data = vec;|
|int main(int argc, char* argv)|
|ros::init(argc, argv, "matrix_sender");|
|ros::Publisher pub = n.advertise<std_msgs::Float32MultiArray>("sent_matrix", 1);|
|std::uint8_t count = 0;|
|from std_msgs.msg import Float32MultiArray|
|from std_msgs.msg import MultiArrayDimension|
|import numpy as np|
|mat = np.array(msg.data)|
|mat = mat.reshape(msg.layout.dim.size, msg.layout.dim.size)|
|rospy.loginfo("Received: \n%s\n", str(mat))|
|if __name__ =="__main__":|
|pub = rospy.Publisher('sent_matrix', Float32MultiArray, queue_size=1)|
|r = rospy.Rate(0.5)|
|# let's build a 3x3 matrix:|
|width = 3|
|height = 3|
|mat = Float32MultiArray()|
|mat.layout.dim.label = "height"|
|mat.layout.dim.label = "width"|
|mat.layout.dim.size = height|
|mat.layout.dim.size = width|
|mat.layout.dim.stride = width*height|
|mat.layout.dim.stride = width|
|mat.layout.data_offset = 0|
|mat.data = *width*height|
|# let's create a subscriber to illustrate how to convert from a received|
|# Float32MultiArray to a numpy array|
|sub = rospy.Subscriber('sent_matrix', Float32MultiArray, matrix_cb, queue_size=1)|
|# save a few dimensions:|
|dstride0 = mat.layout.dim.stride|
|dstride1 = mat.layout.dim.stride|
|offset = mat.layout.data_offset|
|while not rospy.is_shutdown():|
|# create a numpy array that we will use just for logging purposes|
|tmpmat = np.zeros((height,width))|
|# for each instance of the while loop, we will generate random numbers|
|# to go into a matrix that we will publish|
|for i in range(height):|
|for j in range(width):|
|# for each (i,j) entry generate the random number|
|num = random.randrange(0,10)|
|# store the random number in the message we publish|
|mat.data[offset + i*dstride1 + j] = num|
|# also store the number in our numpy array for logging|
|tmpmat[i,j] = num|
|rospy.loginfo("I'm sending: \r\n %s\r\n", str(tmpmat))|
|<description>The matrix_demo package</description>|
@ZazAa1812 I'm not really sure how to interpret your question. The
stride field is important for defining the shape of your multi-dimensional array. Note that in the C++ example, I can use the
stride parameter of the
MultiArrayDimension to access the row and column entries of the 3x3 matrix.
Oh sorry, I was thinking that stride is just for when you interpreting image data. I got confuse in that. From what I understand, you publish a row of array (that was originally 3x3 matrix) and subscriber receive the data and reconstruct the array into matrix. Is that correct or I missing a fundamental knowledge here?
Regardless of the shape of the array, the actual data in the array is stored as a
std::vector. You use the
stride parameter of each entry in the
MultiArrayLayout.dim field to reconstruct the shape of the original array. Probably the best documentation for this is in the definition of the
MultiArrayLayout message: http://docs.ros.org/melodic/api/std_msgs/html/msg/MultiArrayLayout.html
I'll also point out that your assertion of the subscriber receiving the 1D array and reconstructing the 3x3 matrix is correct. I'm using the Eigen::Map function to do that.
I read the documentation a few times but I can't really comprehend the brief documentation. I always thought that it could send a matrix by matrix of data just like that. Need to deconstruct and reconstruct it. If it is not too troublesome for your, can you elaborate more on label, size and stride? Is height and width refers to the original rows and columns of your intended matrix (3x3) which is why you set the size into 3 for both of them? Regarding the stride, is dim.stride the total vector size of the the data and what is dim?
If I want to publish 2D array of amplitudes and time, can I just use your method or I need to include dim?
Jun 12, 2019
I have a 2D array. Thus I have two entries in
mat.layout.dim -- one defines the rows, and one defines the columns. The stride of the zeroth dimension is indeed the total number of entries, and stride of the first dimension is the number of columns. The label field is just a convenience. You also have a 2D array, and you'll need the same information (of course, you may have different labels and values for stride).
If you really only have two vectors (time and amplitude), it might be much easier to have a custom message that contains two separate arrays. E.g. a possible message definition would be:
# time: float32 time # amplitude: float32 amplitude
Then they could be accessed with
As a side note, it's generally poor form to insert images of text. Now you can't copy/paste or search. The text was originally much more friendly for readers.
Thank you so much for explaining this to me. I understand now about the concept. Sorry for the images though. Haha
Glad to help!
@jarvisschultz Thank you very much for posting this tutorial. Could you elaborate on the purpose of this line
@Mechazo11 In Python, the
mat.layout.dim field is a
List that is initialized as an empty list when we construct
mat = Float32MultiArray(). I call the
mat.layout.dim.append(MultiArrayDimension()) line twice to create a list of length 2 (because I'm creating a 2-dimensional matrix) with each entry in the list being a default-constructed
MultiArrayDimension object. Then in the chunk below, I can use the
 operator to access each of those entries and fill out appropriate details about the
@jarvisschultz Thanks for the prompt response. I was trying to replicate sending a 2D rectangular matrix which in python list in list would look like
[[Cls_id, x1,y1,x2,y2], [....], .....]. My question is can we use Multiarray to replicate this idea? I am really confused with how mat.data[offset + i + dstride1*j] is working, when I try to access element by element of each row (each row is a 1 x5 row vector), it throws an error list range out of index when I tried to access the i,j th element
I think what my comment above simplifies to what is the syntax to access (i,j)th element from both Python and C++ using MultiArray msgs
Float32MultiArray message is definitely appropriate for sending/receiving a 2D matrix like you are describing. In this message, the actual matrix data will be stored as a vector/list that has a length equal to the matrix width multiplied by the matrix height. The stride and dimensions fields contained in the
.layout.dim field describe the "shape" of the matrix even though the actual data is stored in a single-dimensional vector. The
mat.data[offset + i + dstride1*j] is accessing the entry in the vector corresponding to the
(i,j) index. This is described in the
MultiArrayLayout message comments.
So in the Python script, inside of the nested for-loop I am doing the following:
- Generating a random number to be the
(i,j)entry in a random matrix that I'll publish
- Setting the corresponding entry in the
- Setting the
tmpmatjust so I can log the matrix that I will publish with an appropriate shape.
If you were to run this node and at the same time run
rostopic echo /sent_matrix you should be able to see how the
(i,j) entries in the log messages map to the list entries in the published message.
If you are subscribing to a
Float32MultiArray, in general, I think you can either:
- Convert the vector data to some data structure representing a matrix in whatever language you are subscribing in. This is what I did in the C++ example above where I converted to an
Eigen::MatrixXf. In that file you can see I can then access data using Eigen's
- Continue storing the data in a single vector and then always access the
(i,j)entries using a calculation like
offset + i + dstride1*j
Just updated the gist with this commit to clean up a few things. Also added an example of how to convert from a ROS
Float32MultiArray to a numpy array in Python since that was missing before.
Oct 18, 2022
@jarvisschultz Thank you very much for updating the tutorial. I got your code to work with my python script to send out a python list in list to a C++ receiver which converts the data message into a usable rectangular/square matrix
Feb 27, 2023
I am trying to access members of MultiArrayDimension() within MultiArrayLayout which is within Float64MultiArray
i cannot access the members of MultiArrayDimension()
When I initiate the dim as an empty araay: msg.dim = 
Later, I append it with MulitArrayDimension : msg.dim.append(MulitArrayDimension())
Later, I access the members of it and assign values to it: msg.dim.label = ''
msg.dim.size = 36.
However, I get the error saying: The label field mult be of type str.
I dont understand it can you help me out?!
Apr 19, 2023
@jarvisschultz Hi. Thank you for your help last year in explaining how to use the Float32MultiArray to send and receive 2D matrices between Python and C++. Can you kindly show an example that sends an Eigen [m x n] 2D matrix from a C++ node?
Apr 20, 2023
@Mechazo11 Added a simple example in C++
Apr 20, 2023
@jarvisschultz thank you very much, really appreciate your prompt response. Using your example, I was able to transfer an Eigen matrix both ways between two C++ nodes.
Jun 5, 2023
Jun 5, 2023
@mhdadk that's definitely correct! Thanks for the tip!
This is in agreement with the ROS Message Description Specification documentation. Note,
data as a field type of
float32, which according to the previous link should be a
std::vector<float> in C++ and a
float in Python.
What should I do if I'm not using the stride parameters ? Is there a way to properly define it?