Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rktl Comments and Documentation #147

Open
wants to merge 109 commits into
base: sim-refactor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
3345629
reviewed sim side code and left questions"
Abuynits Jun 12, 2022
77c836c
_ros_interface.py: added comments
Abuynits Jun 13, 2022
69c1399
rocket_league_interface.py: added comments
Abuynits Jun 13, 2022
b52b82a
node:rocket_league_agent: added comments
Abuynits Jun 13, 2022
5a51127
train_rocket_league.py script: added comments
Abuynits Jun 13, 2022
cda5c78
simulator node: added comments
Abuynits Jun 13, 2022
6ce321f
sim.py: added comments
Abuynits Jun 13, 2022
b3b9869
added field randomization
Abuynits Jun 15, 2022
2e0b9bf
added max_speed randomization
Abuynits Jun 15, 2022
7cb2c5c
added steering throw randomization
Abuynits Jun 15, 2022
7814cf8
redid randomization function, added comments for readability
Abuynits Jun 15, 2022
df7d91a
split up reset function
Abuynits Jun 16, 2022
6108620
moved max_speed and steering_throw generation to seperate functions
Abuynits Jun 16, 2022
28cf355
created separate func init for spawn location
Abuynits Jun 16, 2022
a13861d
created seperate func into car properties
Abuynits Jun 16, 2022
ceb460e
fixed type in car properties init
Abuynits Jun 16, 2022
f958784
added function for initializing car properties
Abuynits Jun 16, 2022
9ffe685
added function for initialization of sensor noise
Abuynits Jun 16, 2022
bbbedf5
specified renewal of functions in the simulator and the passing of pa…
Abuynits Jun 16, 2022
e9cbb44
added functions for ball generation
Abuynits Jun 16, 2022
dd3e5b8
added comments
Abuynits Jun 16, 2022
70b1432
added further split of functions to the simulator class
Abuynits Jun 16, 2022
2203310
added function for creation of any type of rospy variable given a few…
Abuynits Jun 23, 2022
2ed9ee6
split up car creation and car properties generation in multiple funct…
Abuynits Jun 23, 2022
29d7ec6
added function for looping over and deleting all of the cars
Abuynits Jun 23, 2022
3e1fe94
split up initialization in the simulator node
Abuynits Jun 23, 2022
19f8b22
fixed typos in functions
Abuynits Jun 23, 2022
a34b935
formated the simulation yaml
Abuynits Jun 25, 2022
f6019ff
meeting 2 feedback:
Abuynits Jun 25, 2022
70345bd
setup for reset of parameters:
Abuynits Jun 25, 2022
06a800f
allowed functions to set and return values at the same time for redun…
Abuynits Jun 25, 2022
e44a8a3
fixed moment with bad indent in python to add reset_car and reset_bal…
Abuynits Jun 25, 2022
d7e7816
removed update function and handed it off to sim.py class
Abuynits Jun 25, 2022
903a710
added new vars to car.py, added main logic for reseting cars and ball…
Abuynits Jun 25, 2022
1ccc57a
fixed bug with unfilled paramter
Abuynits Jun 25, 2022
f0537f6
fixed typos in tex
Abuynits Jun 25, 2022
eda35a9
fixed get_param to be more modular, made exception throw fatal error …
Abuynits Jun 25, 2022
d2cb850
added a list as a valid return type from sim_get_param
Abuynits Jun 25, 2022
e18a92d
added untested get_sim_dict function that loops thorugh recursion giv…
Abuynits Jun 26, 2022
838a2fb
added logic that prevents cars from spawning on each other;
Abuynits Jun 27, 2022
5d55fa0
added print statment to invalidrospyparam exception
Abuynits Jul 5, 2022
ddd361f
fixed white space type for functions
Abuynits Jul 5, 2022
386454f
removed white space change
Abuynits Jul 5, 2022
26bf93c
added constants to the top of the file
Abuynits Jul 5, 2022
4aa3b7f
large comment organization and changes:
Abuynits Jul 5, 2022
5f53f75
added urdf folder with auto-generated files to .gitignore
Abuynits Jul 5, 2022
ec41d42
added init_pos to param instead of pos. for cap.py
Abuynits Jul 5, 2022
2cb0e78
removed check of whether the care name is in the configs as outdated …
Abuynits Jul 5, 2022
6c90ec2
removed set member variables in set_senosr_noise function
Abuynits Jul 5, 2022
2becdee
removed function for setting sensor noise and placed it in the init f…
Abuynits Jul 5, 2022
b1374ea
renamed function from set_car_properties to set_properties
Abuynits Jul 5, 2022
e0aa4d3
redid get_sim_param and get_sim_dict
Abuynits Jul 5, 2022
3b76816
remove the reset of car properties as they are part of the instantiat…
Abuynits Jul 5, 2022
22796c9
added changes to a function that was renamed in a previous commit
Abuynits Jul 5, 2022
6ab5af7
added reworked comments for the car class
Abuynits Jul 7, 2022
7a12de8
cleaned up comments, split them up in smaller, more concise chunks
Abuynits Jul 7, 2022
8407694
updated comments to be more concise and formated the file
Abuynits Jul 8, 2022
3f78d4b
simplified comments, split them up according to organization of wiki
Abuynits Jul 8, 2022
cfd52db
reworked comments for simulator class
Abuynits Jul 8, 2022
6395703
cleaned up comments, removed uneeded descriptions
Abuynits Jul 8, 2022
79ca826
added urdf autogenerated files to .gitignore
Abuynits Jul 8, 2022
437d97d
added clean exit via rospy.logfatal(error) over calling custom exception
Abuynits Jul 8, 2022
24da15f
removed straightforward and summary based comments
Abuynits Jul 8, 2022
1efbb13
fixed grammer, punctuation, capitalization for _ros_interface
Abuynits Jul 8, 2022
3252740
fixed grammer, punctuation, capitalization, cleaned and clarified var…
Abuynits Jul 9, 2022
d3566cd
fixed grammer, punctuation, capitalization, cleaned and clarified var…
Abuynits Jul 9, 2022
3951104
fixed grammer, punctuation, capitalization, cleaned and clarified var…
Abuynits Jul 9, 2022
d6f134b
removed walls.urdf from being tracked in git repo
Abuynits Jul 9, 2022
288ae4c
reformated comments, removed confusing comment
Abuynits Jul 9, 2022
66dc5a1
added second paramter to get_sim_param, calling all rospy.getparam wi…
Abuynits Jul 9, 2022
dcf4ce2
fixed grammer, punctuation, capitalization, cleaned and clarified var…
Abuynits Jul 9, 2022
80db691
fixed comments
Abuynits Jul 10, 2022
0af68da
fixed comments
Abuynits Jul 10, 2022
8c2c214
added params for resetting the ball and passing in velocity and position
Abuynits Jul 12, 2022
97fdb81
simulator node rebuild:
Abuynits Jul 12, 2022
3ba1e69
added two more callback functions: delete and create all cars. might …
Abuynits Jul 12, 2022
30e80c1
removed generate_car_properties function to reset_cb
Abuynits Jul 12, 2022
03ed884
worked through comments and changes for these file
Abuynits Jul 17, 2022
8297e13
fixed comments and added better documentation
Abuynits Jul 19, 2022
07b69a6
undid bad formating
Abuynits Jul 20, 2022
e65a5f6
added better comments and documentation
Abuynits Jul 21, 2022
ab9b859
added comments for car.py
Abuynits Jul 21, 2022
56a5796
added the configuration function back as it is called by the sim.py
Abuynits Jul 22, 2022
e56fc01
changed the reset_cb function to have an empty param. removed the cur…
Abuynits Jul 22, 2022
8b4df34
fixed bug in bad function call, reworked comments and swapped out res…
Abuynits Jul 24, 2022
5d4a184
refactored car.py
Abuynits Jul 24, 2022
2998978
reworked reset fucntion and create_cb / delete_cb functions, saving work
Abuynits Jul 26, 2022
6a2f9a0
refactored the reset function
Abuynits Jul 26, 2022
612208c
add pfilter to dockerfile
jaketockerman Sep 11, 2022
886bd3c
Merge pull request #148 from purdue-arc/pfilter
ajernandes Sep 11, 2022
c71d6e3
add pull argument to docker build call in server run
jaketockerman Sep 12, 2022
ed99727
Merge pull request #149 from purdue-arc/dsr
ajernandes Sep 15, 2022
ef1b82c
fixed bugs with bad param naming, identified future bug with attempt …
Abuynits Sep 16, 2022
9f04ae0
fixed error with fetching dictionary
Abuynits Sep 18, 2022
5fe306f
fixed errors with invalid access and checking in hashmap
Abuynits Sep 18, 2022
cfc93a7
fixed moment with indent, fixed other bugs
Abuynits Sep 18, 2022
ee1dd09
no more momments with compilation
Abuynits Sep 18, 2022
19862ff
final changes
Abuynits Sep 22, 2022
1fed44f
Update .gitignore
jcrm1 Sep 22, 2022
0604602
added print statements, fixed bad math sqrt call
Abuynits Sep 25, 2022
ea04ee7
added debug print, fixed math.random call
Abuynits Sep 25, 2022
b16934c
Merge pull request #151 from purdue-arc/jcrm1-gitignore-patch
halcdev Sep 25, 2022
1916b63
Merge branch 'main' into sim-refactor
Abuynits Sep 25, 2022
fd94e61
fixed overlap generations of cars
Abuynits Sep 26, 2022
90550ef
udpated sim
Abuynits Sep 26, 2022
0a4b5cd
added debug messages: problem: 0 velocity in car
Abuynits Sep 27, 2022
04b9ec7
aded debug print
Abuynits Sep 27, 2022
1d3fd83
found problem with bad cmd
Abuynits Sep 27, 2022
4b34087
tested out the randomization of parameters.need to update callback fu…
Abuynits Sep 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,9 @@ dmypy.json

