So, we’ve reached the GSoC final evaluations period. In this post I’ll be summarizing in brief we’ve accomplished in the last 3 months.
We worked on two projects one is Hydrus and the other one is a Forestry Patrol Simulation using various Hydrus instances with a mechanics layer on the top.
I already wrote a post about hydrus. So in this post, I’ll tell you about the simulation in detail.
General Idea
Hydra Flock Simulation is an application that simulates the movements of a flock of drones that have as objective to detect the presence of fires or abnormal heat spots in a given geographical area using infrared sensors.
The simulation uses the Hydrus and Hydra-py.
Running the Simulation
Running the simulation is pretty straightforward, you just need to run two scripts.
First clone the simulation repo to your local system using
git clone https://github.com/HTTP-APIs/hydra-flock-demo
and then follow the following steps:
- Create and activate a new virtualenv
- Upgrade pip and setuptools
- Check if
python3-dev
is installed cd
to the project directory- Run
chmod +x bootstrap-dev.sh
- Run
./bootstrap-dev.sh
- Run
chmod +x init.sh
- Run
./bootstrap-dev.sh
Simulation GUI should be available at http://127.0.0.1:5000/
Central Controller should be available at http://127.0.0.1:8080/api
Drones should be available at
- Drone 1 -
http://127.0.0.1:8081/api
- Drone 2 -
http://127.0.0.1:8082/api
- Drone 3 -
http://127.0.0.1:8083/api
- Drone 4 -
http://127.0.0.1:8084/api
NOTE: To stop all simulation processes use killall python
Interacting with the simulation
The user can interact with the simulation from the simulation GUI. He can submit messages in a structured format to the central controller, the central controller will then parse the submitted message and issue commands to respective drones. Progress can be seen in controller and drone logs in the GUI.
Some general information
- Supported Directions are [N, S, E, W]
- The user can only turn drones ON (
Active
) or Off (Off
), rest all the states are automatically handled by the drone mechanics. To read more about the drone mechanics, read the drone design page. - Speed can be any number less than the
MaxSpeed
value of the drone, for any value greater thanMaxSpeed
, drones will use theMaxSpeed
value.
Some Examples
Set Drone 2 speed 100
(Change the speed of drone with id 2 to 100 Km/h.)Set Drone 8 direction N
(Change the direction of drone with id 8 to North.)Set Drone 13 status off
(Turn drone with id 13 off.)Set Drone 8 status active
(Turn drone with id 8 on.)
NOTE:- Changing the drone direction won’t work if the drone is in Confirming
state.
Central Controller design
The central controller is a Hydra based API server acting as a central datastore and control center for all the drones. All active drones submit their status updates every 15 seconds to the central controller. Just like drones the central controller also has a client part that can be used to issue commands to different drones.
Main Features
- It acts as a central database for all the drones to store their logs and sensor data. This is being done using the drone client side, drones issue POST/PUT requests after every 15 seconds or upon some status change. The central controller has different endpoints for different types of data.
/api/DroneCollection/<drone_id>
Each active drone updates their drone object usingPUT/POST
requests here, every 15 seconds./api/DatastreamCollection/
Datastream from the drone sensors is stored here./api/DroneLogCollection/
Logs related to drones are stored here./api/ControllerLogCollection
Logs related to the controller are stored here./api/HttpApiLogCollection
Logs related to general interactions between different components are stored here.
- The user can issue a message to the central controller at
/api/MessageCollection/
in a structured format. The message is then parsed and commands are issued to the respective drones. - The server will act as an intermediate if different drones want to interact with each other. For example - If some drone wants to know the position of all nearby drones.
- The central controller acts as the main docking station to recharge drones.
The Central controller is driven by a 17 second time loop (2-second delay to let drones finish updating their status at the controller).
Main Loop design
"""Main loop for the central controller."""
print("Controller Simulation Loop")
try:
handle_messages()
handle_anomalies()
except Exception as e:
print(e)
finally:
threading.Timer(LOOP_TIME, main).start()
In the main Loop, the controller tries to handle two things.
- The messages submitted by the user
- Anomalies detected by drones
Handling Messages
"""Handle messages in the MessageCollection."""
try:
message_collection = get_message_collection()
print(message_collection)
for message in message_collection:
regex = r'/(.*)/(\d*)'
matchObj = re.match(regex, message["@id"])
if matchObj:
message_id = matchObj.group(2)
message_details = get_message(message_id)
# parse message
message_string = message_details["MessageString"]
parsed_message = parse_message(message_string)
if parsed_message is not None:
drone_id, prop, value = parsed_message
if not validate_message_prop_value(prop, value):
delete_message(message_id)
else:
command = generate_command(drone_id, prop, value)
try:
RES, NAMESPACE = find_res(drone_id)
if RES is not None and NAMESPACE is not None:
issue_command(RES, NAMESPACE, command)
except:
controllerlog = gen_ControllerLog(
"Drone with id %s not found, deleting message." % (str(drone_id)), "")
send_controllerlog(controllerlog)
delete_message(message_id)
# delete message
delete_message(message_id)
except Exception as e:
print(e)
Parsing a Message
""" Parsing a given message string.
Messages will be in format 'Set Drone <DroneID> <Property> <Value>'.
DroneID is the drone identifier assigned by the central controller.
Property can be Direction, Speed or Status and values can be anything
supported by that respective Property."""
message_items = message_string.lower().split(" ")
try:
drone_id = message_items[message_items.index("drone") + 1]
prop = message_items[message_items.index(drone_id) + 1]
value = message_items[message_items.index(prop) + 1]
return (drone_id, prop.title(), value.title())
except Exception as e:
print(e)
return None
Handling Anomalies
"""Handling the anomalies in AnomalyCollection."""
try:
anomaly_collection = get_anomaly_collection()
drone_collection = get_drone_collection()
non_confirmed_anomalies, negative_anomalies = find_non_confirmed_and_negative_anomalies(
anomaly_collection)
# Handle non_confirmed_anomalies
for anomaly in non_confirmed_anomalies:
handle_anomaly(anomaly, drone_collection)
# Delete Negative anomalies
for anomaly in negative_anomalies:
delete_anomaly(anomaly["AnomalyID"])
except Exception as e:
print(e)
The full implementation of the main loop is available at flock_controller.mechanics.simulate
.
Full source code for the controller component is available here.
Drone Design
Drones act as Hydra Servers as well as Hydra Clients (Hybrids): they have some exposed endpoints where other Hydra Client can send orders or ask for updates, but they also have Hydra Client capabilities themselves, to send information and signals to the Controller by calling the Controller’s Hydra API.
Main Features
- Drones can receive commands from the server at
/api/CommandCollection
.
Each command has aState
object with properties likeSpeed
andDirection
orStatus
. Upon receiving a command the drone will act immediately unless there is some issue and it needs to override the command.
The drone deletes the command from it’s/api/CommandCollection
after successful execution of each command. - Drones publish their current state with all other properties at
/api/Drone
. - Drone publish the latest
Datastream
from its temperature sensors at/api/Datastream
. - Each drone has a unique id assigned by the controller at runtime.
- Drones cannot interact with other drones or issue commands to them directly, they need to do so via the central server.
- Drones will automatically decide when to charge and only, in that case, override the commands sent by the server only after sending a log of its status to the central server.
- Drones obey some basic rules ( For example - can’t hit each other)
Drone Behavior
Drones are driven by a 15 second time loop after which the drones update their position, generate new sensor datastreams depending upon anomalies detected (if any).
Main Loop design
"""Main 15 second time loop for drone mechanics."""
try:
print("Retrieving the drone details")
drone = get_drone()
drone_identifier = drone["DroneID"]
datastream = None
# Commands will be executed in any state
drone = handle_drone_commands(drone)
if is_not_off(drone):
## Handle drone battery change
drone = handle_drone_battery(drone)
## Handle drone general behaviour
anomaly = get_anomaly()
if anomaly is not None:
if anomaly["Status"] == "Confirming" and drone["State"]["Status"] == "Active":
drone["State"]["Status"] = "Confirming"
if is_confirming(drone):
print("Drone handling anomaly")
drone = handle_anomaly(drone)
elif is_inactive(drone):
print("Drone battery low, needs to charge")
drone = handle_drone_low_battery(drone)
elif is_active(drone):
anomaly = gen_grid_anomaly(drone)
if anomaly is not None:
print("New anomaly created")
send_anomaly(anomaly, drone_identifier)
datastream = gen_Datastream(gen_abnormal_sensor_data(
), drone["State"]["Position"], drone_identifier)
else:
datastream = gen_Datastream(gen_normal_sensor_data(
), drone["State"]["Position"], drone_identifier)
# Handle positions change
drone = handle_drone_position(drone)
# update the drone both locally and on the controller
update_drone(drone)
update_drone_at_controller(drone, drone_identifier)
if datastream is not None:
# Send datastream to central controller
send_datastream(datastream)
# Update datastream locally
update_datastream(datastream)
except Exception as e:
print(e)
finally:
# call main() again in LOOP_TIME
threading.Timer(LOOP_TIME, main).start()
The full implementation of the main loop is available at flock_drone.mechanics.simulate
.
Sample Drone Object
All the drone configurations are defined in a drone object.
DRONE = {
"@type": "Drone",
"DroneID": "-1000",
"name": "Drone 1",
"model": "xyz",
"MaxSpeed": "130",
"Sensor": "Temperature",
"State": {
"@type": "State",
"Speed": "100",
"Position": "0,0",
"Battery": "100",
"Direction": "N",
"Status": "Active",
}
}
During runtime the drone mechanics modifies the State
object to simulate the drone behavior.
Speed
The speed of each drone can be controlled by user from the Simulation GUI.
NOTE:- Speed <= MaxSpeed
of the drone.
Direction and Position
- Drones will always move within the area of interest, if a drone reaches the boundary then it will change its direction.
- Each drone can move in one of the four directions East, West, North and South
[E, W, N, S]
Status
Drones use different Status
values to handle different scenarios
- Active - Active is the drone default drone status, in this state the drone is free to move around within the area of interest.
- Inactive - The inactive mode is like power saving mode. Drone takes only 1/4 battery in this state and all the sensors are turned off. Drones use this state to return back to the central controller for charging.
- Charging -
Drones need to return to the central controller for charging. Drones change their status to
Charging
once they reach the central controller. - Confirming - When the drone is confirming any anomaly detected by some other drone. User can’t change the drone direction in this state.
- Off - Drone is turned off.
Battery
- When
Battery > 20
- Drones function normally when theirBattery > 20
- When
4 < Battery < 20
- Drones enter power saving mode (Inactive
state) and start moving toward the central controller for charging. - When
Battery < 4
- If any drone fails to reach the central controller then it shuts down.
Full source code for the drone component is available here.