diff --git a/.github/workflows/ros-ci.yml b/.github/workflows/ros-ci.yml
index 903cf76..5e8d842 100644
--- a/.github/workflows/ros-ci.yml
+++ b/.github/workflows/ros-ci.yml
@@ -49,6 +49,23 @@ jobs:
run: |
printf "[install]\nbreak-system-packages = true\n" | sudo tee /etc/pip.conf
+ - name: Check and Install ROS dependencies
+ shell: bash
+ run: |
+ set -e
+ source /opt/ros/${{ matrix.ros_distribution }}/setup.bash
+ echo "--- Updating rosdep definitions ---"
+ rosdep update
+ echo "--- Installing system dependencies for ROS 2 ${{ matrix.ros_distribution }} ---"
+ rosdep install --from-paths ros_ws/src --ignore-src -y -r --rosdistro ${{ matrix.ros_distribution }}
+ echo "--- Performing rosdep check for ROS 2 ${{ matrix.ros_distribution }} ---"
+ if rosdep check --from-paths ros_ws/src --ignore-src --rosdistro ${{ matrix.ros_distribution }}; then
+ echo "--- rosdep check passed ---"
+ else
+ echo "--- rosdep check failed: Missing system dependencies or unresolvable keys. ---"
+ exit 1
+ fi
+
- name: Build and Test
uses: ros-tooling/action-ros-ci@v0.3
env:
diff --git a/README.md b/README.md
index 011fb0f..dbdc612 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# TurtleBot3
-- Active Branches: noetic, humble, jazzy, main(rolling)
-- Legacy Branches: *-devel
+- Active Branches: humble, jazzy, main(rolling)
+- Legacy Branches: *-devel, noetic
## Open Source Projects Related to TurtleBot3
- [turtlebot3](https://github.com/ROBOTIS-GIT/turtlebot3)
@@ -17,6 +17,7 @@
- [turtlebot3_home_service_challenge](https://github.com/ROBOTIS-GIT/turtlebot3_home_service_challenge)
- [hls_lfcd_lds_driver](https://github.com/ROBOTIS-GIT/hls_lfcd_lds_driver)
- [ld08_driver](https://github.com/ROBOTIS-GIT/ld08_driver)
+- [coin_d4_driver](https://github.com/ROBOTIS-GIT/coin_d4_driver)
- [open_manipulator](https://github.com/ROBOTIS-GIT/open_manipulator)
- [dynamixel_sdk](https://github.com/ROBOTIS-GIT/DynamixelSDK)
- [OpenCR-Hardware](https://github.com/ROBOTIS-GIT/OpenCR-Hardware)
diff --git a/turtlebot3_dqn/CHANGELOG.rst b/turtlebot3_dqn/CHANGELOG.rst
index b910ed9..560c3e5 100644
--- a/turtlebot3_dqn/CHANGELOG.rst
+++ b/turtlebot3_dqn/CHANGELOG.rst
@@ -2,6 +2,17 @@
Changelog for package turtlebot3_dqn
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1.0.2 (2026-01-06)
+------------------
+* Fixed a bug in the JSON file where the step parameter was incorrectly named; renamed it to step_counter.
+* Changed the system arguments to be passed as ROS parameters for execution.
+* Added a use_gpu parameter to allow selection of whether to use GPU.
+* Added a model_file parameter to enable loading an existing trained model and continuing training.
+* Renamed the load_model variable to use_pretrained_model for clarity.
+* Changed model_path from a class variable to a local variable.
+* Introduced lazy import for TensorFlow modules.
+* Contributors: Hyungyu Kim
+
1.0.1 (2025-05-02)
------------------
* Support for ROS 2 Jazzy version
diff --git a/turtlebot3_dqn/package.xml b/turtlebot3_dqn/package.xml
index 3c7da70..5cc8369 100644
--- a/turtlebot3_dqn/package.xml
+++ b/turtlebot3_dqn/package.xml
@@ -1,7 +1,7 @@
turtlebot3_dqn
- 1.0.1
+ 1.0.2
The turtlebot3_dqn package using reinforcement learning with DQN (Deep Q-Learning).
@@ -12,11 +12,10 @@
https://github.com/ROBOTIS-GIT/turtlebot3_machine_learning/issues
Gilbert
ChanHyeong Lee
+ Hyungyu Kim
python3-pip
ament_index_python
geometry_msgs
- python-tensorflow-pip
- python3-numpy
python3-pyqt5
python3-pyqtgraph
rclpy
diff --git a/turtlebot3_dqn/saved_model/model1.h5 b/turtlebot3_dqn/saved_model/model1.h5
new file mode 100644
index 0000000..2dbd5eb
Binary files /dev/null and b/turtlebot3_dqn/saved_model/model1.h5 differ
diff --git a/turtlebot3_dqn/saved_model/model1.json b/turtlebot3_dqn/saved_model/model1.json
new file mode 100644
index 0000000..06437af
--- /dev/null
+++ b/turtlebot3_dqn/saved_model/model1.json
@@ -0,0 +1 @@
+{"epsilon": 0.05000073888272134, "step_counter": 84401, "trained_episodes": 800}
diff --git a/turtlebot3_dqn/saved_model/stage1_episode600.h5 b/turtlebot3_dqn/saved_model/stage1_episode600.h5
deleted file mode 100644
index fd84e50..0000000
Binary files a/turtlebot3_dqn/saved_model/stage1_episode600.h5 and /dev/null differ
diff --git a/turtlebot3_dqn/saved_model/stage1_episode600.json b/turtlebot3_dqn/saved_model/stage1_episode600.json
deleted file mode 100644
index 7b3e0ef..0000000
--- a/turtlebot3_dqn/saved_model/stage1_episode600.json
+++ /dev/null
@@ -1 +0,0 @@
-{"epsilon": 0.1416302983127139}
diff --git a/turtlebot3_dqn/setup.py b/turtlebot3_dqn/setup.py
index 7cab70f..f1a7e00 100644
--- a/turtlebot3_dqn/setup.py
+++ b/turtlebot3_dqn/setup.py
@@ -8,20 +8,28 @@
('Gilbert', 'kkjong@robotis.com'),
('Ryan Shim', 'N/A'),
('ChanHyeong Lee', 'dddoggi1207@gmail.com'),
+ ('Hyungyu Kim', 'kimhg@robotis.com'),
]
authors = ', '.join(author for author, _ in authors_info)
author_emails = ', '.join(email for _, email in authors_info)
setup(
name=package_name,
- version='1.0.1',
+ version='1.0.2',
packages=find_packages(),
data_files=[
('share/ament_index/resource_index/packages', ['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/' + package_name + '/launch', glob.glob('launch/*.py')),
],
- install_requires=['setuptools', 'launch'],
+ install_requires=[
+ 'setuptools',
+ 'launch',
+ 'tensorflow==2.19.0',
+ 'numpy==1.26.4',
+ 'scipy==1.10.1',
+ 'keras==3.9.2',
+ ],
zip_safe=True,
author=authors,
author_email=author_emails,
diff --git a/turtlebot3_dqn/turtlebot3_dqn/dqn_agent.py b/turtlebot3_dqn/turtlebot3_dqn/dqn_agent.py
index 69c7f7c..d20ba94 100644
--- a/turtlebot3_dqn/turtlebot3_dqn/dqn_agent.py
+++ b/turtlebot3_dqn/turtlebot3_dqn/dqn_agent.py
@@ -15,7 +15,7 @@
# limitations under the License.
#################################################################################
#
-# Authors: Ryan Shim, Gilbert, ChanHyeong Lee
+# Authors: Ryan Shim, Gilbert, ChanHyeong Lee, Hyungyu Kim
import collections
import datetime
@@ -31,52 +31,119 @@
from rclpy.node import Node
from std_msgs.msg import Float32MultiArray
from std_srvs.srv import Empty
-import tensorflow
-from tensorflow.keras.layers import Dense
-from tensorflow.keras.layers import Input
-from tensorflow.keras.losses import MeanSquaredError
-from tensorflow.keras.models import load_model
-from tensorflow.keras.models import Sequential
-from tensorflow.keras.optimizers import Adam
from turtlebot3_msgs.srv import Dqn
-tensorflow.config.set_visible_devices([], 'GPU')
-
LOGGING = True
current_time = datetime.datetime.now().strftime('[%mm%dd-%H:%M]')
+_tensorflow = None
+_Dense = None
+_Input = None
+_MeanSquaredError = None
+_load_model = None
+_Sequential = None
+_Adam = None
+
+
+def _import_tensorflow():
+ try:
+ import tensorflow
+ from tensorflow.keras.layers import Dense
+ from tensorflow.keras.layers import Input
+ from tensorflow.keras.losses import MeanSquaredError
+ from tensorflow.keras.models import load_model
+ from tensorflow.keras.models import Sequential
+ from tensorflow.keras.optimizers import Adam
+ return (
+ tensorflow,
+ Dense,
+ Input,
+ MeanSquaredError,
+ load_model,
+ Sequential,
+ Adam
+ )
+ except ImportError as e:
+ print(f'Error importing TensorFlow: {e}', file=sys.stderr)
+ print('Please ensure TensorFlow is properly installed.', file=sys.stderr)
+ sys.exit(1)
+ except Exception as e:
+ print(f'Fatal error during TensorFlow import: {e}', file=sys.stderr)
+ print('This may be due to missing system libraries or incompatible versions.',
+ file=sys.stderr)
+ sys.exit(1)
+
+
+def _ensure_tensorflow():
+ global _tensorflow, _Dense, _Input, _MeanSquaredError, _load_model, _Sequential, _Adam
+ if _tensorflow is None:
+ (_tensorflow,
+ _Dense,
+ _Input,
+ _MeanSquaredError,
+ _load_model,
+ _Sequential,
+ _Adam) = _import_tensorflow()
+
+def _create_dqn_metric_class():
+ _ensure_tensorflow()
+ base_class = _tensorflow.keras.metrics.Metric
-class DQNMetric(tensorflow.keras.metrics.Metric):
+ class DQNMetric(base_class):
- def __init__(self, name='dqn_metric'):
- super(DQNMetric, self).__init__(name=name)
- self.loss = self.add_weight(name='loss', initializer='zeros')
- self.episode_step = self.add_weight(name='step', initializer='zeros')
+ def __init__(self, name='dqn_metric'):
+ super(DQNMetric, self).__init__(name=name)
+ self.loss = self.add_weight(name='loss', initializer='zeros')
+ self.episode_step = self.add_weight(name='step', initializer='zeros')
- def update_state(self, y_true, y_pred=0, sample_weight=None):
- self.loss.assign_add(y_true)
- self.episode_step.assign_add(1)
+ def update_state(self, y_true, y_pred=0, sample_weight=None):
+ self.loss.assign_add(y_true)
+ self.episode_step.assign_add(1)
- def result(self):
- return self.loss / self.episode_step
+ def result(self):
+ return self.loss / self.episode_step
- def reset_states(self):
- self.loss.assign(0)
- self.episode_step.assign(0)
+ def reset_states(self):
+ self.loss.assign(0)
+ self.episode_step.assign(0)
+
+ return DQNMetric
class DQNAgent(Node):
- def __init__(self, stage_num, max_training_episodes):
+ def __init__(self):
super().__init__('dqn_agent')
+ self.declare_parameter('epsilon_decay', 6000)
+ self.declare_parameter('max_training_episodes', 1000)
+ self.declare_parameter('model_file', '')
+ self.declare_parameter('use_gpu', False)
+ self.declare_parameter('verbose', True)
+ self.max_training_episodes = self.get_parameter(
+ 'max_training_episodes'
+ ).get_parameter_value().integer_value
+ model_file = self.get_parameter('model_file').get_parameter_value().string_value
+ use_gpu = self.get_parameter('use_gpu').get_parameter_value().bool_value
+ self.verbose = self.get_parameter('verbose').get_parameter_value().bool_value
+
+ DQNMetric = _create_dqn_metric_class()
+ _ensure_tensorflow()
+ self.tf = _tensorflow
+ self.Dense = _Dense
+ self.Input = _Input
+ self.MeanSquaredError = _MeanSquaredError
+ self.load_model = _load_model
+ self.Sequential = _Sequential
+ self.Adam = _Adam
+
+ if not use_gpu:
+ self.tf.config.set_visible_devices([], 'GPU')
- self.stage = int(stage_num)
self.train_mode = True
self.state_size = 26
self.action_size = 5
- self.max_training_episodes = int(max_training_episodes)
self.done = False
self.succeed = False
@@ -86,7 +153,9 @@ def __init__(self, stage_num, max_training_episodes):
self.learning_rate = 0.0007
self.epsilon = 1.0
self.step_counter = 0
- self.epsilon_decay = 6000 * self.stage
+ self.epsilon_decay = self.get_parameter(
+ 'epsilon_decay'
+ ).get_parameter_value().integer_value
self.epsilon_min = 0.05
self.batch_size = 128
@@ -94,39 +163,45 @@ def __init__(self, stage_num, max_training_episodes):
self.min_replay_memory_size = 5000
self.model = self.create_qnetwork()
- self.target_model = self.create_qnetwork()
- self.update_target_model()
- self.update_target_after = 5000
- self.target_update_after_counter = 0
-
- self.load_model = False
+ self.use_pretrained_model = bool(model_file)
self.load_episode = 0
self.model_dir_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
'saved_model'
)
- self.model_path = os.path.join(
+ model_path = os.path.join(
self.model_dir_path,
- 'stage' + str(self.stage) + '_episode' + str(self.load_episode) + '.h5'
+ model_file
)
+ if self.use_pretrained_model:
+ self.model.set_weights(self.load_model(model_path).get_weights())
+ json_path = model_path.replace('.h5', '.json')
+ if os.path.exists(json_path):
+ with open(json_path) as outfile:
+ param = json.load(outfile)
+ self.epsilon = param.get('epsilon', self.epsilon)
+ self.step_counter = param.get('step_counter', self.step_counter)
+ self.load_episode = param.get('trained_episodes', self.load_episode)
+ if self.load_episode >= self.max_training_episodes:
+ self.get_logger().error('Loaded model episode exceeds max training episodes.')
+ raise ValueError('Loaded model episode exceeds max training episodes.')
+ else:
+ self.get_logger().warn(
+ f'JSON file not found for {model_file}, using default values.'
+ )
- if self.load_model:
- self.model.set_weights(load_model(self.model_path).get_weights())
- with open(os.path.join(
- self.model_dir_path,
- 'stage' + str(self.stage) + '_episode' + str(self.load_episode) + '.json'
- )) as outfile:
- param = json.load(outfile)
- self.epsilon = param.get('epsilon')
- self.step_counter = param.get('step_counter')
+ self.target_model = self.create_qnetwork()
+ self.update_target_after = 5000
+ self.target_update_after_counter = 0
+ self.update_target_model()
if LOGGING:
- tensorboard_file_name = current_time + ' dqn_stage' + str(self.stage) + '_reward'
+ tensorboard_file_name = current_time + ' dqn_reward'
home_dir = os.path.expanduser('~')
dqn_reward_log_dir = os.path.join(
home_dir, 'turtlebot3_dqn_logs', 'gradient_tape', tensorboard_file_name
)
- self.dqn_reward_writer = tensorflow.summary.create_file_writer(dqn_reward_log_dir)
+ self.dqn_reward_writer = self.tf.summary.create_file_writer(dqn_reward_log_dir)
self.dqn_reward_metric = DQNMetric()
self.rl_agent_interface_client = self.create_client(Dqn, 'rl_agent_interface')
@@ -156,7 +231,7 @@ def process(self):
while True:
local_step += 1
- q_values = self.model.predict(state)
+ q_values = self.model.predict(state, verbose=self.verbose)
sum_max_q += float(numpy.max(q_values))
action = int(self.get_action(state))
@@ -183,7 +258,7 @@ def process(self):
if LOGGING:
self.dqn_reward_metric.update_state(score)
with self.dqn_reward_writer.as_default():
- tensorflow.summary.scalar(
+ self.tf.summary.scalar(
'dqn_reward', self.dqn_reward_metric.result(), step=episode_num
)
self.dqn_reward_metric.reset_states()
@@ -194,8 +269,8 @@ def process(self):
'memory length:', len(self.replay_memory),
'epsilon:', self.epsilon)
- param_keys = ['epsilon', 'step']
- param_values = [self.epsilon, self.step_counter]
+ param_keys = ['epsilon', 'step_counter', 'trained_episodes']
+ param_values = [self.epsilon, self.step_counter, episode]
param_dictionary = dict(zip(param_keys, param_values))
break
@@ -203,17 +278,21 @@ def process(self):
if self.train_mode:
if episode % 100 == 0:
- self.model_path = os.path.join(
- self.model_dir_path,
- 'stage' + str(self.stage) + '_episode' + str(episode) + '.h5')
- self.model.save(self.model_path)
- with open(
- os.path.join(
+ idx = 1
+ while True:
+ model_path = os.path.join(
+ self.model_dir_path,
+ f'model{idx}.h5'
+ )
+ json_path = os.path.join(
self.model_dir_path,
- 'stage' + str(self.stage) + '_episode' + str(episode) + '.json'
- ),
- 'w'
- ) as outfile:
+ f'model{idx}.json'
+ )
+ if not os.path.exists(model_path):
+ break
+ idx += 1
+ self.model.save(model_path)
+ with open(json_path, 'w') as outfile:
json.dump(param_dictionary, outfile)
def env_make(self):
@@ -251,9 +330,9 @@ def get_action(self, state):
if lucky > (1 - self.epsilon):
result = random.randint(0, self.action_size - 1)
else:
- result = numpy.argmax(self.model.predict(state))
+ result = numpy.argmax(self.model.predict(state, verbose=self.verbose))
else:
- result = numpy.argmax(self.model.predict(state))
+ result = numpy.argmax(self.model.predict(state, verbose=self.verbose))
return result
@@ -280,13 +359,15 @@ def step(self, action):
return next_state, reward, done
def create_qnetwork(self):
- model = Sequential()
- model.add(Input(shape=(self.state_size,)))
- model.add(Dense(512, activation='relu'))
- model.add(Dense(256, activation='relu'))
- model.add(Dense(128, activation='relu'))
- model.add(Dense(self.action_size, activation='linear'))
- model.compile(loss=MeanSquaredError(), optimizer=Adam(learning_rate=self.learning_rate))
+ model = self.Sequential()
+ model.add(self.Input(shape=(self.state_size,)))
+ model.add(self.Dense(512, activation='relu'))
+ model.add(self.Dense(256, activation='relu'))
+ model.add(self.Dense(128, activation='relu'))
+ model.add(self.Dense(self.action_size, activation='linear'))
+ model.compile(
+ loss=self.MeanSquaredError(),
+ optimizer=self.Adam(learning_rate=self.learning_rate))
model.summary()
return model
@@ -306,11 +387,11 @@ def train_model(self, terminal):
current_states = numpy.array([transition[0] for transition in data_in_mini_batch])
current_states = current_states.squeeze()
- current_qvalues_list = self.model.predict(current_states)
+ current_qvalues_list = self.model.predict(current_states, verbose=self.verbose)
next_states = numpy.array([transition[3] for transition in data_in_mini_batch])
next_states = next_states.squeeze()
- next_qvalues_list = self.target_model.predict(next_states)
+ next_qvalues_list = self.target_model.predict(next_states, verbose=self.verbose)
x_train = []
y_train = []
@@ -334,8 +415,8 @@ def train_model(self, terminal):
y_train = numpy.reshape(y_train, [len(data_in_mini_batch), self.action_size])
self.model.fit(
- tensorflow.convert_to_tensor(x_train, tensorflow.float32),
- tensorflow.convert_to_tensor(y_train, tensorflow.float32),
+ self.tf.convert_to_tensor(x_train, self.tf.float32),
+ self.tf.convert_to_tensor(y_train, self.tf.float32),
batch_size=self.batch_size, verbose=0
)
self.target_update_after_counter += 1
@@ -345,13 +426,9 @@ def train_model(self, terminal):
def main(args=None):
- if args is None:
- args = sys.argv
- stage_num = args[1] if len(args) > 1 else '1'
- max_training_episodes = args[2] if len(args) > 2 else '1000'
rclpy.init(args=args)
- dqn_agent = DQNAgent(stage_num, max_training_episodes)
+ dqn_agent = DQNAgent()
rclpy.spin(dqn_agent)
dqn_agent.destroy_node()
diff --git a/turtlebot3_dqn/turtlebot3_dqn/dqn_test.py b/turtlebot3_dqn/turtlebot3_dqn/dqn_test.py
index d7742a0..4ead1ad 100755
--- a/turtlebot3_dqn/turtlebot3_dqn/dqn_test.py
+++ b/turtlebot3_dqn/turtlebot3_dqn/dqn_test.py
@@ -15,7 +15,7 @@
# limitations under the License.
#################################################################################
#
-# Authors: Ryan Shim, Gilbert, ChanHyeong Lee
+# Authors: Ryan Shim, Gilbert, ChanHyeong Lee, Hyungyu Kim
import collections
import os
@@ -25,22 +25,80 @@
import numpy
import rclpy
from rclpy.node import Node
-from tensorflow.keras.layers import Dense
-from tensorflow.keras.losses import MeanSquaredError
-from tensorflow.keras.models import load_model
-from tensorflow.keras.models import Sequential
-from tensorflow.keras.optimizers import RMSprop
from turtlebot3_msgs.srv import Dqn
+_tensorflow = None
+_Dense = None
+_MeanSquaredError = None
+_load_model = None
+_Sequential = None
+_RMSprop = None
+
+
+def _import_tensorflow():
+ """Lazy import TensorFlow and Keras modules."""
+ try:
+ import tensorflow
+ from tensorflow.keras.layers import Dense
+ from tensorflow.keras.losses import MeanSquaredError
+ from tensorflow.keras.models import load_model
+ from tensorflow.keras.models import Sequential
+ from tensorflow.keras.optimizers import RMSprop
+ return (
+ tensorflow,
+ Dense,
+ MeanSquaredError,
+ load_model,
+ Sequential,
+ RMSprop
+ )
+ except ImportError as e:
+ print(f'Error importing TensorFlow: {e}', file=sys.stderr)
+ print('Please ensure TensorFlow is properly installed.', file=sys.stderr)
+ sys.exit(1)
+ except Exception as e:
+ print(f'Fatal error during TensorFlow import: {e}', file=sys.stderr)
+ print(
+ 'This may be due to missing system libraries or incompatible versions.',
+ file=sys.stderr)
+ sys.exit(1)
+
+
+def _ensure_tensorflow():
+ """Ensure TensorFlow is imported and stored in global variables."""
+ global _tensorflow, _Dense, _MeanSquaredError, _load_model, _Sequential, _RMSprop
+ if _tensorflow is None:
+ (_tensorflow,
+ _Dense,
+ _MeanSquaredError,
+ _load_model,
+ _Sequential,
+ _RMSprop) = _import_tensorflow()
+
class DQNTest(Node):
- def __init__(self, stage, load_episode):
+ def __init__(self):
super().__init__('dqn_test')
-
- self.stage = int(stage)
- self.load_episode = int(load_episode)
+ self.declare_parameter('model_file', '')
+ self.declare_parameter('use_gpu', False)
+ self.declare_parameter('verbose', True)
+ model_file = self.get_parameter('model_file').get_parameter_value().string_value
+ use_gpu = self.get_parameter('use_gpu').get_parameter_value().bool_value
+ self.verbose = self.get_parameter('verbose').get_parameter_value().bool_value
+
+ # Lazy import TensorFlow and store as instance variables
+ _ensure_tensorflow()
+ self.tf = _tensorflow
+ self.Dense = _Dense
+ self.MeanSquaredError = _MeanSquaredError
+ self.load_model = _load_model
+ self.Sequential = _Sequential
+ self.RMSprop = _RMSprop
+
+ if not use_gpu:
+ self.tf.config.set_visible_devices([], 'GPU')
self.state_size = 26
self.action_size = 5
@@ -51,11 +109,11 @@ def __init__(self, stage, load_episode):
model_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
'saved_model',
- f'stage{self.stage}_episode{self.load_episode}.h5'
+ model_file
)
- loaded_model = load_model(
- model_path, compile=False, custom_objects={'mse': MeanSquaredError()}
+ loaded_model = self.load_model(
+ model_path, compile=False, custom_objects={'mse': self.MeanSquaredError()}
)
self.model.set_weights(loaded_model.get_weights())
@@ -64,21 +122,25 @@ def __init__(self, stage, load_episode):
self.run_test()
def build_model(self):
- model = Sequential()
- model.add(Dense(
+ model = self.Sequential()
+ model.add(self.Dense(
512, input_shape=(self.state_size,),
activation='relu',
kernel_initializer='lecun_uniform'
))
- model.add(Dense(256, activation='relu', kernel_initializer='lecun_uniform'))
- model.add(Dense(128, activation='relu', kernel_initializer='lecun_uniform'))
- model.add(Dense(self.action_size, activation='linear', kernel_initializer='lecun_uniform'))
- model.compile(loss=MeanSquaredError(), optimizer=RMSprop(learning_rate=0.00025))
+ model.add(self.Dense(256, activation='relu', kernel_initializer='lecun_uniform'))
+ model.add(self.Dense(128, activation='relu', kernel_initializer='lecun_uniform'))
+ model.add(
+ self.Dense(
+ self.action_size,
+ activation='linear',
+ kernel_initializer='lecun_uniform'))
+ model.compile(loss=self.MeanSquaredError(), optimizer=self.RMSprop(learning_rate=0.00025))
return model
def get_action(self, state):
state = numpy.asarray(state)
- q_values = self.model.predict(state.reshape(1, -1), verbose=0)
+ q_values = self.model.predict(state.reshape(1, -1), verbose=self.verbose)
return int(numpy.argmax(q_values[0]))
def run_test(self):
@@ -120,9 +182,7 @@ def run_test(self):
def main(args=None):
rclpy.init(args=args if args else sys.argv)
- stage = sys.argv[1] if len(sys.argv) > 1 else '1'
- load_episode = sys.argv[2] if len(sys.argv) > 2 else '600'
- node = DQNTest(stage, load_episode)
+ node = DQNTest()
try:
rclpy.spin(node)
diff --git a/turtlebot3_machine_learning/CHANGELOG.rst b/turtlebot3_machine_learning/CHANGELOG.rst
index 6013291..822a160 100644
--- a/turtlebot3_machine_learning/CHANGELOG.rst
+++ b/turtlebot3_machine_learning/CHANGELOG.rst
@@ -2,6 +2,17 @@
Changelog for package turtlebot3_machine_learning
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1.0.2 (2026-01-06)
+------------------
+* Fixed a bug in the JSON file where the step parameter was incorrectly named; renamed it to step_counter.
+* Changed the system arguments to be passed as ROS parameters for execution.
+* Added a use_gpu parameter to allow selection of whether to use GPU.
+* Added a model_file parameter to enable loading an existing trained model and continuing training.
+* Renamed the load_model variable to use_pretrained_model for clarity.
+* Changed model_path from a class variable to a local variable.
+* Introduced lazy import for TensorFlow modules.
+* Contributors: Hyungyu Kim
+
1.0.1 (2025-05-02)
------------------
* Support for ROS 2 Jazzy version
diff --git a/turtlebot3_machine_learning/package.xml b/turtlebot3_machine_learning/package.xml
index 29e15a8..3e71461 100644
--- a/turtlebot3_machine_learning/package.xml
+++ b/turtlebot3_machine_learning/package.xml
@@ -1,7 +1,7 @@
turtlebot3_machine_learning
- 1.0.1
+ 1.0.2
This metapackage for ROS 2 TurtleBot3 machine learning.
@@ -12,6 +12,7 @@
https://github.com/ROBOTIS-GIT/turtlebot3_machine_learning/issues
Gilbert
ChanHyeong Lee
+ Hyungyu Kim
turtlebot3_dqn
ament_cmake