# MATLAB
*.asv

#urdf removal:
rktl_sim/urdf/walls.urdf
rktl_sim/urdf/goal_a.urdf
rktl_sim/urdf/goal_b.urdf
rktl_sim/urdf/walls.urdf
8 changes: 5 additions & 3 deletions rktl_autonomy/nodes/rocket_league_agent
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ from stable_baselines3 import PPO
from os.path import expanduser
import rospy

# create interface (and init ROS)
# Create interface (and init ROS).
hmcty marked this conversation as resolved.
Show resolved Hide resolved
env = RocketLeagueInterface(eval=True)

# load the model
# Load the model.
weights = expanduser(rospy.get_param('~weights'))
model = PPO.load(weights)

# evaluate in real-time
# Evaluate in real-time.
obs = env.reset()
while True:
# Predict the future action for the sim.
action, __ = model.predict(obs)
try:
# step the sim with the action from the model.
obs, __, __, __ = env.step(action)
except rospy.ROSInterruptException:
exit()
27 changes: 15 additions & 12 deletions rktl_autonomy/scripts/train_rocket_league.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
BSD 3-Clause License
Copyright (c) 2021, Autonomous Robotics Club of Purdue (Purdue ARC)
All rights reserved.
stable_baselines3 resource: https://stable-baselines3.readthedocs.io/_/downloads/en/master/pdf/
"""

from rktl_autonomy import RocketLeagueInterface
Expand All @@ -15,32 +16,34 @@
from os.path import expanduser
import uuid

if __name__ == '__main__': # this is required due to forking processes
run_id = str(uuid.uuid4()) # ALL running environments must share this
if __name__ == '__main__':
# This is required due to forking processes.
# ALL running environments must share this id.
run_id = str(uuid.uuid4())
print(f"RUN ID: {run_id}")

# to pass launch args, add to env_kwargs: 'launch_args': ['render:=false', 'plot_log:=true']
env = make_vec_env(RocketLeagueInterface, env_kwargs={'run_id':run_id},
n_envs=24, vec_env_cls=SubprocVecEnv)
# Pass launch args by adding to env_kwargs: 'launch_args': ['render:=false', 'plot_log:=true'].
env = make_vec_env(RocketLeagueInterface, env_kwargs={'run_id': run_id},
n_envs=24, vec_env_cls=SubprocVecEnv)

model = PPO("MlpPolicy", env)

# log training progress as CSV
# Log training progress as CSV.
log_dir = expanduser(f'~/catkin_ws/data/rocket_league/{run_id}')
logger = configure(log_dir, ["stdout", "csv", "log"])
model.set_logger(logger)

# log model weights
freq = 20833 # save 20 times
# Log model weights.
freq = 20833 # save 20 times
# freq = steps / (n_saves * n_envs)
callback = CheckpointCallback(save_freq=freq, save_path=log_dir)

# run training
steps = 240000000 # 240M (10M sequential)
# Run training.
steps = 240000000 # 240M (10M sequential)
print(f"training on {steps} steps")
model.learn(total_timesteps=steps, callback=callback)

# save final weights
# Save final weights.
print("done training")
model.save(log_dir + "/final_weights")
env.close() # this must be done to clean up other processes
env.close() # This must be done to clean up other processes
107 changes: 59 additions & 48 deletions rktl_autonomy/src/rktl_autonomy/_ros_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@
from rosgraph_msgs.msg import Clock
from diagnostic_msgs.msg import DiagnosticStatus, KeyValue


class SimTimeException(Exception):
"""For when advancing sim time does not go as planned."""
pass


class ROSInterface(Env):
"""Extension of the Gym environment class for all specific interfaces
to extend. This class handles logic regarding timesteps in ROS, and
allows users to treat any ROS system as a Gym environment once the
interface is created.

