Skip to content

Commit b90e0f4

Browse files
Merge pull request #181 from FS-Online/depth-cameras
Depth cameras
2 parents 777afa3 + df4809a commit b90e0f4

7 files changed

Lines changed: 121 additions & 20 deletions

File tree

docs/camera.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Cameras
2+
3+
To add a camera to your vehicle, add the following json to the "Cameras" map in your `settings.json`:
4+
5+
```
6+
"Camera1": {
7+
"CaptureSettings": [{
8+
"ImageType": 0,
9+
"Width": 785,
10+
"Height": 785,
11+
"FOV_Degrees": 90
12+
}],
13+
"X": 1.0,
14+
"Y": 0.06,
15+
"Z": -2.20,
16+
"Pitch": 0.0,
17+
"Roll": 0.0,
18+
"Yaw": 180
19+
}
20+
```
21+
22+
`Camera1` is the name of the camera. This name will be used in ros topics and in coordinate frame.
23+
24+
`X`, `Y` and `Z` are the position of the lidar relative the [vehicle pawn center](vehicle_model.md) of the car in NED frame.
25+
26+
`Roll`,`Pitch` and `Yaw` are rotations in degrees.
27+
28+
`ImageType` describes the type of camera.
29+
At this moment only rgb and depth cameras are supported.
30+
For rgb camera, set this value to 0 and for depth camera set the value to 2.
31+
32+
* Depth Cameras (aka DepthPerspective) act as follows: each pixel is given a float value in meters corresponding to the smallest distance from the camera to that point. Images published in ros are encoded in `32FC1`
33+
* RGB images are just your normal video camera. Images published in ros are encoded using `bgr8`
34+
35+
`FOV_Degrees` describes [how much the camera sees](https://en.wikipedia.org/wiki/Field_of_view).
36+
The vertical FoV will be automatically calculated using the following formula: `vertical FoV = image height / image width * horizontal FoV`.

docs/integration-handbook.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,22 @@ To ensure the simulation will perform as expected, the sensor suite has some res
8080
Here you can read the requirements and restrictions that apply to every sensor.
8181

8282
### Camera
83-
**A this moment camera's are a bit broken. You can use the camera's but the framerate and topic names might change. See #43 and #85.**
83+
**Be warned, camera framerate cannot be guaranteed, see #43.**
84+
85+
Only rgb camera's are allowed during competition ([see camera docs](camera.md)).
86+
Depth cameras are not allowed during FSOnline 2020.
8487

8588
Every vehicle can have a maximum of 2 camera sensors.
8689
These camera(s) can be placed anywhere on the vehicle that would be allowed by FSG 2020 rules.
8790
The camera body dimensions are a 4x4x4 cm cube with mounting points at any side except the front-facing side.
8891

89-
All camera sensors output uncompressed RGBA8 images at 30 FPS.
92+
All camera sensors output images at around 20 FPS.
9093
You can choose the resolution of the camera(s).
91-
In total, the camera’s can have 1232450 pixels.
94+
In total, the camera’s can have 1232450 pixels.
9295
Every dimension (width or height) must be at least 240px and no greater than 1600px.
9396
The horizontal field of view (FoV) is configurable for each camera and must be at least 30 degrees and not be greater than 90 degrees.
94-
The vertical FoV will be automatically calculated using the following formula: `vertical FoV = image height / image width * horizontal FoV`.
95-
9697
The camera's auto exposure, motion blur and gamma settings will be equal for all teams.
9798

98-
9999
### Lidar
100100
A vehicle can have between 0 and 5 lidars.
101101
The lidar(s) can be placed anywhere on the vehicle that would be allowed by FSG 2020 rules.
@@ -112,6 +112,7 @@ For each lidar, during a single rotation, the number of collected points for 1 l
112112
The number of collected points per lidar per laser per second cannot exceed 20480.
113113

114114
To ensure the simulation keeps running smoothly:
115+
115116
* Every lidar is limited to collect 10000 points per scan.
116117
* The total number of points collected per second can be no larger than 100000
117118

docs/lidar.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ This is an example lidar:
2222

2323
`Lidar1` is the name of the lidar. This value will be used in the ros topic name and coordinate frame.
2424

25-
`X`, `Y` and `Z` are the position of the lidar relative the the center of the car in NED frame.
25+
`X`, `Y` and `Z` are the position of the lidar relative the [vehicle pawn center](vehicle_model.md) of the car in NED frame.
2626

2727
`Roll`,`Pitch` and `Yaw` are rotations in degrees.
2828

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ nav:
1616
- "Vehicle model": "vehicle_model.md"
1717
- "IMU": "imu.md"
1818
- "Lidar": "lidar.md"
19+
- "Camera": "camera.md"
1920
- "GPS": "gps.md"
2021
- "GSS": "gss.md"
2122
- "#tech madness":

ros/src/fsds_ros_bridge/scripts/cameralauncher.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ def signal_handler(sig, frame):
4646
required=True, output='screen',
4747
args=args({
4848
'camera_name': cameraname,
49+
'depthcamera': camsettings["CaptureSettings"][0]["ImageType"] == 2,
4950
'framerate': CAMERA_FRAMERATE,
50-
'host_ip': AIRSIM_HOSTIP
51+
'host_ip': AIRSIM_HOSTIP,
5152
})))
5253
# launch.launch(
5354
# roslaunch.core.Node(

