Skip to content

Commit 792a784

Browse files
committed
working 3d lidar publisher
1 parent db281d8 commit 792a784

File tree

9 files changed

+1408
-71
lines changed

9 files changed

+1408
-71
lines changed

Runtime/Scripts/ROS/MessageTypes/Sensor/ZOROSSensorMessages.cs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,237 @@ public LaserScanMessage(HeaderMessage header, float angle_min, float angle_max,
144144
}
145145
}
146146

147+
148+
/// <summary>
149+
/// This message holds the description of one point entry in the
150+
/// PointCloud2 message format.
151+
/// uint8 INT8 = 1
152+
/// uint8 UINT8 = 2
153+
/// uint8 INT16 = 3
154+
/// uint8 UINT16 = 4
155+
/// uint8 INT32 = 5
156+
/// uint8 UINT32 = 6
157+
/// uint8 FLOAT32 = 7
158+
/// uint8 FLOAT64 = 8
159+
/// </summary>
160+
public class PointFieldMessage : ZOROSMessageInterface {
161+
162+
[Newtonsoft.Json.JsonIgnore]
163+
public string MessageType { get { return PointFieldMessage.Type; } }
164+
165+
[Newtonsoft.Json.JsonIgnore]
166+
public static string Type = "sensor_msgs/PointField";
167+
168+
169+
/// <summary>
170+
/// name of field.
171+
/// </summary>
172+
/// <value></value>
173+
public string name { get; set; }
174+
175+
/// <summary>
176+
/// Offset from start of point struct
177+
/// </summary>
178+
/// <value></value>
179+
public uint offset { get; set; }
180+
181+
/// <summary>
182+
/// Datatype enumeration.
183+
/// uint8 INT8 = 1
184+
/// uint8 UINT8 = 2
185+
/// uint8 INT16 = 3
186+
/// uint8 UINT16 = 4
187+
/// uint8 INT32 = 5
188+
/// uint8 UINT32 = 6
189+
/// uint8 FLOAT32 = 7
190+
/// uint8 FLOAT64 = 8
191+
/// </summary>
192+
/// <value></value>
193+
public byte datatype { get; set; }
194+
195+
/// <summary>
196+
/// How many elements in the field
197+
/// </summary>
198+
/// <value></value>
199+
public uint count { get; set; }
200+
201+
public PointFieldMessage() {
202+
this.name = "";
203+
this.offset = 0;
204+
this.datatype = 1;
205+
this.count = 0;
206+
}
207+
208+
public PointFieldMessage(string name, uint offset, byte datatype, uint count) {
209+
this.name = name;
210+
this.offset = offset;
211+
this.datatype = datatype;
212+
this.count = count;
213+
}
214+
215+
public static PointFieldMessage[] CreateXYZPointFieldArray(uint count) {
216+
PointFieldMessage[] pointFieldMessages = new PointFieldMessage[3];
217+
218+
// x
219+
pointFieldMessages[0] = new PointFieldMessage();
220+
pointFieldMessages[0].name = "x";
221+
pointFieldMessages[0].offset = 0;
222+
pointFieldMessages[0].datatype = 7; // float32
223+
pointFieldMessages[0].count = count;
224+
225+
// y
226+
pointFieldMessages[1] = new PointFieldMessage();
227+
pointFieldMessages[1].name = "y";
228+
pointFieldMessages[1].offset = 4;
229+
pointFieldMessages[1].datatype = 7; // float32
230+
pointFieldMessages[1].count = count;
231+
232+
// z
233+
pointFieldMessages[2] = new PointFieldMessage();
234+
pointFieldMessages[2].name = "z";
235+
pointFieldMessages[2].offset = 8;
236+
pointFieldMessages[2].datatype = 7; // float32
237+
pointFieldMessages[2].count = count;
238+
239+
return pointFieldMessages;
240+
241+
}
242+
243+
}
244+
245+
/// <summary>
246+
/// This message holds a collection of N-dimensional points, which may
247+
/// contain additional information such as normals, intensity, etc. The
248+
/// point data is stored as a binary blob, its layout described by the
249+
/// contents of the "fields" array.
250+
///
251+
/// The point cloud data may be organized 2d (image-like) or 1d
252+
/// (unordered). Point clouds organized as 2d images may be produced by
253+
/// camera depth sensors such as stereo or time-of-flight.
254+
/// </summary>
255+
public class PointCloud2Message : ZOROSMessageInterface {
256+
257+
[Newtonsoft.Json.JsonIgnore]
258+
public string MessageType { get { return PointCloud2Message.Type; } }
259+
260+
[Newtonsoft.Json.JsonIgnore]
261+
public static string Type = "sensor_msgs/PointCloud2";
262+
263+
264+
/// <summary>
265+
/// Time of sensor data acquisition, and the coordinate frame ID (for 3d points).
266+
/// </summary>
267+
/// <value></value>
268+
public HeaderMessage header { get; set; }
269+
270+
271+
/// <summary>
272+
/// 2D structure of the point cloud. If the cloud is unordered, height is
273+
/// 1 and width is the length of the point cloud.
274+
/// </summary>
275+
/// <value></value>
276+
public uint height { get; set; }
277+
public uint width { get; set; }
278+
279+
/// <summary>
280+
/// Describes the channels and their layout in the binary data blob.
281+
/// </summary>
282+
/// <value></value>
283+
public PointFieldMessage[] fields { get; set; }
284+
285+
/// <summary>
286+
/// Is this data bigendian?
287+
/// </summary>
288+
/// <value></value>
289+
public bool is_bigendian { get; set; }
290+
291+
/// <summary>
292+
/// Length of a point in bytes
293+
/// </summary>
294+
/// <value></value>
295+
public uint point_step { get; set; }
296+
297+
/// <summary>
298+
/// Length of a row in bytes
299+
/// </summary>
300+
/// <value></value>
301+
public uint row_step { get; set; }
302+
303+
/// <summary>
304+
/// Actual point data, size is (row_step*height)
305+
/// </summary>
306+
/// <value></value>
307+
public byte[] data { get; set; }
308+
309+
/// <summary>
310+
/// True if there are no invalid points
311+
/// </summary>
312+
/// <value></value>
313+
public bool is_dense { get; set; }
314+
315+
public PointCloud2Message() {
316+
this.header = new HeaderMessage();
317+
this.height = 0;
318+
this.width = 0;
319+
this.fields = new PointFieldMessage[0];
320+
this.is_bigendian = false;
321+
this.point_step = 0;
322+
this.row_step = 0;
323+
this.data = new byte[0];
324+
this.is_dense = true;
325+
326+
}
327+
328+
public PointCloud2Message(HeaderMessage header, uint height, uint width, PointFieldMessage[] pointFieldMessages, bool is_bigendian, uint point_step, uint row_step, byte[] data, bool is_dense) {
329+
this.header = header;
330+
this.height = height;
331+
this.width = width;
332+
this.fields = pointFieldMessages;
333+
this.is_bigendian = is_bigendian;
334+
this.point_step = point_step;
335+
this.row_step = row_step;
336+
this.data = data;
337+
this.is_dense = is_dense;
338+
339+
}
340+
341+
public void Update() {
342+
this.header.Update();
343+
}
344+
345+
public static PointCloud2Message CreateXYZPointCloud(UnityEngine.Vector3[] pointsInUnityCoordinates) {
346+
byte[] byteData = new byte[sizeof(float) * 3 * pointsInUnityCoordinates.Length];
347+
348+
// public void FromUnityVector3(UnityEngine.Vector3 v) {
349+
// this.x = (double)v.z;
350+
// this.y = -(double)v.x;
351+
// this.z = (double)v.y;
352+
// }
353+
354+
int byteDataOffset = 0;
355+
foreach (UnityEngine.Vector3 v in pointsInUnityCoordinates) {
356+
Buffer.BlockCopy(BitConverter.GetBytes(v.z), 0, byteData, byteDataOffset + (0 * sizeof(float)), sizeof(float));
357+
Buffer.BlockCopy(BitConverter.GetBytes(-v.x), 0, byteData, byteDataOffset + (1 * sizeof(float)), sizeof(float));
358+
Buffer.BlockCopy(BitConverter.GetBytes(v.y), 0, byteData, byteDataOffset + (2 * sizeof(float)), sizeof(float));
359+
byteDataOffset += (3 * sizeof(float));
360+
}
361+
362+
PointCloud2Message pointCloud2Message = new PointCloud2Message(new HeaderMessage(),
363+
height: 1,
364+
width: (uint)pointsInUnityCoordinates.Length,
365+
pointFieldMessages: PointFieldMessage.CreateXYZPointFieldArray((uint)pointsInUnityCoordinates.Length),
366+
is_bigendian: false,
367+
point_step: sizeof(float) * 3,
368+
row_step: sizeof(float) * 12 * (uint)pointsInUnityCoordinates.Length,
369+
byteData,
370+
false);
371+
372+
return pointCloud2Message;
373+
}
374+
375+
376+
}
377+
147378
/// <summary>
148379
/// This is a message that holds data to describe the state of a set of torque controlled joints.
149380
///
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using System.Threading.Tasks;
2+
using UnityEngine;
3+
using Unity.Collections;
4+
using Newtonsoft.Json.Linq;
5+
using ZO.ROS.MessageTypes.Sensor;
6+
using ZO.ROS.MessageTypes.Geometry;
7+
using ZO.ROS.Publisher;
8+
using ZO.Sensors;
9+
using ZO.ROS.Unity;
10+
using ZO.Document;
11+
12+
13+
namespace ZO.ROS.Publisher {
14+
15+
/// <summary>
16+
/// Publish ROS /sensor/LaserScan message using the ZOLIDAR2D sensor.
17+
/// See: http://docs.ros.org/en/noetic/api/sensor_msgs/html/msg/LaserScan.html
18+
/// </summary>
19+
[RequireComponent(typeof(ZOROSTransformPublisher))]
20+
[RequireComponent(typeof(ZOLIDAR3D))]
21+
public class ZOROSPointCloud2Publisher : ZOROSUnityGameObjectBase {
22+
23+
public ZOLIDAR3D _lidar3DSensor;
24+
25+
/// <summary>
26+
/// The 2D LIDAR sensor to publish it's scan data.
27+
/// </summary>
28+
/// <value></value>
29+
public ZOLIDAR3D LIDAR3DSensor {
30+
get => _lidar3DSensor;
31+
set => _lidar3DSensor = value;
32+
}
33+
34+
public string _parentTransformId;
35+
36+
37+
private ZOROSTransformPublisher _transformPublisher = null;
38+
public ZOROSTransformPublisher TransformPublisher {
39+
get {
40+
if (_transformPublisher == null) {
41+
_transformPublisher = GetComponent<ZOROSTransformPublisher>();
42+
}
43+
44+
if (_transformPublisher == null) {
45+
Debug.LogError("ERROR: ZOROSLaserScanPublisher is missing corresponding ZOROSTransformPublisher");
46+
}
47+
return _transformPublisher;
48+
}
49+
}
50+
51+
private PointCloud2Message _pointCloud2Message = new PointCloud2Message();
52+
53+
54+
protected override void ZOStart() {
55+
base.ZOStart();
56+
if (ZOROSBridgeConnection.Instance.IsConnected) {
57+
Initialize();
58+
}
59+
}
60+
61+
protected override void ZOOnValidate() {
62+
base.ZOOnValidate();
63+
if (LIDAR3DSensor == null) {
64+
LIDAR3DSensor = GetComponent<ZOLIDAR3D>();
65+
}
66+
67+
if (ROSTopic == "") {
68+
ROSTopic = "point_cloud";
69+
}
70+
71+
if (UpdateRateHz == 0) {
72+
UpdateRateHz = 10;
73+
}
74+
75+
if (_parentTransformId == "" || _parentTransformId == null) {
76+
if (transform.parent != null) {
77+
ZOROSTransformPublisher parentTransformPublisher = transform.parent.GetComponent<ZOROSTransformPublisher>();
78+
if (parentTransformPublisher != null) {
79+
_parentTransformId = parentTransformPublisher.ChildFrameID;
80+
} else {
81+
_parentTransformId = ZOROSUnityManager.Instance.WorldFrameId;
82+
}
83+
84+
} else {
85+
_parentTransformId = ZOROSUnityManager.Instance.WorldFrameId;
86+
}
87+
}
88+
}
89+
90+
private void Initialize() {
91+
// advertise
92+
ROSBridgeConnection.Advertise(ROSTopic, _pointCloud2Message.MessageType);
93+
94+
// hookup to the sensor update delegate
95+
_lidar3DSensor.OnPublishDelegate = OnPublishLidarScanDelegate;
96+
97+
}
98+
99+
100+
protected override void ZOOnDestroy() {
101+
ROSBridgeConnection?.UnAdvertise(ROSTopic);
102+
}
103+
104+
public override void OnROSBridgeConnected(ZOROSUnityManager rosUnityManager) {
105+
Debug.Log("INFO: ZOROSLaserScanPublisher::OnROSBridgeConnected");
106+
Initialize();
107+
}
108+
109+
public override void OnROSBridgeDisconnected(ZOROSUnityManager rosUnityManager) {
110+
Debug.Log("INFO: ZOROSLaserScanPublisher::OnROSBridgeDisconnected");
111+
ROSBridgeConnection.UnAdvertise(ROSTopic);
112+
}
113+
114+
private Task OnPublishLidarScanDelegate(ZOLIDAR3D lidar, string name, NativeArray<Vector3> positions, NativeArray<Vector3> normals) {
115+
_pointCloud2Message = PointCloud2Message.CreateXYZPointCloud(positions.ToArray());
116+
_pointCloud2Message.header.Update();
117+
_pointCloud2Message.header.frame_id = _parentTransformId;
118+
119+
120+
ROSBridgeConnection.Publish(_pointCloud2Message, ROSTopic, Name);
121+
122+
return Task.CompletedTask;
123+
}
124+
125+
#region ZOSerializationInterface
126+
public override string Type {
127+
get { return "ros.publisher.point_cloud2"; }
128+
}
129+
130+
131+
132+
#endregion // ZOSerializationInterface
133+
134+
}
135+
136+
}

Runtime/Scripts/ROS/Unity/Publishers/ZOROSPointCloud2Publisher.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)