Skip to content

Commit 5a7d1c0

Browse files
committed
Simulation Director, runs multiple sims
1 parent 5c71358 commit 5a7d1c0

File tree

4 files changed

+153
-26
lines changed

4 files changed

+153
-26
lines changed

src/FVD_Dual_HRI.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "vader_msgs/GripperCommand.h"
1010
#include "vader_msgs/CutterCommand.h"
1111

12+
#include "vader_msgs/HarvestResult.h"
13+
1214
#include "vader_msgs/PlanningRequest.h"
1315

1416
#include <ros/ros.h>
@@ -58,6 +60,8 @@ class VADERStateMachine
5860
// End effector talking to dynamixel node
5961
ros::Publisher gripperCommandPub, cutterCommandPub;
6062

63+
ros::Publisher resultStatusPub; //publishes to /harvest_result
64+
6165
/*
6266
Input: cameraFrameMsg containing the pose of the fruit in the camera frame from the coarse/fine pepper estimate
6367
Output: result containing the pose of the fruit in the world frame using TF2.
@@ -96,10 +100,10 @@ class VADERStateMachine
96100
peduncle_pose,
97101
"world",
98102
ros::Duration(3.0));
99-
ROS_INFO("Transformed pose: x=%f, y=%f, z=%f",
100-
transformed_pose.pose.position.x,
101-
transformed_pose.pose.position.y,
102-
transformed_pose.pose.position.z);
103+
// ROS_INFO("Transformed pose: x=%f, y=%f, z=%f",
104+
// transformed_pose.pose.position.x,
105+
// transformed_pose.pose.position.y,
106+
// transformed_pose.pose.position.z);
103107
result->fruit_data.pose.position.x = transformed_pose.pose.position.x;
104108
result->fruit_data.pose.position.y = transformed_pose.pose.position.y;
105109
result->fruit_data.pose.position.z = transformed_pose.pose.position.z;
@@ -149,6 +153,17 @@ class VADERStateMachine
149153
ROS_INFO_STREAM("[" << currentState << "]: " << message);
150154
}
151155

156+
void _publishResultStatus(int result_code, const std::string &reason)
157+
{
158+
vader_msgs::HarvestResult resultMsg;
159+
resultMsg.result = result_code;
160+
resultMsg.reason = reason;
161+
resultStatusPub.publish(resultMsg);
162+
ros::Duration(1.0).sleep();
163+
resultStatusPub.publish(resultMsg);
164+
ROS_WARN_STREAM("Published harvest result: " << result_code << ", reason: " << reason);
165+
}
166+
152167
public:
153168
VADERStateMachine() : currentState(State::HomeGripper)
154169
{
@@ -161,6 +176,8 @@ class VADERStateMachine
161176

162177
gripperCommandPub = n.advertise<vader_msgs::GripperCommand>("/gripper_command", 10);
163178
cutterCommandPub = n.advertise<vader_msgs::CutterCommand>("/cutter_command", 10);
179+
180+
resultStatusPub = n.advertise<vader_msgs::HarvestResult>("/harvest_status", 10);
164181
}
165182

166183
void setCoarsePoseEstimate(const vader_msgs::Pepper::ConstPtr &msg)
@@ -209,6 +226,7 @@ class VADERStateMachine
209226
}
210227
else
211228
{
229+
_publishResultStatus(srv.response.success, "Failed to home gripper.");
212230
_logWithState("Failed to call planning service for gripper homing.");
213231
currentState = State::Error;
214232
}
@@ -234,6 +252,7 @@ class VADERStateMachine
234252
}
235253
else
236254
{
255+
_publishResultStatus(srv.response.success, "Failed to home cutter.");
237256
_logWithState("Failed to call planning service for cutter homing.");
238257
currentState = State::Error;
239258
}
@@ -272,6 +291,7 @@ class VADERStateMachine
272291
}
273292
else
274293
{
294+
_publishResultStatus(srv.response.success, "Failed to move to pregrasp.");
275295
_logWithState("Failed to call planning service for parallel move to pregrasp.");
276296
currentState = State::Error;
277297
}
@@ -310,6 +330,7 @@ class VADERStateMachine
310330
}
311331
else
312332
{
333+
_publishResultStatus(srv.response.success, "Failed to gripper grasp.");
313334
_logWithState("Failed to call planning service for gripper grasp.");
314335
currentState = State::Error;
315336
}
@@ -346,6 +367,7 @@ class VADERStateMachine
346367
}
347368
else
348369
{
370+
_publishResultStatus(srv.response.success, "Failed to cutter grasp.");
349371
_logWithState("Failed to call planning service for cutter grasp.");
350372
currentState = State::Error;
351373
}
@@ -387,6 +409,7 @@ class VADERStateMachine
387409
}
388410
else
389411
{
412+
_publishResultStatus(srv.response.success, "Failed to move to storage.");
390413
_logWithState("Failed to call planning service for parallel move to storage.");
391414
currentState = State::Error;
392415
}
@@ -395,6 +418,7 @@ class VADERStateMachine
395418
case State::Done:
396419
{
397420
// ROS_INFO("Done");
421+
_publishResultStatus(vader_msgs::HarvestResult::RESULT_SUCCESS, "Success.");
398422
_logWithState("Done");
399423
break;
400424
}

src/FVD_simulation_director.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import rospy
4+
from vader_msgs.msg import HarvestResult
5+
import subprocess
6+
import threading
7+
import signal
8+
import matplotlib.pyplot as plt
9+
from collections import Counter
10+
11+
class SimulationDirector:
12+
def __init__(self):
13+
rospy.init_node('harvest_status_listener', anonymous=True)
14+
self.sub = rospy.Subscriber('/harvest_status', HarvestResult, self.callback)
15+
self.message_received = threading.Event()
16+
self.process = None
17+
self.received_msg = None
18+
self.run_count = 0
19+
self.max_runs = 2
20+
21+
self.results = [] # Will hold result codes (e.g., 0 for failure, 1 for success)
22+
self.reasons = [] # Will hold reasons for failure, etc.
23+
def callback(self, msg):
24+
self.received_msg = msg
25+
self.results.append(self.received_msg.result)
26+
self.reasons.append(self.received_msg.reason)
27+
rospy.loginfo("Message received on /harvest_status: %i, failure reason (if any): %s", self.received_msg.result, self.received_msg.reason)
28+
self.message_received.set()
29+
30+
def run(self):
31+
pepper_x = 1.0 #m
32+
pepper_y = 0.15 #m
33+
pepper_z = 0.4 #m
34+
35+
while not rospy.is_shutdown() and self.run_count < self.max_runs:
36+
self.message_received.clear()
37+
rospy.loginfo("Starting run %i", self.run_count)
38+
# self.process = subprocess.Popen(['rosrun', 'vader_hri', 'dummy_sim.py'])# 'SVD_sim_randomized_launcher.py'])
39+
40+
41+
self.process = subprocess.Popen(
42+
['rosrun', 'vader_hri', 'SVD_sim_randomized_launcher.py', '--x', str(pepper_x), '--y', str(pepper_y), '--z', str(pepper_z)],
43+
preexec_fn=os.setsid
44+
)
45+
46+
# Wait until a message is received
47+
self.message_received.wait()
48+
49+
# Print the message
50+
# rospy.loginfo("Received message: %i", self.received_msg)
51+
52+
# Send Ctrl+C (SIGINT) to subprocess
53+
rospy.loginfo("Terminating subprocess...")
54+
55+
56+
os.killpg(os.getpgid(self.process.pid), signal.SIGINT)
57+
self.process.wait() # Wait for subprocess to exit
58+
59+
self.run_count += 1
60+
self.plot_results(pepper_x, pepper_y, pepper_z)
61+
62+
def plot_results(self, x, y, z):
63+
# Bin according to success (1) / failure (0)
64+
success_count = self.results.count(1)
65+
failure_count = self.run_count - success_count
66+
# labels = ['Success', 'Failure']
67+
# counts = [success_count, failure_count]
68+
69+
print("%i out of %i runs succeeded" % (success_count, self.run_count))
70+
71+
# plt.figure()
72+
# plt.bar(labels, counts)
73+
# plt.title('Success vs Failure Count')
74+
# plt.show()
75+
76+
# Plot reasons distribution if needed
77+
title = 'Failure Reason Distribution for pepper (%.2f, %.2f, %.2f)' % (x, y, z)
78+
reason_counter = Counter(self.reasons)
79+
plt.figure()
80+
plt.bar(reason_counter.keys(), reason_counter.values())
81+
plt.title(title)
82+
plt.ylabel('Count')
83+
plt.xlabel('Reasons')
84+
plt.xticks(rotation=45)
85+
plt.show()
86+
if __name__ == '__main__':
87+
listener = SimulationDirector()
88+
listener.run()

src/SVD_sim_randomized_launcher.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from geometry_msgs.msg import Pose
88
from tf.transformations import quaternion_from_euler
99
import rospy
10+
import argparse
1011

1112
PREFIX="clear; roslaunch vader_hri vader_fvd_dualsim.launch "
1213

@@ -16,7 +17,7 @@
1617
# [0.2, -0.5, 0.5],
1718
# [0.5, -0.6, 0.4],
1819
# [0.35, -0.55, 0.65],
19-
[0.9, 0.25, 0.4]
20+
# [0.9, 0.25, 0.4]
2021
]
2122

2223
def randomize_pepper(pepper_base_pose):
@@ -119,26 +120,13 @@ def generate_and_spawn():
119120

120121

121122
if __name__ == "__main__":
122-
generate_and_spawn()
123+
parser = argparse.ArgumentParser(description='Launch simulation with pepper pose.')
124+
parser.add_argument('--x', type=float, default=0.0, help='X coordinate of pose')
125+
parser.add_argument('--y', type=float, default=0.0, help='Y coordinate of pose')
126+
parser.add_argument('--z', type=float, default=0.0, help='Z coordinate of pose')
123127

124-
# #!/usr/bin/env python3
125-
# import random
128+
args = parser.parse_args()
126129

127-
# PREFIX="clear; roslaunch vader_hri vader_fvd_dualsim.launch "
128-
129-
# def generate_poses():
130-
# # Generate random pose
131-
# pose = {
132-
# "x": round(random.uniform(0.45, 0.55), 3), #depth from arm reach
133-
# "y": round(random.uniform(0.35, 0.55), 3), #left from x-axis
134-
# "z": round(random.uniform(0.4, 0.6), 3),
135-
# "roll": round(random.uniform(-0.5, 0.5), 3),
136-
# "pitch": round(random.uniform(-0.5, 0.5), 3)
137-
# }
138-
139-
# arg_sim_pepper_pose = f"sim_pepper_pose:=\"{pose['x']} {pose['y']} {pose['z']} {pose['roll']} {pose['pitch']} 0.0\""
140-
141-
# print(PREFIX + arg_sim_pepper_pose)
142-
143-
# if __name__ == "__main__":
144-
# generate_poses()
130+
pose = [args.x, args.y, args.z]
131+
pepper_base_poses.append(pose)
132+
generate_and_spawn()

src/dummy_sim.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env python3
2+
import rospy
3+
from vader_msgs.msg import HarvestResult
4+
import time
5+
import random
6+
7+
def publisher_node():
8+
rospy.init_node('simulation_result_publisher')
9+
pub = rospy.Publisher('/harvest_status', HarvestResult, queue_size=10)
10+
11+
# Wait for 3 seconds before publishing
12+
time.sleep(.1)
13+
14+
msg = HarvestResult()
15+
msg.result = random.randint(0, 10)
16+
msg.reason = "testing" + str(msg.result)
17+
18+
rospy.loginfo("Publishing HarvestResult message with result=%d", msg.result)
19+
pub.publish(msg)
20+
21+
rospy.spin() # Keep the node alive to allow message delivery
22+
23+
if __name__ == '__main__':
24+
try:
25+
publisher_node()
26+
except rospy.ROSInterruptException:
27+
pass

0 commit comments

Comments
 (0)