# IMPORTANT: All the below abstract methods marked with @abstractmethod must be implemented by subclasses
Abuynits marked this conversation as resolved.
Show resolved Hide resolved
All classes extending this for a particular environment must do the following:
- implement all abstract properties:
- action_space
Expand All @@ -40,13 +42,13 @@ class ROSInterface(Env):
"""

def __init__(self, node_name='gym_interface', eval=False, launch_file=None, launch_args=[], run_id=None):
"""init function
Params:
node_name: desired name of this node in the ROS network
eval: set true if evaluating an agent in an existing ROS env, set false if training an agent
launch_file: if training, launch file to be used (ex: ['rktl_autonomy', 'rocket_league_train.launch'])
launch_args: if training, arguments to be passed to roslaunch (ex: ['render:=true', rate:=10])
run_id: if training, used to prevent deadlocks. if logging, run_id describes where to save files. Default is randomly generated
"""
initializes the rospy interface
Abuynits marked this conversation as resolved.
Show resolved Hide resolved
@param node_name: desired name of this node in the ROS network
@param eval: set true if evaluating an agent in an existing ROS env, set false if training an agent
@param launch_file: if training, launch file to be used (ex: ['rktl_autonomy', 'rocket_league_train.launch'])
@param launch_args: if training, arguments to be passed to roslaunch (ex: ['render:=true', rate:=10])
@param run_id: if training, used to prevent deadlocks. if logging, run_id describes where to save files.
Abuynits marked this conversation as resolved.
Show resolved Hide resolved
"""
super().__init__()
self.__EVAL_MODE = eval
Expand Down Expand Up @@ -76,10 +78,11 @@ def __init__(self, node_name='gym_interface', eval=False, launch_file=None, laun
ros_id = roslaunch.rlutil.get_or_generate_uuid(None, False)
roslaunch.configure_logging(ros_id)
launch_file = roslaunch.rlutil.resolve_launch_arguments(launch_file)[0]
launch_args = [f'render:={port==11311}', f'plot_log:={port==11311}'] + launch_args + [f'agent_name:={node_name}']
launch_args = [f'render:={port == 11311}', f'plot_log:={port == 11311}'] + launch_args + [
f'agent_name:={node_name}']
Abuynits marked this conversation as resolved.
Show resolved Hide resolved
launch = roslaunch.parent.ROSLaunchParent(ros_id, [(launch_file, launch_args)], port=port)
launch.start()
self.close = lambda : launch.shutdown()
self.close = lambda: launch.shutdown()
# initialize self
os.environ['ROS_MASTER_URI'] = f'http://localhost:{port}'
rospy.init_node(node_name)
Expand All @@ -91,6 +94,7 @@ def __init__(self, node_name='gym_interface', eval=False, launch_file=None, laun

# private variables
self._cond = Condition()
# TODO: why cant merge to if statement above?

# additional set up for training
if not self.__EVAL_MODE:
Expand All @@ -116,72 +120,77 @@ def step(self, action):
"""
Implementation of gym.Env.step. This function will intentionally block
if the ROS environment is not ready.

Run one timestep of the environment's dynamics. When end of
episode is reached, you are responsible for calling `reset()`
to reset this environment's state.
Accepts an action and returns a tuple (observation, reward, done, info).
Args:
action (object): an action provided by the agent
Returns:
observation (object): agent's observation of the current environment
Run one timestep of the environment's dynamics.
When end of episode is reached, you are responsible for calling `reset()` to reset this environment's state.
@param action: action (object): an action provided by the agent
@return: observation a tuple of the following:
(object): agent's observation of the current environment
reward (float) : amount of reward returned after previous action
done (bool): whether the episode has ended, in which case further step() calls will return undefined results
info (dict): contains auxiliary diagnostic information (helpful for debugging, and sometimes learning)

"""

self._clear_state()
self._publish_action(action)
self.__step_time_and_wait_for_state()
state = self._get_state()
self.__net_reward += state[1] # logging
self.__net_reward += state[1] # logging
return state

def reset(self):
"""Resets the environment to an initial state and returns an initial observation.

"""
Resets the environment to an initial state and returns an initial observation.
Note that this function should not reset the environment's random
number generator(s); random variables in the environment's state should
be sampled independently between multiple calls to `reset()`. In other
words, each call of `reset()` should yield an environment suitable for
a new episode, independent of previous episodes.
Returns:
observation (object): the initial observation.
number generator(s).
Random variables in the environment's state should
be sampled independently between multiple calls to `reset()`.
@return: the initial observation.
"""

# Checks if a new state is ready via: _has_state.
if self._has_state():
# generate log
# Gathers the following information: episode #, net reward, duration of the episode.

# Generate a log.
info = {
'episode' : self.__episode,
'net_reward' : self.__net_reward,
'duration' : (rospy.Time.now() - self.__start_time).to_sec()
'episode': self.__episode,
'net_reward': self.__net_reward,
'duration': (rospy.Time.now() - self.__start_time).to_sec()
}
# Update the message log with these parameters by publishing it.

info.update(self._get_state()[3])
# send message
# Send message.
msg = DiagnosticStatus()
msg.level = DiagnosticStatus.OK
msg.name = 'ROS-Gym Interface'
msg.message = 'log of episode data'
msg.hardware_id = self.__LOG_ID
msg.values = [KeyValue(key=key, value=str(value)) for key, value in info.items()]
self.__log_pub.publish(msg)
# update variables (update time after reset)
self.__episode += 1
self.__net_reward = 0

# reset
if not self.__EVAL_MODE:
self._reset_env()
# Reset the ROS interface (abstract method).
self._reset_self()
self.__step_time_and_wait_for_state(5)
self.__start_time = rospy.Time.now() # logging
self.__start_time = rospy.Time.now() # logging
return self._get_state()[0]

def __step_time_and_wait_for_state(self, max_retries=1):
"""Step time until a state is known."""
"""
Increment time and clock, try to publish the next simulation step in the number of tries.
@param max_retries: Number of time steps until state is known.
"""
if not self.__EVAL_MODE:
self.__time += self.__DELTA_T
self.__clock_pub.publish(self.__time)
retries = 0
while not self.__wait_once_for_state():

if retries >= max_retries:
rospy.logerr("Failed to get new state.")
raise SimTimeException
Expand All @@ -190,56 +199,58 @@ def __step_time_and_wait_for_state(self, max_retries=1):
self.__clock_pub.publish(self.__time)
retries += 1
else:
# Call for the provided number of retries.
while not self.__wait_once_for_state():
pass # idle wait
pass # idle wait

def __wait_once_for_state(self):
"""Wait and allow other threads to run."""
""" Wait and allow other threads to run."""
with self._cond:
has_state = self._cond.wait_for(self._has_state, 0.25)
if rospy.is_shutdown():
raise rospy.ROSInterruptException()
return has_state

# All the below abstract methods / properties must be implemented by subclasses
@property
@abstractmethod
def action_space(self):
"""The Space object corresponding to valid actions."""
""" The Space object corresponding to valid actions."""

raise NotImplementedError

@property
@abstractmethod
def observation_space(self):
"""The Space object corresponding to valid observations."""
""" The Space object corresponding to valid observations."""
raise NotImplementedError

@abstractmethod
def _reset_env(self):
"""Reset environment for a new episode."""
""" Reset environment for a new episode."""
raise NotImplementedError

@abstractmethod
def _reset_self(self):
"""Reset internally for a new episode."""
""" Reset internally for a new episode."""
raise NotImplementedError

@abstractmethod
def _has_state(self):
"""Determine if the new state is ready."""
""" Determine if the new state is ready."""

raise NotImplementedError

@abstractmethod
def _clear_state(self):
"""Clear state variables / flags in preparation for new ones."""
""" Clear state variables / flags in preparation for new ones."""
raise NotImplementedError

@abstractmethod
def _get_state(self):
"""Get state tuple (observation, reward, done, info)."""
""" Get state tuple (observation, reward, done, info)."""
raise NotImplementedError

@abstractmethod
def _publish_action(self, action):
"""Publish an action to the ROS network."""
""" Publish an action to the ROS network."""
raise NotImplementedError
Loading