Skip to content

Commit e562707

Browse files
authored
Merge pull request #6 from BCLab-UNM/virtual_fence
Initial commit of the virtual fence
2 parents 4496be5 + f83f8b5 commit e562707

6 files changed

Lines changed: 462 additions & 17 deletions

File tree

src/behaviours/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ add_executable(
2929
src/ROSAdapter.cpp
3030
src/PID.cpp
3131
src/DriveController.cpp
32+
src/RangeController.cpp
3233
src/LogicController.cpp
3334
)
3435

src/behaviours/src/LogicController.cpp

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "LogicController.h"
22

33
LogicController::LogicController() {
4+
45
logicState = LOGIC_STATE_INTERRUPT;
56
processState = PROCCESS_STATE_SEARCHING;
67

@@ -32,10 +33,11 @@ Result LogicController::DoWork() {
3233
//most important. A priority of less than 0 is an ignored controller use -1 for standards sake.
3334
//if any controller needs and interrupt the logic state is changed to interrupt
3435
for(PrioritizedController cntrlr : prioritizedControllers) {
35-
if(cntrlr.controller->ShouldInterrupt() && cntrlr.priority >= 0) {
36-
logicState = LOGIC_STATE_INTERRUPT;
37-
//do not break all shouldInterupts may need calling in order to properly pre-proccess data.
38-
}
36+
if(cntrlr.controller->ShouldInterrupt() && cntrlr.priority >= 0)
37+
{
38+
logicState = LOGIC_STATE_INTERRUPT;
39+
//do not break all shouldInterupts may need calling in order to properly pre-proccess data.
40+
}
3941
}
4042

4143
//logic state switch
@@ -186,8 +188,9 @@ void LogicController::ProcessData() {
186188
if (processState == PROCCESS_STATE_SEARCHING) {
187189
prioritizedControllers = {
188190
PrioritizedController{0, (Controller*)(&searchController)},
189-
PrioritizedController{1, (Controller*)(&obstacleController)},
190-
PrioritizedController{2, (Controller*)(&pickUpController)},
191+
PrioritizedController{10, (Controller*)(&obstacleController)},
192+
PrioritizedController{15, (Controller*)(&pickUpController)},
193+
PrioritizedController{5, (Controller*)(&range_controller)},
191194
PrioritizedController{-1, (Controller*)(&dropOffController)}
192195
};
193196
}
@@ -196,8 +199,9 @@ void LogicController::ProcessData() {
196199
else if (processState == PROCCESS_STATE_TARGET_PICKEDUP) {
197200
prioritizedControllers = {
198201
PrioritizedController{-1, (Controller*)(&searchController)},
199-
PrioritizedController{2, (Controller*)(&obstacleController)},
202+
PrioritizedController{15, (Controller*)(&obstacleController)},
200203
PrioritizedController{-1, (Controller*)(&pickUpController)},
204+
PrioritizedController{10, (Controller*)(&range_controller)},
201205
PrioritizedController{1, (Controller*)(&dropOffController)}
202206
};
203207
}
@@ -242,16 +246,18 @@ void LogicController::controllerInterconnect() {
242246
}
243247
}
244248

245-
249+
// Recieves position in the world inertial frame (should rename to SetOdomPositionData)
246250
void LogicController::SetPositionData(Point currentLocation) {
247251
searchController.SetCurrentLocation(currentLocation);
248252
dropOffController.SetCurrentLocation(currentLocation);
249253
obstacleController.SetCurrentLocation(currentLocation);
250254
driveController.SetCurrentLocation(currentLocation);
251255
}
252256

253-
void LogicController::SetMapPositionData(Point currentLocationMap) {
254-
257+
// Recieves position in the world frame with global data (GPS)
258+
void LogicController::SetMapPositionData(Point currentLocation) {
259+
range_controller.setCurrentLocation(currentLocation);
260+
255261
}
256262

257263
void LogicController::SetVelocityData(float linearVelocity, float angularVelocity) {
@@ -273,11 +279,23 @@ void LogicController::SetSonarData(float left, float center, float right) {
273279
obstacleController.SetSonarData(left,center,right);
274280
}
275281

282+
// Called once by RosAdapter in guarded init
276283
void LogicController::SetCenterLocationOdom(Point centerLocationOdom) {
277284
searchController.SetCenterLocation(centerLocationOdom);
278285
dropOffController.SetCenterLocation(centerLocationOdom);
279286
}
280287

288+
void LogicController::setVirtualFenceOn( RangeShape* range )
289+
{
290+
range_controller.setRangeShape(range);
291+
range_controller.setEnabled(true);
292+
}
293+
294+
void LogicController::setVirtualFenceOff()
295+
{
296+
range_controller.setEnabled(false);
297+
}
298+
281299
void LogicController::SetCenterLocationMap(Point centerLocationMap) {
282300

283301
}

src/behaviours/src/LogicController.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "SearchController.h"
88
#include "ObstacleController.h"
99
#include "DriveController.h"
10+
#include "RangeController.h"
1011

1112
#include <vector>
1213
#include <queue>
@@ -47,6 +48,12 @@ class LogicController : virtual Controller
4748

4849
void SetCurrentTimeInMilliSecs( long int time );
4950

51+
// Tell the logic controller whether rovers should automatically
52+
// resstrict their foraging range. If so provide the shape of the
53+
// allowed range.
54+
void setVirtualFenceOn( RangeShape* range );
55+
void setVirtualFenceOff( );
56+
5057
protected:
5158
void ProcessData();
5259

@@ -73,6 +80,7 @@ class LogicController : virtual Controller
7380
SearchController searchController;
7481
ObstacleController obstacleController;
7582
DriveController driveController;
83+
RangeController range_controller;
7684

7785
std::vector<PrioritizedController> prioritizedControllers;
7886
priority_queue<PrioritizedController> control_queue;

src/behaviours/src/ROSAdapter.cpp

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <geometry_msgs/Twist.h>
2121
#include <nav_msgs/Odometry.h>
2222
#include <apriltags_ros/AprilTagDetectionArray.h>
23+
#include <std_msgs/Float32MultiArray.h>
2324

2425
// Include Controllers
2526
#include "LogicController.h"
@@ -33,9 +34,30 @@
3334
#include <ros/ros.h>
3435
#include <signal.h>
3536

37+
#include <exception> // For exception handling
3638

3739
using namespace std;
3840

41+
// Define Exceptions
42+
// Define an exception to be thrown if the user tries to create
43+
// a RangeShape using invalid dimensions
44+
class ROSAdapterRangeShapeInvalidTypeException : public std::exception {
45+
public:
46+
ROSAdapterRangeShapeInvalidTypeException(std::string msg) {
47+
this->msg = msg;
48+
}
49+
50+
virtual const char* what() const throw()
51+
{
52+
std::string message = "Invalid RangeShape type provided: " + msg;
53+
return message.c_str();
54+
}
55+
56+
private:
57+
std::string msg;
58+
};
59+
60+
3961
// Random number generator
4062
random_numbers::RandomNumberGenerator* rng;
4163

@@ -106,6 +128,7 @@ ros::Subscriber modeSubscriber;
106128
ros::Subscriber targetSubscriber;
107129
ros::Subscriber odometrySubscriber;
108130
ros::Subscriber mapSubscriber;
131+
ros::Subscriber virtualFenceSubscriber;
109132

110133

111134
// Timers
@@ -133,6 +156,7 @@ void modeHandler(const std_msgs::UInt8::ConstPtr& message);
133156
void targetHandler(const apriltags_ros::AprilTagDetectionArray::ConstPtr& tagInfo);
134157
void odometryHandler(const nav_msgs::Odometry::ConstPtr& message);
135158
void mapHandler(const nav_msgs::Odometry::ConstPtr& message);
159+
void virtualFenceHandler(const std_msgs::Float32MultiArray& message);
136160
void behaviourStateMachine(const ros::TimerEvent&);
137161
void publishStatusTimerEventHandler(const ros::TimerEvent& event);
138162
void publishHeartBeatTimerEventHandler(const ros::TimerEvent& event);
@@ -167,7 +191,8 @@ int main(int argc, char **argv) {
167191
targetSubscriber = mNH.subscribe((publishedName + "/targets"), 10, targetHandler);
168192
odometrySubscriber = mNH.subscribe((publishedName + "/odom/filtered"), 10, odometryHandler);
169193
mapSubscriber = mNH.subscribe((publishedName + "/odom/ekf"), 10, mapHandler);
170-
message_filters::Subscriber<sensor_msgs::Range> sonarLeftSubscriber(mNH, (publishedName + "/sonarLeft"), 10);
194+
virtualFenceSubscriber = mNH.subscribe(("/virtualFence"), 10, virtualFenceHandler);
195+
message_filters::Subscriber<sensor_msgs::Range> sonarLeftSubscriber(mNH, (publishedName + "/sonarLeft"), 10);
171196
message_filters::Subscriber<sensor_msgs::Range> sonarCenterSubscriber(mNH, (publishedName + "/sonarCenter"), 10);
172197
message_filters::Subscriber<sensor_msgs::Range> sonarRightSubscriber(mNH, (publishedName + "/sonarRight"), 10);
173198

@@ -294,7 +319,7 @@ void behaviourStateMachine(const ros::TimerEvent&) {
294319
}
295320

296321
//publishHandeling here
297-
//logicController.getPublishData() suggested;
322+
//logicController.getPublishData(); suggested
298323

299324

300325
//adds a blank space between sets of debugging data to easly tell one tick from the next
@@ -385,6 +410,53 @@ void odometryHandler(const nav_msgs::Odometry::ConstPtr& message) {
385410
logicController.SetVelocityData(linearVelocity, angularVelocity);
386411
}
387412

413+
// Allows a virtual fence to be defined and enabled or disabled through ROS
414+
void virtualFenceHandler(const std_msgs::Float32MultiArray& message)
415+
{
416+
// Read data from the message array
417+
// The first element is an integer indicating the shape type
418+
// 0 = Disable the virtual fence
419+
// 1 = circle
420+
// 2 = rectangle
421+
int shape_type = static_cast<int>(message.data[0]); // Shape type
422+
423+
if (shape_type == 0)
424+
{
425+
logicController.setVirtualFenceOff();
426+
}
427+
else
428+
{
429+
// Elements 2 and 3 are the x and y coordinates of the range center
430+
Point center;
431+
center.x = message.data[1]; // Range center x
432+
center.y = message.data[2]; // Range center y
433+
434+
// If the shape type is "circle" then element 4 is the radius, if rectangle then width
435+
switch ( shape_type )
436+
{
437+
case 1: // Circle
438+
{
439+
if ( message.data.size() != 4 ) throw ROSAdapterRangeShapeInvalidTypeException("Wrong number of parameters for circle shape type in ROSAdapter.cpp:virtualFenceHandler()");
440+
float radius = message.data[3];
441+
logicController.setVirtualFenceOn( new RangeCircle(center, radius) );
442+
break;
443+
}
444+
case 2: // Rectangle
445+
{
446+
if ( message.data.size() != 5 ) throw ROSAdapterRangeShapeInvalidTypeException("Wrong number of parameters for rectangle shape type in ROSAdapter.cpp:virtualFenceHandler()");
447+
float width = message.data[3];
448+
float height = message.data[4];
449+
logicController.setVirtualFenceOn( new RangeRectangle(center, width, height) );
450+
break;
451+
}
452+
default:
453+
{ // Unknown shape type specified
454+
throw ROSAdapterRangeShapeInvalidTypeException("Unknown Shape type in ROSAdapter.cpp:virtualFenceHandler()");
455+
}
456+
}
457+
}
458+
}
459+
388460
void mapHandler(const nav_msgs::Odometry::ConstPtr& message) {
389461
//Get (x,y) location directly from pose
390462
currentLocationMap.x = message->pose.pose.position.x;
@@ -400,11 +472,11 @@ void mapHandler(const nav_msgs::Odometry::ConstPtr& message) {
400472
linearVelocity = message->twist.twist.linear.x;
401473
angularVelocity = message->twist.twist.angular.z;
402474

403-
Point currentLoc;
404-
currentLoc.x = currentLocation.x;
405-
currentLoc.y = currentLocation.y;
406-
currentLoc.theta = currentLocation.theta;
407-
logicController.SetPositionData(currentLoc);
475+
Point curr_loc;
476+
curr_loc.x = currentLocationMap.x;
477+
curr_loc.y = currentLocationMap.y;
478+
curr_loc.theta = currentLocationMap.theta;
479+
logicController.SetMapPositionData(curr_loc);
408480
logicController.SetMapVelocityData(linearVelocity, angularVelocity);
409481
}
410482

0 commit comments

Comments
 (0)