# Revision history [back]

Testing your node shows two issues. Firstly:

AnglePublisher.py", line 18, in timer_callback
msg.data = [(20.0,20.0,320.0.i,20.0)]
AttributeError: 'float' object has no attribute 'i'


Assuming that's a typo :) removing the .i results in your real issue:

AnglePublisher.py", line 18, in timer_callback
msg.data = [(20.0,20.0,320.0,20.0)]
File "/opt/ros/foxy/lib/python3.8/site-packages/std_msgs/msg/_float64_multi_array.py", line 156, in data
assert \
AssertionError: The 'data' field must be a set or sequence and each value of type 'float'


As seen in the documentation of Float64MultiArray linked by @Spectre, the data member is a 1-dimensional array, whereas you assign a list with a tuple to it, which is akin to a 2D array. The normal way to use a MultiArray message is to flatten your data to a single array, and then to specify the original layout in the layout member, also mentioned by @Spectre.

However this is not a hard requirement. If you only ever send 4 values, and your subscribers will always expect just that, you could just send them as a flat array and ignore the layout member on both sides:

msg.data = [20.0,20.0,320.0,20.0]


If however you really want to send 'multi arrays', as in lists of multiple tuples/nested lists, and you want to make your node generic so that the size of the tuples is not fixed, you should specify the layout. For instance in the following you actually have 3 4-tuples, but the code handles any data length and size of sub-arrays:

# A 2-dimensional piece of data
data = [(20.0,20.0,320.0,20.0), (20.0,20.0,320.0,20.0), (20.0,20.0,320.0,20.0)]
# Flatten data
msg.data = [v for nested in data for v in nested]
# Specify the layout of the 2 dimensions
dim0 = MultiArrayDimension()
dim0.label = "foo"
# First dimension size is the number of nested tuples
dim0.size = len(data)
# First stride is defined as the total data size
dim0.stride = len(msg.data)

dim1 = MultiArrayDimension()
dim1.label = "bar"
# Second dimension size is the length of individual nested tuples
dim1.size = len(data[0])
# Second (last) stride is equal to the size when there is no padding
dim1.stride = dim1.size

msg.layout.dim = [dim0, dim1]


As you see this gets relatively complex, and includes things you probably don't want to care about, like strides and padding. This is a good sign that you indeed probably should make your own custom message, that has just the fields you need, with explicit names fitting your use case (e.g. angles).