Skip to content

Commit a8febab

Browse files
committed
working stereo camera
1 parent 9503ed3 commit a8febab

File tree

8 files changed

+692
-251
lines changed

8 files changed

+692
-251
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Linq;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using UnityEngine;
5+
using UnityEditor;
6+
using ZO.ROS.Publisher;
7+
8+
namespace ZO.Editor {
9+
10+
[CustomEditor(typeof(ZO.ROS.Publisher.ZOROSStereoImagePublisher))]
11+
public class ZOROSStereoImagePublisherEditor : UnityEditor.Editor {
12+
13+
/// <summary>
14+
/// Hides unused ROSTopic. See: https://answers.unity.com/questions/316286/how-to-remove-script-field-in-inspector.html
15+
/// </summary>
16+
/// <value></value>
17+
private static readonly string[] _dontIncludeMe = new string[] { "_ROSTopic", "_name" };
18+
public override void OnInspectorGUI() {
19+
20+
DrawPropertiesExcluding(serializedObject, _dontIncludeMe);
21+
serializedObject.ApplyModifiedProperties();
22+
23+
}
24+
}
25+
26+
}

Editor/CustomEditors/ZOROSStereoImagePublisherEditor.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.
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
using System.Threading.Tasks;
2+
using UnityEngine;
3+
using Newtonsoft.Json.Linq;
4+
using ZO.ROS.MessageTypes.Sensor;
5+
using ZO.Sensors;
6+
using ZO.ROS.Unity;
7+
using ZO.Document;
8+
9+
namespace ZO.ROS.Publisher {
10+
11+
/// <summary>
12+
/// Publish left & right stereo camera over two ROS Image & ImageInfo topic messages.
13+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
14+
/// To test run: `rosrun image_view image_view image:=/unity_image/image _image_transport:=raw`
15+
/// </summary>
16+
public class ZOROSStereoImagePublisher : ZOROSUnityGameObjectBase, ZOSerializationInterface {
17+
18+
public ZORGBCamera _leftCameraSensor;
19+
public ZORGBCamera _rightCameraSensor;
20+
21+
[Header("ROS Topics")]
22+
/// <summary>
23+
/// The ROS Image message topic to publish to.
24+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
25+
/// </summary>
26+
public string _leftImageROSTopic = "stereo/left/image_raw";
27+
public string _rightImageROSTopic = "stereo/right/image_raw";
28+
29+
/// <summary>
30+
/// The CameraInfo message topic to publish to.
31+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/CameraInfo.html
32+
/// </summary>
33+
public string _leftCameraInfoROSTopic = "stereo/left/camera_info";
34+
public string _rightCameraInfoROSTopic = "stereo/right/camera_info";
35+
36+
37+
/// <summary>
38+
/// The left camera sensor that we will publish.
39+
/// </summary>
40+
/// <value></value>
41+
public ZORGBCamera LeftCameraSensor {
42+
get => _leftCameraSensor;
43+
set => _leftCameraSensor = value;
44+
}
45+
46+
/// <summary>
47+
/// The right camera sensor that we will publish.
48+
/// </summary>
49+
/// <value></value>
50+
public ZORGBCamera RightCameraSensor {
51+
get => _rightCameraSensor;
52+
set => _rightCameraSensor = value;
53+
}
54+
55+
private ImageMessage _leftImageMessage = new ImageMessage();
56+
private ImageMessage _rightImageMessage = new ImageMessage();
57+
private CameraInfoMessage _leftCameraInfoMessage = new CameraInfoMessage();
58+
private CameraInfoMessage _rightCameraInfoMessage = new CameraInfoMessage();
59+
60+
/// <summary>
61+
/// The ROS Image message topic to publish to.
62+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
63+
/// </summary>
64+
/// <value></value>
65+
public string LeftImageROSTopic {
66+
get => _leftImageROSTopic;
67+
set => _leftImageROSTopic = value;
68+
}
69+
70+
/// <summary>
71+
/// The ROS Image message topic to publish to.
72+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
73+
/// </summary>
74+
/// <value></value>
75+
public string RightImageROSTopic {
76+
get => _rightImageROSTopic;
77+
set => _rightImageROSTopic = value;
78+
}
79+
80+
/// <summary>
81+
/// The CameraInfo message topic to publish to.
82+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/CameraInfo.html
83+
/// </summary>
84+
/// <value></value>
85+
public string LeftCameraInfoROSTopic {
86+
get => _leftCameraInfoROSTopic;
87+
set => _leftCameraInfoROSTopic = value;
88+
}
89+
90+
/// <summary>
91+
/// The CameraInfo message topic to publish to.
92+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/CameraInfo.html
93+
/// </summary>
94+
/// <value></value>
95+
public string RightCameraInfoROSTopic {
96+
get => _rightCameraInfoROSTopic;
97+
set => _rightCameraInfoROSTopic = value;
98+
}
99+
100+
protected override void ZOStart() {
101+
base.ZOStart();
102+
if (ZOROSBridgeConnection.Instance.IsConnected) {
103+
Initialize();
104+
}
105+
}
106+
protected override void ZOOnDestroy() {
107+
ROSBridgeConnection?.UnAdvertise(ROSTopic);
108+
}
109+
110+
private void Initialize() {
111+
// advertise
112+
ROSBridgeConnection.Advertise(LeftImageROSTopic, _leftImageMessage.MessageType);
113+
ROSBridgeConnection.Advertise(LeftCameraInfoROSTopic, _leftCameraInfoMessage.MessageType);
114+
ROSBridgeConnection.Advertise(RightImageROSTopic, _rightImageMessage.MessageType);
115+
ROSBridgeConnection.Advertise(RightCameraInfoROSTopic, _rightCameraInfoMessage.MessageType);
116+
117+
// hookup to the sensor update delegate
118+
_leftCameraSensor.OnPublishRGBImageDelegate = OnPublishLeftRGBImageDelegate;
119+
_rightCameraSensor.OnPublishRGBImageDelegate = OnPublishRightRGBImageDelegate;
120+
121+
}
122+
123+
public override void OnROSBridgeConnected(ZOROSUnityManager rosUnityManager) {
124+
Debug.Log("INFO: ZOImagePublisher::OnROSBridgeConnected");
125+
Initialize();
126+
127+
}
128+
129+
public override void OnROSBridgeDisconnected(ZOROSUnityManager rosUnityManager) {
130+
Debug.Log("INFO: ZOImagePublisher::OnROSBridgeDisconnected");
131+
ROSBridgeConnection?.UnAdvertise(LeftImageROSTopic);
132+
ROSBridgeConnection?.UnAdvertise(LeftCameraInfoROSTopic);
133+
ROSBridgeConnection?.UnAdvertise(RightImageROSTopic);
134+
ROSBridgeConnection?.UnAdvertise(RightCameraInfoROSTopic);
135+
136+
}
137+
138+
139+
140+
private int _cameraSync = 1;
141+
private bool IsCamerasSynched {
142+
get { return _cameraSync % 2 == 0; }
143+
}
144+
private void UpdateCameraSynchronization() {
145+
if (IsCamerasSynched == true) {
146+
// synchronize the headers
147+
_rightImageMessage.header = _leftImageMessage.header;
148+
_rightCameraInfoMessage.header = _leftImageMessage.header;
149+
_leftCameraInfoMessage.header = _leftImageMessage.header;
150+
151+
// send the messages
152+
ROSBridgeConnection.Publish<ImageMessage>(_leftImageMessage, LeftImageROSTopic, LeftCameraSensor.Name);
153+
ROSBridgeConnection.Publish<CameraInfoMessage>(_leftCameraInfoMessage, LeftCameraInfoROSTopic, LeftCameraSensor.Name);
154+
ROSBridgeConnection.Publish<ImageMessage>(_rightImageMessage, RightImageROSTopic, RightCameraSensor.Name);
155+
ROSBridgeConnection.Publish<CameraInfoMessage>(_rightCameraInfoMessage, RightCameraInfoROSTopic, RightCameraSensor.Name);
156+
}
157+
_cameraSync++;
158+
}
159+
160+
161+
162+
/// <summary>
163+
/// Publishes raw camera RBG8 data as a ROS Image message.
164+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
165+
/// </summary>
166+
/// <param name="rgbCamera">The camera component</param>
167+
/// <param name="cameraId">Camera ID</param>
168+
/// <param name="width">Frame width</param>
169+
/// <param name="height">Frame height</param>
170+
/// <param name="rgbData">Raw RBG8 data </param>
171+
/// <returns></returns>
172+
private Task OnPublishLeftRGBImageDelegate(ZORGBCamera rgbCamera, string cameraId, int width, int height, byte[] rgbData) {
173+
174+
// figure out the transforms
175+
ZOROSTransformPublisher transformPublisher = GetComponent<ZOROSTransformPublisher>();
176+
if (transformPublisher != null) {
177+
_leftImageMessage.header.frame_id = transformPublisher.ChildFrameID;
178+
_leftCameraInfoMessage.header.frame_id = transformPublisher.ChildFrameID;
179+
} else {
180+
_leftImageMessage.header.frame_id = Name;
181+
_leftCameraInfoMessage.header.frame_id = Name;
182+
}
183+
184+
// setup and send Image message
185+
_leftImageMessage.header.Update();
186+
_leftImageMessage.height = (uint)height;
187+
_leftImageMessage.width = (uint)width;
188+
_leftImageMessage.encoding = "rgb8";
189+
_leftImageMessage.is_bigendian = 0;
190+
_leftImageMessage.step = 1 * 3 * (uint)width;
191+
_leftImageMessage.data = rgbData;
192+
// ROSBridgeConnection.Publish<ImageMessage>(_rosImageMessage, _imageROSTopic, Name);
193+
194+
// setup and send CameraInfo message
195+
_leftCameraInfoMessage.Update();
196+
_leftCameraInfoMessage.header = _leftImageMessage.header;
197+
// initialize the camera info
198+
if (LeftCameraSensor.UnityCamera.usePhysicalProperties == true) {
199+
_leftCameraInfoMessage.BuildCameraInfo((uint)LeftCameraSensor.Width, (uint)LeftCameraSensor.Height,
200+
(double)LeftCameraSensor.FocalLengthMM,
201+
(double)LeftCameraSensor.SensorSizeMM.x, (double)LeftCameraSensor.SensorSizeMM.y);
202+
} else {
203+
_leftCameraInfoMessage.BuildCameraInfo((uint)LeftCameraSensor.Width, (uint)LeftCameraSensor.Height, (double)LeftCameraSensor.FieldOfViewDegrees);
204+
}
205+
206+
// ROSBridgeConnection.Publish<CameraInfoMessage>(_rosCameraInfoMessage, _cameraInfoROSTopic, cameraId);
207+
UpdateCameraSynchronization();
208+
209+
return Task.CompletedTask;
210+
}
211+
212+
/// <summary>
213+
/// Publishes raw camera RBG8 data as a ROS Image message.
214+
/// See: http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html
215+
/// </summary>
216+
/// <param name="rgbCamera">The camera component</param>
217+
/// <param name="cameraId">Camera ID</param>
218+
/// <param name="width">Frame width</param>
219+
/// <param name="height">Frame height</param>
220+
/// <param name="rgbData">Raw RBG8 data </param>
221+
/// <returns></returns>
222+
private Task OnPublishRightRGBImageDelegate(ZORGBCamera rgbCamera, string cameraId, int width, int height, byte[] rgbData) {
223+
224+
// figure out the transforms
225+
ZOROSTransformPublisher transformPublisher = GetComponent<ZOROSTransformPublisher>();
226+
if (transformPublisher != null) {
227+
_rightImageMessage.header.frame_id = transformPublisher.ChildFrameID;
228+
_rightCameraInfoMessage.header.frame_id = transformPublisher.ChildFrameID;
229+
} else {
230+
_rightImageMessage.header.frame_id = Name;
231+
_rightCameraInfoMessage.header.frame_id = Name;
232+
}
233+
234+
// setup and send Image message
235+
_rightImageMessage.header.Update();
236+
_rightImageMessage.height = (uint)height;
237+
_rightImageMessage.width = (uint)width;
238+
_rightImageMessage.encoding = "rgb8";
239+
_rightImageMessage.is_bigendian = 0;
240+
_rightImageMessage.step = 1 * 3 * (uint)width;
241+
_rightImageMessage.data = rgbData;
242+
// ROSBridgeConnection.Publish<ImageMessage>(_rosImageMessage, _imageROSTopic, Name);
243+
244+
// setup and send CameraInfo message
245+
_rightCameraInfoMessage.Update();
246+
_rightCameraInfoMessage.header = _rightImageMessage.header;
247+
// initialize the camera info
248+
if (RightCameraSensor.UnityCamera.usePhysicalProperties == true) {
249+
_rightCameraInfoMessage.BuildCameraInfo((uint)RightCameraSensor.Width, (uint)RightCameraSensor.Height,
250+
(double)RightCameraSensor.FocalLengthMM,
251+
(double)RightCameraSensor.SensorSizeMM.x, (double)RightCameraSensor.SensorSizeMM.y);
252+
} else {
253+
_rightCameraInfoMessage.BuildCameraInfo((uint)RightCameraSensor.Width, (uint)RightCameraSensor.Height, (double)RightCameraSensor.FieldOfViewDegrees);
254+
}
255+
256+
// ROSBridgeConnection.Publish<CameraInfoMessage>(_rightCameraInfoMessage, _cameraInfoROSTopic, cameraId);
257+
UpdateCameraSynchronization();
258+
259+
return Task.CompletedTask;
260+
}
261+
262+
#region ZOSerializationInterface
263+
public override string Type {
264+
get { return "ros.publisher.image"; }
265+
}
266+
267+
268+
public override JObject Serialize(ZOSimDocumentRoot documentRoot, UnityEngine.Object parent = null) {
269+
// TODO: needs to be finished for this
270+
JObject json = new JObject(
271+
new JProperty("name", Name),
272+
new JProperty("type", Type),
273+
new JProperty("ros_topic", ROSTopic),
274+
new JProperty("update_rate_hz", UpdateRateHz),
275+
new JProperty("left_camera_name", LeftCameraSensor.Name),
276+
new JProperty("right_camera_name", LeftCameraSensor.Name)
277+
);
278+
JSON = json;
279+
return json;
280+
}
281+
282+
public override void Deserialize(ZOSimDocumentRoot documentRoot, JObject json) {
283+
_json = json;
284+
Name = json["name"].Value<string>();
285+
ROSTopic = json["ros_topic"].Value<string>();
286+
UpdateRateHz = json["update_rate_hz"].Value<float>();
287+
288+
// find connected camera. needs to be done post load hence the Lamda
289+
documentRoot.OnPostDeserializationNotification((docRoot) => {
290+
if (JSON.ContainsKey("left_camera_name")) {
291+
ZORGBCamera[] rgbCameras = docRoot.gameObject.GetComponentsInChildren<ZORGBCamera>();
292+
foreach (ZORGBCamera camera in rgbCameras) {
293+
if (camera.Name == JSON["left_camera_name"].Value<string>()) {
294+
LeftCameraSensor = camera;
295+
}
296+
if (camera.Name == JSON["right_camera_name"].Value<string>()) {
297+
RightCameraSensor = camera;
298+
}
299+
300+
}
301+
}
302+
});
303+
304+
}
305+
306+
#endregion // ZOSerializationInterface
307+
}
308+
309+
}

Runtime/Scripts/ROS/Unity/Publishers/ZOROSStereoImagePublisher.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)