1
1
#!/usr/bin/python3
2
2
3
+ from typing import Optional
3
4
import rospy
4
5
import numpy as np
5
6
from tf2_msgs .msg import TFMessage
6
7
from geometry_msgs .msg import TransformStamped , PoseWithCovarianceStamped
7
8
from sensor_msgs .msg import Range
8
- from message_filters import ApproximateTimeSynchronizer , Subscriber
9
- from applevision_rospkg .msg import PointWithCovarianceStamped , RegionOfInterestWithCovarianceStamped
9
+ from message_filters import Subscriber
10
+ from applevision_rospkg .msg import PointWithCovarianceStamped , RegionOfInterestWithConfidenceStamped
10
11
from applevision_rospkg .srv import Tf2Transform
11
12
from applevision_kalman .filter import KalmanFilter , EnvProperties
12
- from helpers import RobustServiceProxy , ServiceProxyFailed , HeaderCalc , CameraInfoHelper
13
+ from helpers import RobustServiceProxy , ServiceProxyFailed , HeaderCalc , CameraInfoHelper , SynchronizerMinTick
13
14
14
15
15
16
def second_order_var_approx (fprime , fdoubleprime , ftrippleprime , xvar ):
16
17
# https://en.wikipedia.org/wiki/Taylor_expansions_for_the_moments_of_functions_of_random_variables
17
18
return fprime ** 2 * xvar + 1 / 2 * fdoubleprime ** 2 * xvar ** 2 + fprime * ftrippleprime * xvar ** 2
18
19
19
20
20
- class MainHandler :
21
+ class FilterHandler :
21
22
22
23
def __init__ (self , topic : str , kal : KalmanFilter ) -> None :
23
24
self .tf_get = RobustServiceProxy ('Tf2Transform' ,
@@ -35,13 +36,8 @@ def __init__(self, topic: str, kal: KalmanFilter) -> None:
35
36
self ._header = HeaderCalc ('palm' )
36
37
self ._gen = np .random .default_rng ()
37
38
38
- def callback (self , dist : Range ,
39
- cam : RegionOfInterestWithCovarianceStamped ):
40
- self .cam_info .wait_for_camera_info ()
41
- cam_info_obj = self .cam_info .get_last_camera_info ()
42
- cam_res = np .array ([cam_info_obj .width , cam_info_obj .height ])
43
- cam_focal = np .array ([cam_info_obj .P [0 ], cam_info_obj .P [5 ]])
44
-
39
+ def callback (self , dist : Optional [Range ],
40
+ cam : Optional [RegionOfInterestWithConfidenceStamped ]):
45
41
try :
46
42
dist_to_home = self .tf_get ('palm' , 'applevision_start_pos' ,
47
43
rospy .Time (), rospy .Duration ())
@@ -54,46 +50,8 @@ def callback(self, dist: Range,
54
50
trans .transform .translation .x , trans .transform .translation .y ,
55
51
trans .transform .translation .z
56
52
]))
57
- range_corrected = dist .range + self .kal .env .apple_r
58
-
59
- # publish the kalman filters predicted apple distance
60
- # if bounding box is on the edge the variance is infinite
61
- if cam .w == 0 or cam .h == 0 or cam .x == 0 or cam .x + cam .w == cam_res [
62
- 0 ]:
63
- x_est , p_est , var = self .kal .step_filter (
64
- (0 , 0 , 0 , range_corrected ), np .inf , np .inf , np .inf , control )
65
- else :
66
- # compute apple x, y based off of the bounding box width/height avg
67
- # TODO: how to fix this? it's unreliable
68
- # TODO: improve varience calculations
69
- # TODO: initialize robot in correct position
70
- # TODO: this is wrong
71
- est_z = cam_focal [0 ] * (2 * self .kal .env .apple_r ) / cam .w
72
- center_x = cam .x + cam .w / 2 - cam_res [0 ] / 2
73
- center_y = cam .y + cam .h / 2 - cam_res [1 ] / 2
74
- est_x = center_x * est_z / cam_focal [0 ]
75
- est_y = center_y * est_z / cam_focal [1 ]
76
-
77
- z_const = cam_focal [0 ] * (2 * self .kal .env .apple_r )
78
- z_fprime = z_const * - 1 / cam .w ** 2
79
- z_fdoubleprime = z_const * 2 / cam .w ** 3
80
- z_ftrippleprime = z_const * - 6 / cam .w ** 4
81
- z_var = second_order_var_approx (z_fprime , z_fdoubleprime ,
82
- z_ftrippleprime , cam .w_var )
83
-
84
- # assume x and w uncorrelated (probably not true)
85
- center_x_var = cam .x_var + 1 / 4 * cam .w_var
86
- center_y_var = cam .x_var + 1 / 4 * cam .w_var
87
- # https://stats.stackexchange.com/questions/52646/variance-of-product-of-multiple-independent-random-variables
88
- x_var = ((center_x_var + center_x ** 2 ) * (z_var + est_z ** 2 ) -
89
- (center_x * est_z )** 2 ) * 1 / cam_focal [0 ]** 2
90
- y_var = ((center_y_var + center_y ** 2 ) * (z_var + est_z ** 2 ) -
91
- (center_y * est_z )** 2 ) * 1 / cam_focal [0 ]** 2
92
-
93
- # TODO: kalman filter will runaway sometimes?
94
- x_est , p_est , var = self .kal .step_filter (
95
- (est_x , est_y , est_z , range_corrected ), x_var , y_var , z_var ,
96
- control )
53
+
54
+ x_est , p_est , var = self .kal .step_filter (cam , dist , control )
97
55
98
56
trans_out = TransformStamped ()
99
57
trans_out .header = self ._header .get_header ()
@@ -107,8 +65,8 @@ def callback(self, dist: Range,
107
65
108
66
out = PointWithCovarianceStamped ()
109
67
out .header = self ._header .get_header ()
110
- out .camera_stamp = cam .header .stamp
111
- out .distance_stamp = dist .header .stamp
68
+ out .camera_stamp = cam .header .stamp if cam else rospy . Time ()
69
+ out .distance_stamp = dist .header .stamp if dist else rospy . Time ()
112
70
out .point = x_est .tolist ()
113
71
out .covariance = p_est .flatten ().tolist ()
114
72
self .p_out .publish (out )
@@ -126,6 +84,11 @@ def main():
126
84
rospy .init_node ('applevision_filter' )
127
85
rospy .wait_for_service ('Tf2Transform' )
128
86
87
+ rospy .loginfo ('Waiting for camera info...' )
88
+ cam_info = CameraInfoHelper ('palm_camera/camera_info' )
89
+ cam_info .wait_for_camera_info ()
90
+ rospy .loginfo ('Got it! Starting...' )
91
+
129
92
# TODO: Tune these
130
93
env = EnvProperties (delta_t_ms = 33 ,
131
94
accel_std = 0.005 ,
@@ -134,17 +97,24 @@ def main():
134
97
z_std = .005 ,
135
98
backdrop_dist = .5 ,
136
99
apple_r = .040 ,
137
- dist_fov_rad = np .deg2rad (20 ))
138
- kal_filter = KalmanFilter (env , 1.5 , 0.3 , 0.75 , 0.9 )
100
+ dist_fov_rad = np .deg2rad (20 ),
101
+ camera_info = cam_info .get_last_camera_info ())
102
+ kal_filter = KalmanFilter (
103
+ env ,
104
+ std_range = 1.5 ,
105
+ too_close_cam = 0.3 ,
106
+ too_far_dist = 7 ,
107
+ appl_proportion_low = 0.75 ,
108
+ appl_proportion_high = 0.9 )
139
109
rospy .logdebug (
140
110
f'Kalman filter using environment { env } and filter { kal_filter } .' )
141
111
142
112
# input('Press any key to start Applevision...')
143
- main_proc = MainHandler ('applevision/est_apple_pos' , kal_filter )
113
+ main_proc = FilterHandler ('applevision/est_apple_pos' , kal_filter )
144
114
dist = Subscriber ('applevision/apple_dist' , Range )
145
115
camera = Subscriber ('applevision/apple_camera' ,
146
- RegionOfInterestWithCovarianceStamped )
147
- sync = ApproximateTimeSynchronizer ([dist , camera ], 10 , 0.017 )
116
+ RegionOfInterestWithConfidenceStamped )
117
+ sync = SynchronizerMinTick ([dist , camera ], queue_size = 10 , slop = 0.017 , min_tick = 0.2 )
148
118
sync .registerCallback (main_proc .callback )
149
119
150
120
rospy .spin ()
0 commit comments