ros/src/fsds_ros_bridge/src/fsds_ros_bridge_camera.cpp

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
#include "vehicles/car/api/CarRpcLibClient.hpp"
99
#include "statistics.h"
1010
#include "rpc/rpc_error.h"
11-
11+
#include <cv_bridge/cv_bridge.h>
12+
#include <math.h>
1213

1314

1415

@@ -27,6 +28,7 @@ ros_bridge::Statistics fps_statistic;
2728
std::string camera_name = "";
2829
double framerate = 0.0;
2930
std::string host_ip = "localhost";
31+
bool depthcamera = false;
3032

3133
ros::Time make_ts(uint64_t unreal_ts)
3234
{
@@ -41,21 +43,26 @@ std::string logprefix() {
4143
return "[cambridge " + camera_name + "] ";
4244
}
4345

44-
void doImageUpdate(const ros::TimerEvent&)
45-
{
46-
// We are using simGetImages instead of simGetImage because the latter does not return image dimention information.
47-
std::vector<ImageRequest> req;
48-
req.push_back(ImageRequest(camera_name, ImageType::Scene, false, false));
46+
std::vector<ImageResponse> getImage(ImageRequest req) {
47+
// We are using simGetImages instead of simGetImage because the latter does not return image dimention information.
48+
std::vector<ImageRequest> reqs;
49+
reqs.push_back(req);
4950

5051
std::vector<ImageResponse> img_responses;
5152
try {
52-
img_responses = airsim_api->simGetImages(req, "FSCar");
53+
img_responses = airsim_api->simGetImages(reqs, "FSCar");
5354
} catch (rpc::rpc_error& e) {
5455
std::cout << "error" << std::endl;
5556
std::string msg = e.get_error().as<std::string>();
5657
std::cout << "Exception raised by the API while getting image:" << std::endl
5758
<< msg << std::endl;
5859
}
60+
return img_responses;
61+
}
62+
63+
void doImageUpdate(const ros::TimerEvent&)
64+
{
65+
auto img_responses = getImage(ImageRequest(camera_name, ImageType::Scene, false, false));
5966

6067
// if a render request failed for whatever reason, this img will be empty.
6168
if (img_responses.size() == 0 || img_responses[0].time_stamp == 0)
@@ -78,6 +85,60 @@ void doImageUpdate(const ros::TimerEvent&)
7885
fps_statistic.addCount();
7986
}
8087

88+
cv::Mat manual_decode_depth(const ImageResponse& img_response)
89+
{
90+
cv::Mat mat(img_response.height, img_response.width, CV_32FC1, cv::Scalar(0));
91+
int img_width = img_response.width;
92+
93+
for (int row = 0; row < img_response.height; row++)
94+
for (int col = 0; col < img_width; col++)
95+
mat.at<float>(row, col) = img_response.image_data_float[row * img_width + col];
96+
return mat;
97+
}
98+
99+
float roundUp(float numToRound, float multiple)
100+
{
101+
assert(multiple);
102+
return ((numToRound + multiple - 1) / multiple) * multiple;
103+
}
104+
105+
cv::Mat noisify_depthimage(cv::Mat in)
106+
{
107+
cv::Mat out = in.clone();
108+
109+
// Blur
110+
cv::Mat kernel = cv::Mat::ones( 7, 7, CV_32F ) / (float)(7 * 7);
111+
cv::filter2D(in, out, -1 , kernel, cv::Point( -1, -1 ), 0, cv::BORDER_DEFAULT );
112+
113+
// Decrease depth resolution
114+
for (int row = 0; row < in.rows; row++) {
115+
for (int col = 0; col < in.cols; col++) {
116+
float roundtarget = ceil(std::min(std::max(out.at<float>(row, col), 1.0f), 10.0f));
117+
out.at<float>(row, col) = roundUp(out.at<float>(row, col), roundtarget);
118+
}
119+
}
120+
121+
return out;
122+
}
123+
124+
void doDepthImageUpdate(const ros::TimerEvent&) {
125+
auto img_responses = getImage(ImageRequest(camera_name, ImageType::DepthPerspective, true, false));
126+
127+
// if a render request failed for whatever reason, this img will be empty.
128+
if (img_responses.size() == 0 || img_responses[0].time_stamp == 0)
129+
return;
130+
131+
ImageResponse img_response = img_responses[0];
132+
133+
cv::Mat depth_img = noisify_depthimage(manual_decode_depth(img_response));
134+
sensor_msgs::ImagePtr img_msg = cv_bridge::CvImage(std_msgs::Header(), "32FC1", depth_img).toImageMsg();
135+
img_msg->header.stamp = make_ts(img_response.time_stamp);
136+
img_msg->header.frame_id = "/fsds/camera/"+camera_name;
137+
138+
image_pub.publish(img_msg);
139+
fps_statistic.addCount();
140+
}
141+
81142
void printFps(const ros::TimerEvent&)
82143
{
83144
std::cout << logprefix() << "Average FPS: " << (fps_statistic.getCount()/FPS_WINDOW) << std::endl;
@@ -93,6 +154,7 @@ int main(int argc, char ** argv)
93154
nh.param<std::string>("camera_name", camera_name, "");
94155
nh.param<double>("framerate", framerate, 0.0);
95156
nh.param<std::string>("host_ip", host_ip, "localhost");
157+
nh.param<bool>("depthcamera", depthcamera, false);
96158

97159
if(camera_name == "") {
98160
std::cout << logprefix() << "camera_name unset." << std::endl;
@@ -123,7 +185,7 @@ int main(int argc, char ** argv)
123185
image_pub = nh.advertise<sensor_msgs::Image>("/fsds/camera/" + camera_name, 1);
124186

125187
// start the loop
126-
ros::Timer imageTimer = nh.createTimer(ros::Duration(1/framerate), doImageUpdate);
188+
ros::Timer imageTimer = nh.createTimer(ros::Duration(1/framerate), depthcamera ? doDepthImageUpdate : doImageUpdate);
127189
ros::Timer fpsTimer = nh.createTimer(ros::Duration(FPS_WINDOW), printFps);
128190
ros::spin();
129191
return 0;

settings.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@
6969
],
7070
"X": 1.0,
7171
"Y": 0.06,
72-
"Z": -2.20,
72+
"Z": -1.20,
7373
"Pitch": 0.0,
7474
"Roll": 0.0,
75-
"Yaw": 180
75+
"Yaw": 0
7676
},
7777
"cam2": {
7878
"CaptureSettings": [
@@ -84,8 +84,8 @@
8484
}
8585
],
8686
"X": 1.0,
87-
"Y": 0.06,
88-
"Z": -2.20,
87+
"Y": 0.56,
88+
"Z": -1.20,
8989
"Pitch": 0.0,
9090
"Roll": 0.0,
9191
"Yaw": 0.0

0 commit comments

Comments
 (0)