diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index a510eb91b..a99604c0e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -37,13 +37,13 @@ jobs:
submodules: recursive
path: catkin_ws/src/mil
- name: Setup ROS Noetic
- uses: ros-tooling/setup-ros@v0.3
+ uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: noetic
- name: Install pip dependencies
run: |
cd $GITHUB_WORKSPACE/catkin_ws/src/mil
- pip install -r requirements.txt
+ pip3 install -r requirements.txt
# We want to run a full test suite in CI - this includes the BlueView
# tests!
- name: Install BlueView Sonar SDK
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d2f0e2009..5db81ae41 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,15 +15,15 @@ repos:
hooks:
- id: yamllint
- repo: https://github.com/psf/black
- rev: 23.3.0
+ rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v16.0.4
+ rev: v16.0.6
hooks:
- id: clang-format
- repo: https://github.com/PyCQA/autoflake
- rev: v2.1.1
+ rev: v2.2.1
hooks:
- id: autoflake
args: [--remove-all-unused-imports, --ignore-init-module-imports]
@@ -34,18 +34,18 @@ repos:
exclude: ^docker|deprecated|NaviGator/simulation/VRX
args: [--severity=warning, --exclude=SC1090]
- repo: https://github.com/scop/pre-commit-shfmt
- rev: v3.6.0-2
+ rev: v3.7.0-1
hooks:
- id: shfmt
exclude: ^docker|deprecated|NaviGator/simulation/VRX
- - repo: https://github.com/charliermarsh/ruff-pre-commit
+ - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: 'v0.0.270'
+ rev: 'v0.0.287'
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.4
+ rev: v2.2.5
hooks:
- id: codespell
args:
diff --git a/NaviGator/gnc/navigator_msg_multiplexer/nodes/ogrid_arbiter.py b/NaviGator/gnc/navigator_msg_multiplexer/nodes/ogrid_arbiter.py
index ac8fffd4f..eb425ec7a 100755
--- a/NaviGator/gnc/navigator_msg_multiplexer/nodes/ogrid_arbiter.py
+++ b/NaviGator/gnc/navigator_msg_multiplexer/nodes/ogrid_arbiter.py
@@ -493,10 +493,7 @@ def publish(self, timer_event):
)
print(e)
fprint(
- "w: {}, h: {}".format(
- global_ogrid.info.width,
- global_ogrid.info.height,
- ),
+ f"w: {global_ogrid.info.width}, h: {global_ogrid.info.height}",
msg_color="red",
)
diff --git a/NaviGator/gnc/navigator_thrust_mapper/navigator_thrust_mapper/thruster_map.py b/NaviGator/gnc/navigator_thrust_mapper/navigator_thrust_mapper/thruster_map.py
index 588679455..2fc19700a 100644
--- a/NaviGator/gnc/navigator_thrust_mapper/navigator_thrust_mapper/thruster_map.py
+++ b/NaviGator/gnc/navigator_thrust_mapper/navigator_thrust_mapper/thruster_map.py
@@ -166,15 +166,11 @@ def from_urdf(
if find != -1 and find + len(transmission_suffix) == len(transmission.name):
if len(transmission.joints) != 1:
raise Exception(
- "Transmission {} does not have 1 joint".format(
- transmission.name,
- ),
+ f"Transmission {transmission.name} does not have 1 joint",
)
if len(transmission.actuators) != 1:
raise Exception(
- "Transmission {} does not have 1 actuator".format(
- transmission.name,
- ),
+ f"Transmission {transmission.name} does not have 1 actuator",
)
t_ratio = transmission.actuators[0].mechanicalReduction
@@ -195,9 +191,7 @@ def from_urdf(
joint = t_joint
if joint is None:
rospy.logerr(
- "Transmission joint {} not found".format(
- transmission.joints[0].name,
- ),
+ f"Transmission joint {transmission.joints[0].name} not found",
)
try:
trans = buff.lookup_transform(
@@ -217,9 +211,7 @@ def from_urdf(
joints.append(joint.name)
if limit != -1 and joint.limit.effort != limit:
raise Exception(
- "Thruster {} had a different limit, cannot proceed".format(
- joint.name,
- ),
+ f"Thruster {joint.name} had a different limit, cannot proceed",
)
limit = joint.limit.effort
limit_tuple = (limit, -limit)
diff --git a/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/kill.py b/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/kill.py
index a3f7a612b..547c56fa8 100644
--- a/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/kill.py
+++ b/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/kill.py
@@ -28,10 +28,7 @@ def _online_bagger_cb(self, status, result):
rospy.loginfo(f"KILL BAG WRITTEN TO {result.filename}")
else:
rospy.logwarn(
- "KILL BAG {}, status: {}".format(
- TerminalState.to_string(status),
- result.status,
- ),
+ f"KILL BAG {TerminalState.to_string(status)}, status: {result.status}",
)
def _kill_task_cb(self, status, result):
@@ -39,10 +36,7 @@ def _kill_task_cb(self, status, result):
rospy.loginfo("Killed task success!")
return
rospy.logwarn(
- "Killed task failed ({}): {}".format(
- TerminalState.to_string(status),
- result.result,
- ),
+ f"Killed task failed ({TerminalState.to_string(status)}): {result.result}",
)
def raised(self, alarm):
diff --git a/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/thruster_fault.py b/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/thruster_fault.py
index a89612632..ce89ac3a0 100644
--- a/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/thruster_fault.py
+++ b/NaviGator/mission_control/navigator_alarm/navigator_alarm_handlers/thruster_fault.py
@@ -65,9 +65,7 @@ def _check_faults(self, msg, topic):
return
self.broadcaster.raise_alarm(
severity=5,
- problem_description="{} thrusters have faults".format(
- len(self._raised_alarms),
- ),
+ problem_description=f"{len(self._raised_alarms)} thrusters have faults",
parameters={
t: self._get_fault_codes(k) for t, k in self._raised_alarms.items()
},
diff --git a/NaviGator/mission_control/navigator_missions/navigator_missions/detect_deliver_2016.py b/NaviGator/mission_control/navigator_missions/navigator_missions/detect_deliver_2016.py
index bc95612b0..28c3ac4d9 100644
--- a/NaviGator/mission_control/navigator_missions/navigator_missions/detect_deliver_2016.py
+++ b/NaviGator/mission_control/navigator_missions/navigator_missions/detect_deliver_2016.py
@@ -240,10 +240,7 @@ async def search_sides(self, moves):
self.shape_pose = found_pose
return
fprint(
- "Saw (Shape={}, Color={}) on this side".format(
- shape_color[0],
- shape_color[1],
- ),
+ f"Saw (Shape={shape_color[0]}, Color={shape_color[1]}) on this side",
title="DETECT DELIVER",
msg_color="green",
)
@@ -287,10 +284,7 @@ def select_backup_shape(self):
self.shape_pose = point_normal
if self.Shape == shape or self.Color == color:
fprint(
- "Correct shape not found, resorting to shape={} color={}".format(
- shape,
- color,
- ),
+ f"Correct shape not found, resorting to shape={shape} color={color}",
title="DETECT DELIVER",
msg_color="yellow",
)
@@ -430,9 +424,7 @@ async def shoot_and_align_forest(self):
move = await self.align_to_target()
if move.failure_reason != "":
fprint(
- "Error Aligning with target = {}. Ending mission :(".format(
- move.failure_reason,
- ),
+ f"Error Aligning with target = {move.failure_reason}. Ending mission :(",
title="DETECT DELIVER",
msg_color="red",
)
@@ -479,9 +471,7 @@ async def shoot_and_align(self):
move = await self.align_to_target()
if move.failure_reason != "":
fprint(
- "Error Aligning with target = {}. Ending mission :(".format(
- move.failure_reason,
- ),
+ f"Error Aligning with target = {move.failure_reason}. Ending mission :(",
title="DETECT DELIVER",
msg_color="red",
)
diff --git a/NaviGator/mission_control/navigator_missions/navigator_missions/identify_dock.py b/NaviGator/mission_control/navigator_missions/navigator_missions/identify_dock.py
index 28419f6b6..6d2203826 100644
--- a/NaviGator/mission_control/navigator_missions/navigator_missions/identify_dock.py
+++ b/NaviGator/mission_control/navigator_missions/navigator_missions/identify_dock.py
@@ -150,10 +150,7 @@ async def check_bays(self):
def update_shape(self, shape_res, normal_res, tf):
print_good(
- "Found (Shape={}, Color={} in a bay".format(
- shape_res.Shape,
- shape_res.Color,
- ),
+ f"Found (Shape={shape_res.Shape}, Color={shape_res.Color} in a bay",
)
self.identified_shapes[(shape_res.Shape, shape_res.Color)] = self.get_shape_pos(
normal_res,
diff --git a/NaviGator/mission_control/navigator_missions/navigator_missions/move.py b/NaviGator/mission_control/navigator_missions/navigator_missions/move.py
index 03017c09b..8ae428057 100755
--- a/NaviGator/mission_control/navigator_missions/navigator_missions/move.py
+++ b/NaviGator/mission_control/navigator_missions/navigator_missions/move.py
@@ -149,9 +149,7 @@ async def run(self, args):
"yl": "yaw_left",
"yr": "yaw_right",
}
- command = (
- command if command not in shorthand.keys() else shorthand[command]
- )
+ command = command if command not in shorthand else shorthand[command]
movement = getattr(self.move, command)
trans_move = command[:3] != "yaw"
diff --git a/NaviGator/mission_control/navigator_missions/navigator_missions/navigation.py b/NaviGator/mission_control/navigator_missions/navigator_missions/navigation.py
index 9b977b0bf..7812e5108 100644
--- a/NaviGator/mission_control/navigator_missions/navigator_missions/navigation.py
+++ b/NaviGator/mission_control/navigator_missions/navigator_missions/navigation.py
@@ -187,9 +187,7 @@ async def explore_closest_until(self, is_done, filter_and_sort):
)
if classification_index != -1:
self.send_feedback(
- "{} identified. Canceling investigation".format(
- move_id_tuple[1],
- ),
+ f"{move_id_tuple[1]} identified. Canceling investigation",
)
move_task.cancel()
diff --git a/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_navigation.py b/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_navigation.py
index 3086e39b5..7ef98c064 100644
--- a/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_navigation.py
+++ b/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_navigation.py
@@ -151,9 +151,7 @@ async def explore_closest_until(self, is_done, filter_and_sort):
)
if classification_index != -1:
self.send_feedback(
- "{} identified. Canceling investigation".format(
- move_id_tuple[1],
- ),
+ f"{move_id_tuple[1]} identified. Canceling investigation",
)
move_id_tuple[0].cancel()
await self.nh.sleep(1.0)
diff --git a/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_perception.py b/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_perception.py
index f1483416f..2b1ef6d50 100644
--- a/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_perception.py
+++ b/NaviGator/mission_control/navigator_missions/vrx_missions/vrx_perception.py
@@ -79,11 +79,7 @@ async def run(self, parameters):
)
elif objects[key] != new_objects[key]:
self.send_feedback(
- "{} changed from {} to {}".format(
- key,
- objects[key],
- new_objects[key],
- ),
+ f"{key} changed from {objects[key]} to {new_objects[key]}",
)
await self.announce_object(
key,
diff --git a/NaviGator/simulation/navigator_gazebo/test/test_sim_integration.py b/NaviGator/simulation/navigator_gazebo/test/test_sim_integration.py
index 3cb7ab8a7..46e20b856 100755
--- a/NaviGator/simulation/navigator_gazebo/test/test_sim_integration.py
+++ b/NaviGator/simulation/navigator_gazebo/test/test_sim_integration.py
@@ -71,10 +71,7 @@ def test_odom(self):
rospy.sleep(0.1)
self.assertTrue(
len(self.odom_pos_msg) == 3 and len(self.odom_ori_msg) == 4,
- msg="POS, ORI: {}, {}".format(
- len(self.odom_pos_msg),
- len(self.odom_ori_msg),
- ),
+ msg=f"POS, ORI: {len(self.odom_pos_msg)}, {len(self.odom_ori_msg)}",
)
initial_pos = [-1.2319, 0.0, 0.0]
initial_ori = [0.0, 0.0, 0.0, 1.0]
@@ -100,10 +97,7 @@ def test_absodom(self):
rospy.sleep(0.1)
self.assertTrue(
len(self.absodom_pos_msg) == 3 and len(self.absodom_ori_msg) == 4,
- msg="POS, ORI: {}, {}".format(
- len(self.absodom_pos_msg),
- len(self.absodom_ori_msg),
- ),
+ msg=f"POS, ORI: {len(self.absodom_pos_msg)}, {len(self.absodom_ori_msg)}",
)
initial_pos = [743789.637462, -5503821.36715, 3125622.10477]
initial_ori = [0.0, 0.0, 0.0, 1.0]
@@ -122,25 +116,13 @@ def verify_pos_ori(self, pos, initial_pos, ori, initial_ori, topic):
actual,
initial,
places=0,
- msg=(
- "Error: {} position is: {} should be {}".format(
- topic,
- actual,
- initial,
- )
- ),
+ msg=(f"Error: {topic} position is: {actual} should be {initial}"),
)
for actual, initial in zip(ori, initial_ori):
self.assertEqual(
actual,
initial,
- msg=(
- "Error: {} orientation is: {} should be {}".format(
- topic,
- actual,
- initial,
- )
- ),
+ msg=(f"Error: {topic} orientation is: {actual} should be {initial}"),
)
def cam_info_right_cb(self, msg):
@@ -229,10 +211,7 @@ def verify_not_empty(self, data_lists, num_topics):
self.assertEqual(
len(data_lists),
num_topics,
- msg="Number of topics is {}, should be {}".format(
- len(data_lists),
- num_topics,
- ),
+ msg=f"Number of topics is {len(data_lists)}, should be {num_topics}",
)
for data_list in data_lists:
self.assertNotEqual(len(data_list), 0, msg="data is empty")
@@ -243,13 +222,7 @@ def verify_info(self, res_info, initial_info, topic):
self.assertNotEqual(
actual_dim,
initial_dim,
- msg=(
- "Error: {} is: {} shouldn't be {}".format(
- topic,
- actual_dim,
- initial_dim,
- )
- ),
+ msg=(f"Error: {topic} is: {actual_dim} shouldn't be {initial_dim}"),
)
diff --git a/NaviGator/utils/navigator_robotx_comms/nodes/robotx_comms_client.py b/NaviGator/utils/navigator_robotx_comms/nodes/robotx_comms_client.py
index d8583f010..59e6139e3 100755
--- a/NaviGator/utils/navigator_robotx_comms/nodes/robotx_comms_client.py
+++ b/NaviGator/utils/navigator_robotx_comms/nodes/robotx_comms_client.py
@@ -559,10 +559,7 @@ def connect(self) -> None:
"""
if not self.connected:
rospy.loginfo(
- "Attempting Connection to TD Server at {}:{}".format(
- self.tcp_ip,
- self.tcp_port,
- ),
+ f"Attempting Connection to TD Server at {self.tcp_ip}:{self.tcp_port}",
)
while not self.connected and not rospy.is_shutdown():
# recreate socket
diff --git a/NaviGator/utils/navigator_tools/navigator_tools/object_database_helper.py b/NaviGator/utils/navigator_tools/navigator_tools/object_database_helper.py
index 0798c78e0..5cb8762cb 100644
--- a/NaviGator/utils/navigator_tools/navigator_tools/object_database_helper.py
+++ b/NaviGator/utils/navigator_tools/navigator_tools/object_database_helper.py
@@ -1,4 +1,3 @@
-"""Use the DBHelper class to interface with the Database without having to deal with ROS things."""
import asyncio
import sys
import time
@@ -14,10 +13,29 @@
class DBHelper:
- """DBHelper class."""
+ """
+ Use the DBHelper class to interface with the Database without having to deal with ROS things.
+
+ Attributes:
+ found (bool): checks whether the object is what the user has been looking for.
+ nh (axros.NodeHandle): processes database requests.
+ position (int): the current position of the object.
+ rot (): one of the dimensional values.
+ new_object_subscriber (): an object that is being called from the database.
+ ensuring_objects (bool): a variable state for the object.
+ ensuring_object_dep (): a list of objects being used for storage.
+ ensuring_object_cb (): this is considered when it is an "ensuring_objects".
+ looking_for (): a setter variable to which a name is being assigned.
+ is_found (bool): determines whether the object is found or not.
+ """
def __init__(self, nh):
- """Initialize the DB helper class."""
+ """
+ Initialize the DB helper class.
+
+ Args:
+ nh(NodeHandle): NodeHandle object
+ """
self.found = set()
self.nh = nh
self.position = None
@@ -30,7 +48,12 @@ def __init__(self, nh):
self.is_found = False
async def init_(self, navigator=None):
- """Initialize the axros parts of the DBHelper."""
+ """
+ Initialize the axros parts of the DBHelper.
+
+ Args:
+ navigator (NaviGator | None): Base NaviGator object
+ """
# self._sub_database = yield self.nh.subscribe('/database/objects', PerceptionObjectArray, self.object_cb)
self._database = self.nh.get_service_client("/database/requests", ObjectDBQuery)
self.navigator = navigator
@@ -43,21 +66,37 @@ async def init_(self, navigator=None):
return self
def _odom_cb(self, odom):
+ """
+ Sets the position and the rot values.
+
+ Args:
+ odom (object): Odom
+ """
self.position, self.rot = nt.odometry_to_numpy(odom)[0]
async def get_object_by_id(self, my_id):
+ """
+ Retrieves the object with the associated "my_id".
+
+ Args:
+ my_id (int): an "id" of the object is passed in.
+
+ Returns:
+ An individual object is being returned with a unique id.
+ """
print(my_id)
req = ObjectDBQueryRequest()
req.name = "all"
resp = await self._database(req)
- ans = [obj for obj in resp.objects if obj.id == my_id][0]
+ ans = next(obj for obj in resp.objects if obj.id == my_id)
return ans
async def begin_observing(self, cb):
"""
Get notified when a new object is added to the database.
- cb : The callback that is called when a new object is added to the database
+ Args:
+ cb : The callback that is called when a new object is added to the database.
"""
self.new_object_subscriber = cb
req = ObjectDBQueryRequest()
@@ -78,6 +117,12 @@ async def begin_observing(self, cb):
self.new_object_subscriber(o)
async def get_unknown_and_low_conf(self):
+ """
+ Gets unknown objects that are of low confidence.
+
+ Returns:
+ Objects that meet certain criteria.
+ """
req = ObjectDBQueryRequest()
req.name = "all"
resp = await self._database(req)
@@ -91,9 +136,21 @@ async def get_unknown_and_low_conf(self):
return m
def set_looking_for(self, name):
+ """
+ Setting to a name that is being looked for.
+
+ Args:
+ name (string): name of the object.
+ """
self.looking_for = name
- def is_found_func(self):
+ def is_found_func(self) -> bool:
+ """
+ Determines whether the object is being found or not.
+
+ Returns:
+ The existence of the object is returned.
+ """
if self.is_found:
self.looking_for = None
self.is_found = False
@@ -101,7 +158,12 @@ def is_found_func(self):
return False
def object_cb(self, perception_objects):
- """Callback for the object database."""
+ """
+ Callback for the object database.
+
+ Args:
+ perception_objects (object): a list of objects that are passed.
+ """
self.total_num = len(perception_objects.objects)
for o in perception_objects.objects:
if o.name == self.looking_for:
@@ -120,12 +182,23 @@ def object_cb(self, perception_objects):
self.ensuring_object_cb(missings_objs)
def remove_found(self, name):
- """Remove an object that has been listed as found."""
+ """
+ Remove an object that has been listed as found.
+
+ Args:
+ name (string): a name of the object is being passed in.
+ """
self.found.remove(name)
def ensure_object_permanence(self, object_dep, cb):
- """Ensure that all the objects in the object_dep list remain in the database.
- Call the callback if this isn't true."""
+ """
+ Ensure that all the objects in the object_dep list remain in the database.
+ Call the callback if this isn't true.
+
+ Args:
+ object_dep: a new object is passed in order to be set.
+ cb : The callback that is called when a new object is added to the database.
+ """
if object_dep is None or cb is None:
return
self.ensuring_objects = True
@@ -133,10 +206,21 @@ def ensure_object_permanence(self, object_dep, cb):
self.ensuring_object_dep = object_dep
def stop_ensuring_object_permanence(self):
- """Stop ensuring that objects remain in the database."""
+ """
+ Stop ensuring that objects remain in the database.
+ """
self.ensuring_objects = False
- def _wait_for_position(self, timeout=10):
+ def _wait_for_position(self, timeout=10) -> bool:
+ """
+ A possible position is being stored in a variable.
+
+ Args:
+ timeout (int): time limit to retrieve something.
+
+ Returns:
+ bool: Determines whether the position is found within the time limit.
+ """
count = 0
while self.position is None:
if self.navigator is not None:
@@ -148,7 +232,15 @@ def _wait_for_position(self, timeout=10):
return True
async def get_closest_object(self, objects):
- """Get the closest mission."""
+ """
+ Get the closest mission.
+
+ Args:
+ objects (object): a list of objects are being passed in.
+
+ Returns:
+ A object with the closest mission / distance is returned.
+ """
pobjs = []
for obj in objects:
req = ObjectDBQueryRequest()
@@ -171,6 +263,15 @@ async def get_closest_object(self, objects):
return min_obj
async def _dist(self, x):
+ """
+ Finds the distance of the object keeping into account its current position.
+
+ Args:
+ x (object): a specific object is being passed in order to retrieve its position.
+
+ Returns:
+ the distance between the two objects is being returned.
+ """
if self.position is None:
success = asyncio.create_task(self._wait_for_position)
if not success:
@@ -188,7 +289,18 @@ async def get_object(
thresh=50,
thresh_strict=50,
):
- """Get an object from the database."""
+ """
+ Get an object from the database.
+
+ Args:
+ object_name (string): the name of the object is passed in.
+ volume_only (bool): determines whether it is volume-based or not.
+ thresh (int): a maximum distance is passed in.
+ thresh_strict(int): a more strict maximum distance is passed in.
+
+ Returns:
+ closest_potential_object (object): returns the object that is within the closest range.
+ """
if volume_only:
req = ObjectDBQueryRequest()
req.name = object_name
@@ -233,6 +345,15 @@ async def get_object(
return closest_potential_object
def wait_for_additional_objects(self, timeout=60):
+ """
+ Returns true/false whether additional objects are being passed in.
+
+ Args:
+ timeout (int): maximum time limit of 60 seconds to run the program.
+
+ Returns:
+ (bool): determines whether additional objects are added.
+ """
num_items = self.num_items
start = time.time()
while timeout < time.time() - start:
@@ -241,14 +362,36 @@ def wait_for_additional_objects(self, timeout=60):
return False
def set_color(self, color, name):
- """Set the color of an object in the database."""
+ """
+ Sets the color of an object in the database.
+
+ Args:
+ color: the new color of the object is passed in.
+ name: the name of the object is passed in.
+ """
raise NotImplementedError
def set_fake_position(self, pos):
- """Set the position of a fake perception object."""
+ """
+ Sets the position of a fake perception object.
+
+ Args:
+ pos (int): the new position of the object is passed in.
+ """
raise NotImplementedError
async def get_objects_in_radius(self, pos, radius, objects="all"):
+ """
+ Retrieves the objects that are present within the radius.
+
+ Args:
+ pos (int): the position of the object is passed in.
+ radius (int): the radius of the object is passed in.
+ objects (object): this is a list of all the objects that needs to be compared.
+
+ Returns:
+ ans (object): returns the objects that are present within the radius.
+ """
req = ObjectDBQueryRequest()
req.name = "all"
resp = await self._database(req)
diff --git a/NaviGator/utils/navigator_tools/nodes/estimated_object_setter.py b/NaviGator/utils/navigator_tools/nodes/estimated_object_setter.py
index 99845b004..57d4b7c01 100755
--- a/NaviGator/utils/navigator_tools/nodes/estimated_object_setter.py
+++ b/NaviGator/utils/navigator_tools/nodes/estimated_object_setter.py
@@ -24,7 +24,7 @@ async def main(name, lla):
name = "_".join(txt.title() for txt in name.split("_"))
point = await convert.request(CoordinateConversionRequest(frame="lla", point=lla))
- await db(ObjectDBQueryRequest(cmd="{}={p[0]}, {p[1]}".format(name, p=point.enu)))
+ await db(ObjectDBQueryRequest(cmd=f"{name}={point.enu[0]}, {point.enu[1]}"))
if __name__ == "__main__":
diff --git a/SubjuGator/command/subjugator_alarm/alarm_handlers/kill.py b/SubjuGator/command/subjugator_alarm/alarm_handlers/kill.py
index 4781a760a..4055694d8 100644
--- a/SubjuGator/command/subjugator_alarm/alarm_handlers/kill.py
+++ b/SubjuGator/command/subjugator_alarm/alarm_handlers/kill.py
@@ -101,10 +101,7 @@ def _bag_done_cb(self, status, result):
rospy.loginfo(f"KILL BAG WRITTEN TO {result.filename}")
else:
rospy.logwarn(
- "KILL BAG {}, status: {}".format(
- TerminalState.to_string(status),
- result.status,
- ),
+ f"KILL BAG {TerminalState.to_string(status)}, status: {result.status}",
)
def bagger_dump(self):
diff --git a/SubjuGator/command/subjugator_alarm/alarm_handlers/odom_kill.py b/SubjuGator/command/subjugator_alarm/alarm_handlers/odom_kill.py
index 4a7638909..90a8e817b 100644
--- a/SubjuGator/command/subjugator_alarm/alarm_handlers/odom_kill.py
+++ b/SubjuGator/command/subjugator_alarm/alarm_handlers/odom_kill.py
@@ -101,9 +101,7 @@ def check_continuity(self, new_odom_msg: Odometry): # True if 'continuous'
if jump > self.MAX_JUMP:
rospy.logerr("ODOM DISCONTINUITY DETECTED")
self.ab.raise_alarm(
- problem_description="ODOM DISCONTINUITY DETECTED JUMPED {} METERS".format(
- jump,
- ),
+ problem_description=f"ODOM DISCONTINUITY DETECTED JUMPED {jump} METERS",
severity=5,
)
self.odom_discontinuity = True
@@ -120,9 +118,7 @@ def need_kill(self):
if odom_loss:
rospy.logerr_throttle(
1,
- "LOST ODOM FOR {} SECONDS".format(
- (rospy.Time.now() - self.last_time).to_sec(),
- ),
+ f"LOST ODOM FOR {(rospy.Time.now() - self.last_time).to_sec()} SECONDS",
)
self.ab.raise_alarm(
problem_description="LOST ODOM FOR {} SECONDS".format(
diff --git a/SubjuGator/command/subjugator_keyboard_control/teleop_twist_keyboard.py b/SubjuGator/command/subjugator_keyboard_control/teleop_twist_keyboard.py
index ea5c7bedb..870df6ac7 100755
--- a/SubjuGator/command/subjugator_keyboard_control/teleop_twist_keyboard.py
+++ b/SubjuGator/command/subjugator_keyboard_control/teleop_twist_keyboard.py
@@ -141,9 +141,7 @@ def wait_for_subscribers(self):
while not rospy.is_shutdown() and self.publisher.get_num_connections() == 0:
if i == 4:
print(
- "Waiting for subscriber to connect to {}".format(
- self.publisher.name,
- ),
+ f"Waiting for subscriber to connect to {self.publisher.name}",
)
rospy.sleep(0.5)
i += 1
diff --git a/SubjuGator/command/subjugator_launch/config/adaptive_controller_gazebo.yaml b/SubjuGator/command/subjugator_launch/config/adaptive_controller_gazebo.yaml
new file mode 100644
index 000000000..c6406c44d
--- /dev/null
+++ b/SubjuGator/command/subjugator_launch/config/adaptive_controller_gazebo.yaml
@@ -0,0 +1,6 @@
+---
+kp: 1000, 1000, 1000, 5000, 5000, 5000
+kd: 150, 150, 150, 50, 100, 25
+kg: 5,5,5,5,5,5
+ki: 5,5,5,5,10,5
+use_learned: false
diff --git a/SubjuGator/command/subjugator_launch/launch/sub8.launch b/SubjuGator/command/subjugator_launch/launch/sub8.launch
index 178efc6bb..247fe1e62 100644
--- a/SubjuGator/command/subjugator_launch/launch/sub8.launch
+++ b/SubjuGator/command/subjugator_launch/launch/sub8.launch
@@ -39,7 +39,9 @@
-
+
+
+
diff --git a/SubjuGator/command/subjugator_launch/launch/subsystems/adaptive_controller.launch b/SubjuGator/command/subjugator_launch/launch/subsystems/adaptive_controller.launch
index 17bd1d3ac..a758c4278 100644
--- a/SubjuGator/command/subjugator_launch/launch/subsystems/adaptive_controller.launch
+++ b/SubjuGator/command/subjugator_launch/launch/subsystems/adaptive_controller.launch
@@ -1,8 +1,10 @@
-
+
+
-
-
+
+
+
diff --git a/SubjuGator/command/subjugator_missions/subjugator_missions/arm_torpedos.py b/SubjuGator/command/subjugator_missions/subjugator_missions/arm_torpedos.py
index aad130049..7a88e8908 100644
--- a/SubjuGator/command/subjugator_missions/subjugator_missions/arm_torpedos.py
+++ b/SubjuGator/command/subjugator_missions/subjugator_missions/arm_torpedos.py
@@ -170,9 +170,7 @@ async def fire(self, target: str):
)
self.print_good(
- "{} locked. Firing torpedoes. Hit confirmed, good job Commander.".format(
- target,
- ),
+ f"{target} locked. Firing torpedoes. Hit confirmed, good job Commander.",
)
sub_pos = await self.tx_pose()
print("Current Sub Position: ", sub_pos)
diff --git a/SubjuGator/command/subjugator_missions/subjugator_missions/start_gate.py b/SubjuGator/command/subjugator_missions/subjugator_missions/start_gate.py
index f395bd1e0..0886298e1 100644
--- a/SubjuGator/command/subjugator_missions/subjugator_missions/start_gate.py
+++ b/SubjuGator/command/subjugator_missions/subjugator_missions/start_gate.py
@@ -160,9 +160,7 @@ def find_gate(
p2 = rosmsg_to_numpy(o2.pose.position)
if distance.euclidean(p, p2) > max_distance_away:
fprint(
- "Poles too far away. Distance {}".format(
- distance.euclidean(p, p2),
- ),
+ f"Poles too far away. Distance {distance.euclidean(p, p2)}",
)
continue
if distance.euclidean(p, p2) < min_distance_away:
@@ -180,10 +178,7 @@ def find_gate(
continue
if abs(line[0]) < 1 and abs(line[1]) < 1:
fprint(
- "Objects on top of one another. x {}, y {}".format(
- line[0],
- line[1],
- ),
+ f"Objects on top of one another. x {line[0]}, y {line[1]}",
)
continue
return (p, p2)
diff --git a/SubjuGator/command/subjugator_missions/subjugator_missions/sub_singleton.py b/SubjuGator/command/subjugator_missions/subjugator_missions/sub_singleton.py
index 693fc3bd7..8e498fc4d 100644
--- a/SubjuGator/command/subjugator_missions/subjugator_missions/sub_singleton.py
+++ b/SubjuGator/command/subjugator_missions/subjugator_missions/sub_singleton.py
@@ -599,7 +599,7 @@ async def start_search_in_cone(
distance_tol: float = 10,
speed: float = 0.5,
clear: bool = False,
- c_func: Callable = None,
+ c_func: Callable | None = None,
):
if clear:
print("SONAR_OBJECTS: clearing pointcloud")
diff --git a/SubjuGator/drivers/magnetic_compensation/sub8_magnetic_hardsoft_compensation/scripts/generate_config b/SubjuGator/drivers/magnetic_compensation/sub8_magnetic_hardsoft_compensation/scripts/generate_config
index 86ce1a09a..4b65395af 100755
--- a/SubjuGator/drivers/magnetic_compensation/sub8_magnetic_hardsoft_compensation/scripts/generate_config
+++ b/SubjuGator/drivers/magnetic_compensation/sub8_magnetic_hardsoft_compensation/scripts/generate_config
@@ -111,9 +111,7 @@ def test():
assert numpy.allclose(
shift2,
shift,
- ), "Magnetic Hardsoft Compenstion self-test failed, shifts: {}".format(
- (shift2, shift),
- )
+ ), f"Magnetic Hardsoft Compenstion self-test failed, shifts: {(shift2, shift)}"
assert (
error < 1e-5
), f"Magnetic Hardsoft Compenstion self-test failed, error: {error}"
diff --git a/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/handle.py b/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/handle.py
index c62f5732b..19df02c33 100644
--- a/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/handle.py
+++ b/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/handle.py
@@ -109,9 +109,7 @@ def on_command(self, msg: Thrust) -> None:
# If we don't have a mapping for this thruster, ignore it
if cmd.name not in self.thrusters:
rospy.logwarn(
- "Command received for {}, but this is not a thruster.".format(
- cmd.name,
- ),
+ f"Command received for {cmd.name}, but this is not a thruster.",
)
continue
# Map commanded thrust (in newetons) to effort value (-1 to 1)
diff --git a/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/packets.py b/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/packets.py
index 8cc3a0f27..dcd1aaa0b 100644
--- a/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/packets.py
+++ b/SubjuGator/drivers/sub8_thrust_and_kill_board/sub8_thrust_and_kill_board/packets.py
@@ -287,7 +287,4 @@ def command(self) -> float:
return struct.unpack("f", self.payload[1:])[0]
def __str__(self):
- return "ThrustPacket(thruster_id={}, command={})".format(
- self.thruster_id,
- self.command,
- )
+ return f"ThrustPacket(thruster_id={self.thruster_id}, command={self.command})"
diff --git a/SubjuGator/perception/subjugator_perception/ml_classifiers/path_marker/path_localizer.py b/SubjuGator/perception/subjugator_perception/ml_classifiers/path_marker/path_localizer.py
index 29665f12a..c70e5e258 100755
--- a/SubjuGator/perception/subjugator_perception/ml_classifiers/path_marker/path_localizer.py
+++ b/SubjuGator/perception/subjugator_perception/ml_classifiers/path_marker/path_localizer.py
@@ -17,7 +17,7 @@
rospack.get_path("subjugator_perception") + "/ml_classifiers/path_marker/utils",
)
-from utils import detector_utils # noqa: manually appending sys.path
+from utils import detector_utils # noqa
class classifier:
diff --git a/docs/images/gazebo/Bottom_Toolbar.png b/docs/images/gazebo/Bottom_Toolbar.png
new file mode 100644
index 000000000..de1fb6d98
Binary files /dev/null and b/docs/images/gazebo/Bottom_Toolbar.png differ
diff --git a/docs/images/gazebo/Interface.png b/docs/images/gazebo/Interface.png
new file mode 100644
index 000000000..b6f5018c4
Binary files /dev/null and b/docs/images/gazebo/Interface.png differ
diff --git a/docs/images/gazebo/Labeled_Interface.png b/docs/images/gazebo/Labeled_Interface.png
new file mode 100644
index 000000000..871ddca52
Binary files /dev/null and b/docs/images/gazebo/Labeled_Interface.png differ
diff --git a/docs/images/gazebo/Menus.png b/docs/images/gazebo/Menus.png
new file mode 100644
index 000000000..d6c39d62e
Binary files /dev/null and b/docs/images/gazebo/Menus.png differ
diff --git a/docs/images/gazebo/Model_Editor.png b/docs/images/gazebo/Model_Editor.png
new file mode 100644
index 000000000..362f7cb16
Binary files /dev/null and b/docs/images/gazebo/Model_Editor.png differ
diff --git a/docs/images/gazebo/Mouse.png b/docs/images/gazebo/Mouse.png
new file mode 100644
index 000000000..728c8fc8f
Binary files /dev/null and b/docs/images/gazebo/Mouse.png differ
diff --git a/docs/images/gazebo/TrackPad.png b/docs/images/gazebo/TrackPad.png
new file mode 100644
index 000000000..1231be57b
Binary files /dev/null and b/docs/images/gazebo/TrackPad.png differ
diff --git a/docs/images/gazebo/Upper_Toolbar.png b/docs/images/gazebo/Upper_Toolbar.png
new file mode 100644
index 000000000..68e36e69a
Binary files /dev/null and b/docs/images/gazebo/Upper_Toolbar.png differ
diff --git a/docs/software/gazebo_guide.md b/docs/software/gazebo_guide.md
new file mode 100644
index 000000000..752147b10
--- /dev/null
+++ b/docs/software/gazebo_guide.md
@@ -0,0 +1,327 @@
+# Gazebo Guide
+
+This is a guide on how to use Gazebo, including launching the sub and
+viewing it inside Gazebo, how Gazebo can be controlled and manipulated, how to
+use Gazebo to make world files, and where you can find more info on Gazebo.
+
+## Brief Introduction
+
+Gazebo is an open-source 3D dynamic simulator, that allows us to design, model,
+and test robots and their behavior in a virtual world. Similar to game engines
+like Unity, Gazebo takes in scripts and models and performs physics simulations
+on them. While it is similar, Gazebo offers physics simulations at a higher level
+of fidelity, a suite of sensors, and interfaces for both users and programs.
+
+There are many versions of Gazebo, but this guide was written for **Gazebo
+Classic 11** as this is the version of Gazebo currently being used at MIL.
+
+### Running Gazebo
+
+There are many ways run Gazebo.
+
+* Click the "Show Applications" button on Ubuntu (the apps button located in
+ the bottom left corner). Then search for the Gazebo Icon and press that icon
+ to open Gazebo.
+
+* You can press Alt + F2 (or Alt+Fn+F2) to bring up the "Run a Command" window.
+ Then type "gazebo" and press Enter to open Gazebo.
+
+* You can also open a terminal and type "gazebo" and it will open Gazebo.
+
+To launch Gazebo will all the necessary files for simulating Subjugator,
+follow these steps:
+
+1. Open a terminal window and execute the following command. This command uses
+ ROS to start all the relevant ROS nodes and to load the world file for
+ subjugator. This also starts a Gazebo Sever.
+
+ ```bash
+ roslaunch subjugator_launch gazebo.launch --screen
+ ```
+
+ :::{note}
+ `--screen` forces all ROS node output to the screen. It is used for debugging.
+ :::
+
+1. Then in another terminal window run this command to start the Gazebo
+ graphical client, which connects to the Gazebo Sever.
+
+ ```bash
+ gazebogui
+ ```
+
+1. Then in another terminal window run this command and then press Shift-C to
+ unkill the sub to allow movement.
+
+ ```bash
+ amonitor kill
+ ```
+
+1. Execute the following command to start a specific mission, replacing
+ "StartGate2022" with the name of the desired mission:
+
+ ```bash
+ mission run StartGate2022
+ ```
+
+## How to use Gazebo
+
+### User Interface
+
+When you launch Gazebo you will be greeted by its user interface.
+
+![Gazebo Interface](/images/gazebo/Interface.png)
+
+The Gazebo interface consists of three main sections: The **Left Panel**, the
+**Scene**, and the **Right Panel**. By default the Right Panel is hidden. This
+is because we do not have anything selected. To show the right panel you can
+always Click and drag the bar on the right to open it.
+
+![Gazebo Labeled Interface](/images/gazebo/Labeled_Interface.png)
+
+#### Left Panel
+
+The Left Panel has three tabs, each with different features. You can see
+these tabs at the top of the Left Panel. You can click on them to switch
+between them. The tabs are:
+
+##### World Tab
+
+The World Tab displays the models that are currently in the scene. Within this
+tab, you can view and modify various model parameters, like their pose (their
+position and rotation). Additionally, you can expand the GUI option to adjust
+the camera view angle by modifying the camera pose.
+
+##### Insert Tab
+
+The Insert Tab allows you to add new models (objects) to the Gazebo simulation.
+Here, you will find a list of file paths where your models are saved. To view
+the model list, click on the arrow located on the left side of each path to
+expand the folder. Select the desired model and click again in the scene to
+place it.
+
+##### Layers Tab
+
+The Layers tab organizes and displays different visualization groups within
+the simulation. Layers can contain one or more models, and enabling or disabling
+a layer will show or hide all the models within it. While not mandatory, layers
+can be helpful for organizing your simulation. Note that this tab may be empty
+if no layers are defined.
+
+To define a layer, you will need to edit a model's SDF file. To add an
+object's visuals to a layer you will need to add a `` tag for information
+and then a `` tag with the layer number under each `` tag. Below
+is an example:
+
+```xml
+
+
+ 0
+
+ ...
+
+```
+
+#### Scene
+
+The Scene is the main window where objects are animated, and you interact with
+the environment. Two toolbars are available:
+
+##### The Upper Toolbar
+
+The Upper Toolbar consists of various buttons that allow you to select, move,
+rotate, and scale objects. It also provides options to create simple shapes,
+as well as copy and paste objects.
+
+![Gazebo Upper Toolbar](/images/gazebo/Upper_Toolbar.png)
+
+##### The Bottom Toolbar
+
+The Bottom Toolbar displays information about the simulation time and its
+relationship to real time. It helps you track the progress of your simulation.
+
+![Gazebo Bottom Toolbar](/images/gazebo/Bottom_Toolbar.png)
+
+#### Right Panel
+
+The Right Panel is used to interact with the mobile parts (joints) of a
+selected model. It provides controls and settings specific to manipulating
+the joints of a model.
+
+#### Menus (File, Edit, Camera, View, Window, Help)
+
+Most Linux apps have menus. These menus are usually tabs (file, edit, ...) at
+the top left of an application. If you don't see it move your cursor to the
+top of the application window and the menus should appear. Below describes
+the features of each menu that Gazebo has.
+
+![Gazebo Menus](/images/gazebo/Menus.png)
+
+#### Mouse
+
+It is recommended that you use a mouse when using Gazebo. Below is a diagram
+showing all the mouse controls.
+
+![Gazebo Mouse Controls](/images/gazebo/Mouse.png)
+
+However, if you want to use a trackpad you can. Below are the controls for
+the trackpad:
+
+![Gazebo Mouse Controls](/images/gazebo/TrackPad.png)
+
+## How to Create Models
+
+The structure for most models in Gazebo is that the model is a folder that
+contains a .config file, .SDF file(s), and .dae or .stl file(s). The config
+file contains meta information about the model. The .SDF file contains
+important simulation information like model definitions, the model's
+positioning, its physical properties, etc. The .dae or .stl files contain 3D
+mesh information. When creating a model it's recommended that you have all
+these components.
+
+### Model Editor
+
+You can use the **Model Editor** to create simple models all within Gazebo,
+but for more complex models you will want to create/write your own SDF files
+and .dae files.
+
+To enter the **Model Editor**, click on Edit in the menu bar and select Model Editor.
+
+The Model Editor Interface looks similar to the regular Gazebo UI with some
+slight changes. The left panel and the top toolbar have been changed to
+contain only buttons and features for editing and creating parts of a model.
+The bottom toolbar is now hidden as the simulation is paused.
+
+![Gazebo Model Editor](/images/gazebo/Model_Editor.png)
+
+When entering the Model Editor all other models will turn white. This can make
+it hard to see the model you are currently working on if you have a lot of
+models in your scene. So it may be easier to open a blank Gazebo world and
+create the model using the Model Editor there. Then when you exit the Model
+Editor it will ask you to save the model. This will save the model as a folder
+on your computer. Then you can go back to the original world and insert
+this model, by going to the insert tab (note this is the regular insert tab,
+not the one in the model editor) and adding that model folder's file path.
+
+:::{note}
+When inserting a model, make sure that the file path you pick is the path to
+the parent directory. This directory contains the model folder you want to
+insert. Do not put the path to the model folder. Often this parent
+directory will contain all the models you want to use. The file hierarchy
+might look like this: where models is the parent directory and contains the
+models model1 and buoys.
+
+```
+ models/
+ ├── model_1/
+ │ ├── model.config
+ │ ├── model1.sdf
+ │ ├── model1.dae
+ │ └── ...
+ ├── buoys/
+ │ ├── model.config
+ │ ├── green.sdf
+ │ ├── red.sdf
+ │ └── ...
+```
+
+:::
+
+#### Insert Tab
+
+The Insert Tab allows you to add new parts, including links and models, to
+your model. You have two options for inserting shapes (links):
+
+* Simple Shapes: Click on the desired shape in the left panel and then click
+ again in the scene to place it.
+
+* Custom Shapes: You can add COLLADA (.dae), 3D Systems (.stl), Wavefront
+ (.obj), and W3C SVG (.svg) files as custom shapes. Create these shapes
+ using 3D modeling software like Blender.
+
+You can also insert other models into your model as nested models. These
+models can be obtained from the Gazebo Model Database
+(http://gazebosim.org/models/), which should be listed as one of your file
+paths under Model Databases. For example, if you need a depth sensor, you can
+add a depth sensor model from the database to your model.
+
+#### Model Tab
+
+The Model Tab displays the settings for the model you are creating. Here, you
+can change the model's name and modify its basic parameters. Additionally, you
+can add plugins to give your model functionality here as well.
+
+#### Placing Shapes
+
+Once you insert a shape, you can use the toolbar to move, rotate, and scale
+it. For finer control, you can double-click the shape or right-click and
+select "Open Link Inspector" to access the link inspector. In the link
+inspector, you can modify the shape's position, rotation, and scale to
+achieve the desired configuration. Make sure to adjust the scale in both the
+Visual and Collision tabs.
+
+#### Adding Joints
+
+To constrain the motion between shapes, you can add joints. Follow these steps:
+
+* Click on the joint icon in the toolbar (a line connecting two points).
+
+* In the Joint Creation Window, select the parent and child links (shapes)
+ of the joint.
+
+* Select the type of joint you need in the Joint Types section near the top
+ of the window.
+
+* Select the joint axis. Some joints do not have an axis.
+
+* Align the link (shape). Use the align links section to align the parent
+ and the child with each other.
+
+#### Adding a Plugin
+
+To control your model, you need to create a plugin. You can do this in the
+Model Tab by specifying the necessary details for the plugin.
+
+You can find more information on how to create your own custom plugins [here](https://classic.gazebosim.org/tutorials?tut=ros_gzplugins).
+
+#### Model Creation Workflow Example
+
+To illustrate the model creation process, let's consider creating a car model
+using Blender:
+
+* Create .dae files for the wheels, chassis, and other parts in Blender.
+
+* Insert these shapes into the Model Editor.
+
+* Use the toolbar and link inspector to position each shape precisely.
+
+* Add joints between the shapes to enable motion constraints.
+
+* Finally, create a plugin to control the model's behavior.
+
+### World File
+
+A World in Gazebo is used to describe the collection of robots and objects,
+and global parameters like the sky, ambient light, and physics properties.
+A World is the entire virtual environment that you have been
+working in. The World stores important information like where all the models
+are, their properties, and important global properties.
+
+You can save the World file by selecting File and Save World As.
+
+:::{note}
+When using roslaunch to start Gazebo, it is crucial to update the World file
+if you make any changes to the simulation environment. At MIL, there is a
+dedicated `worlds` folder where Gazebo World files are saved. When you update
+a World file, ensure that you replace the old file in this folder. Failing
+to do so will result in the continued use of the old World file when
+launching Gazebo using roslaunch.
+:::
+
+## More Info
+
+If you ever need more information on how any aspect of Gazebo works or how to
+use ROS with Gazebo you can check out the official Gazebo Documentation [here](https://classic.gazebosim.org/tutorials).
+Some of the images used in this guide are sourced from here and we are grateful
+to the creators for their exceptional work, which has been instrumental in
+writing this guide.
diff --git a/docs/software/index.rst b/docs/software/index.rst
index 61b036c7c..9e779a0eb 100644
--- a/docs/software/index.rst
+++ b/docs/software/index.rst
@@ -13,10 +13,12 @@ Various documentation related to practices followed by the MIL Software team.
help
zobelisk
asyncio
+ rqt
rostest
Bash Style Guide
C++ Style Guide
Python Style Guide
+ Gazebo Guide
Migrating to ROS Noetic
Installing NVIDIA Drivers for RTX 2080
Installing Ubuntu 18.04 on an M-series Apple computer
diff --git a/docs/software/rqt.md b/docs/software/rqt.md
new file mode 100644
index 000000000..258c43522
--- /dev/null
+++ b/docs/software/rqt.md
@@ -0,0 +1,437 @@
+# Implementing GUI Based Tools Using `rqt`
+
+## Introduction
+
+RQT (ROS Qt-based GUI Framework) is a powerful tool that provides a graphical
+user interface for ROS (Robot Operating System) applications. It allows users
+to monitor ROS topics, interact with nodes, visualize data, and more. This
+document serves as a user guide for understanding and effectively using RQT.
+
+### Benefits of RQT
+
+- **Graphical Interface**: RQT provides a user-friendly graphical interface,
+ making it easier to visualize data, monitor topics, and interact with nodes.
+ This is especially helpful for users who prefer a GUI based approach over
+ the command line.
+
+- **Centralized Tool**: RQT serves as a centralized tool for various ROS tasks,
+ including visualization, configuration, and debugging. This reduces the need
+ to switch between multiple terminals and tools, streamlining the development
+ process.
+
+- **Extensibility**: RQT's plugin architecture allows users to create custom
+ plugins to tailor the tool to their specific application needs. This
+ extensibility significantly enhances RQT's capabilities and to various
+ robotic systems.
+
+- **Integration with ROS**: RQT is tightly integrated with ROS, ensuring
+ seamless interaction with ROS nodes, topics, and services. This integration
+ simplifies the process of debugging and analyzing data in real-time.
+
+## Installation
+
+Before using RQT, make sure you have ROS installed on your system. If ROS is
+not yet installed, follow the official ROS installation guide for your
+operating system. Once ROS is installed, you can install RQT using the package
+manager:
+
+```bash
+sudo apt-get install ros-noetic-rqt
+```
+
+## Getting Started
+
+### Launching RQT
+
+To launch RQT, open a terminal and run the following command:
+
+```bash
+rqt
+```
+
+This command will launch the RQT Graphical User Interface, displaying a
+default layout with available plugins.
+
+## Monitoring Information with RQT
+
+RQT provides various plugins to monitor information published on specific ROS
+topics. The most common plugin for this purpose is the Plot plugin.
+
+### Subscribing to Specific Topics
+
+1. Open RQT using the command mentioned in the previous section.
+1. Click on the "Plugins" menu in the top toolbar and select "Topics" >
+ "Message Publisher."
+1. A new "Message Publisher" tab will appear. Click on the "+" button to
+ subscribe to a specific topic.
+1. Choose the desired topic from the list and click "OK."
+1. Now, you can monitor the information published on the selected topic in
+ real-time.
+
+### Displaying Data with Plot Plugin
+
+1. Open RQT using the command mentioned earlier.
+1. Click on the "Plugins" menu in the top toolbar and select
+ "Visualization" > "Plot."
+1. A new "Plot" tab will appear. Click on the "+" button to add a plot.
+1. Choose the topic you want to visualize from the list, select the message
+ field, and click "OK."
+1. The plot will start displaying the data sent over the selected topic.
+
+## Configuring RQT
+
+RQT allows users to customize its appearance and layout to suit their preferences.
+
+### Theme Configuration
+
+1. Open RQT using the command mentioned earlier.
+1. Click on the "View" menu in the top toolbar and select "Settings."
+1. In the "Settings" window, go to the "Appearance" tab.
+1. Here, you can change the color scheme, font size, and other visual settings.
+
+### Layout Configuration
+
+1. Open RQT using the command mentioned earlier.
+1. Rearrange existing plugins by clicking on their tab and dragging them to
+ a new position.
+1. To create a new tab, right-click on any existing tab and select "Add New Tab."
+1. Drag and drop desired plugins onto the new tab.
+1. Save your customized layout by going to the "View" menu and selecting
+ "Perspectives" > "Save Perspective."
+
+## Writing RQT Plugins
+
+RQT allows users to create their custom plugins to extend its functionality.
+Writing RQT plugins is essential when the available plugins do not fully
+meet your application's requirements.
+
+### Plugin Structure
+
+To write an RQT plugin, you need to follow a specific directory structure and
+include Python or C++ code to implement the plugin's functionality. The
+essential components of an RQT plugin are:
+
+- **Plugin XML (`plugin.xml`)**: This file defines the plugin and its
+ dependencies, specifying essential information like name, description,
+ and which ROS packages it depends on.
+
+- **Python or C++ Code**: This code contains the actual implementation of the
+ plugin's functionality. For Python plugins, the code should extend the
+ `rqt_gui_py::Plugin` class, while for C++ plugins, it should extend the
+ `rqt_gui_cpp::Plugin` class.
+
+### Implementing a Basic Plugin
+
+In this section, we will walk through an example of implementing a basic RQT
+plugin. We will create a simple plugin to display the current time published
+by a ROS topic. The plugin will consist of a label that updates with the
+current time whenever a new message is received.
+
+#### Step 1: Create the Plugin Package
+
+Create a new ROS package for your plugin using the `catkin_create_pkg` command:
+
+```bash
+catkin_create_pkg my_rqt_plugin rospy rqt_gui_py
+```
+
+The package dependencies `rospy` and `rqt_gui_py` are required to work with
+ROS and create a Python-based RQT plugin.
+
+#### Step 2: Implement the Plugin Code
+
+Next, create a Python script for your plugin. In the `src` directory of your
+package, create a file named `my_rqt_plugin.py` with the following content:
+
+```python
+#!/usr/bin/env python
+
+import rospy
+from rqt_gui_py.plugin import Plugin
+from python_qt_binding.QtWidgets import QLabel, QVBoxLayout, QWidget
+from std_msgs.msg import String
+import time
+
+class MyRqtPlugin(Plugin):
+
+ def __init__(self, context):
+ super(MyRqtPlugin, self).__init__(context)
+ self.setObjectName('MyRqtPlugin')
+
+ # Create the main widget and layout
+ self._widget = QWidget()
+ layout = QVBoxLayout()
+ self._widget.setLayout(layout)
+
+ # Create a label to display the current time
+ self._label = QLabel("Current Time: N/A")
+ layout.addWidget(self._label)
+
+ # Subscribe to the topic that provides the current time
+ rospy.Subscriber('/current_time', String, self._update_time)
+
+ # Add the widget to the context
+ context.add_widget(self._widget)
+
+ def _update_time(self, msg):
+ # Update the label with the received time message
+ self._label.setText(f"Current Time: {msg.data}")
+
+ def shutdown_plugin(self):
+ # Unregister the subscriber when the plugin is shut down
+ rospy.Subscriber('/current_time', String, self._update_time)
+
+ def save_settings(self, plugin_settings, instance_settings):
+ # Save the plugin's settings when needed
+ pass
+
+ def restore_settings(self, plugin_settings, instance_settings):
+ # Restore the plugin's settings when needed
+ pass
+
+```
+
+In this code, we create a plugin class named `MyRqtPlugin`, which inherits
+from `rqt_gui_py.Plugin`. The `__init__` method sets up the GUI elements, such
+as a label, and subscribes to the `/current_time` topic to receive time updates.
+
+#### Step 3: Add the Plugin XML File
+
+Create a file named `plugin.xml` in the root of your package to define the
+plugin. Add the following content to the `plugin.xml`:
+
+```xml
+
+
+
+ A custom RQT plugin to display the current time.
+
+
+
+ settings
+ Display the current time.
+
+
+
+```
+
+This XML file defines the plugin class, its description, and the icon to be
+displayed in the RQT GUI.
+
+#### Step 4: Build the Plugin
+
+Build your plugin package using `catkin_make`:
+
+```bash
+catkin_make
+```
+
+#### Step 5: Launch RQT and Load the Plugin
+
+Launch RQT using the command mentioned earlier:
+
+```bash
+rqt
+```
+
+Once RQT is open, navigate to the "Plugins" menu and select "My RQT Plugin"
+from the list. The plugin will be loaded, and you should see the label
+displaying the current time.
+
+### Adding Custom Functionality
+
+The example plugin we implemented is a simple demonstration of how to create
+an RQT plugin. Depending on your application's requirements, you can add more
+sophisticated features, such as custom data visualization, interactive
+controls, and integration with other ROS elements.
+
+You can also improve the plugin by adding error handling, additional options
+for time formatting, and the ability to pause/resume the time updates.
+
+## Implementing a More Advanced Plugin
+
+In this section, we will implement a more advanced RQT plugin that visualizes
+data from multiple ROS topics using a 2D plot. This plugin will allow users to
+choose which topics to plot and specify the message field to display on the X
+and Y axes. Let's call this plugin "ROS Data Plotter."
+
+### Step 1: Create the Plugin Package
+
+Create a new ROS package for your plugin using the `catkin_create_pkg` command:
+
+```bash
+catkin_create_pkg ros_data_plotter rospy rqt_gui_py matplotlib
+```
+
+The package dependencies `rospy`, `rqt_gui_py`, and `matplotlib` are required
+to work with ROS, create a Python-based RQT plugin, and plot data, respectively.
+
+### Step 2: Implement the Plugin Code
+
+Next, create a Python script for your plugin. In the `src` directory of your
+package, create a file named `ros_data_plotter.py` with the following content:
+
+```python
+#!/usr/bin/env python
+
+import rospy
+from rqt_gui_py.plugin import Plugin
+from python_qt_binding.QtWidgets import QWidget, QVBoxLayout, QComboBox
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import Figure
+from std_msgs.msg import Float32 # Modify this import based on your data type
+
+class ROSDataPlotter(Plugin):
+
+ def __init__(self, context):
+ super(ROSDataPlotter, self).__init__(context)
+ self.setObjectName('ROSDataPlotter')
+
+ # Create the main widget and layout
+ self._widget = QWidget()
+ layout = QVBoxLayout()
+ self._widget.setLayout(layout)
+
+ # Create a combo box to select topics and message fields
+ self._topic_field_selector = QComboBox()
+ self._topic_field_selector.currentIndexChanged.connect(self._update_plot)
+ layout.addWidget(self._topic_field_selector)
+
+ # Create the matplotlib figure and plot
+ self._figure = Figure()
+ self._canvas = FigureCanvas(self._figure)
+ layout.addWidget(self._canvas)
+
+ # Initialize the plot
+ self._update_plot()
+
+ # Add the widget to the context
+ context.add_widget(self._widget)
+
+ def _update_plot(self):
+ # Clear the previous plot data
+ self._figure.clear()
+ axes = self._figure.add_subplot(111)
+
+ # Get the selected topic and field from the combo box
+ selected_topic_field = self._topic_field_selector.currentText()
+ topic, field = selected_topic_field.split(' - ')
+
+ # Subscribe to the selected topic
+ rospy.Subscriber(topic, Float32, self._plot_data) # Modify this based on your data type
+
+ def _plot_data(self, msg):
+ # Get the selected topic and field from the combo box
+ selected_topic_field = self._topic_field_selector.currentText()
+ topic, field = selected_topic_field.split(' - ')
+
+ # Plot the data on the graph
+ # Modify this part based on your data type and plot requirements
+ x_data = rospy.Time.now().to_sec() # Use timestamp as X-axis
+ y_data = getattr(msg, field) # Use the specified field from the message as Y-axis data
+ axes = self._figure.add_subplot(111)
+ axes.plot(x_data, y_data)
+
+ # Refresh the plot
+ self._canvas.draw()
+
+ def shutdown_plugin(self):
+ # Unsubscribe from the topic when the plugin is shut down
+ self._topic_field_selector.clear()
+ rospy.Subscriber(topic, Float32, self._plot_data)
+
+ def save_settings(self, plugin_settings, instance_settings):
+ # Save the plugin's settings when needed
+ pass
+
+ def restore_settings(self, plugin_settings, instance_settings):
+ # Restore the plugin's settings when needed
+ pass
+
+```
+
+In this code, we create a plugin class named `ROSDataPlotter`, which inherits
+from `rqt_gui_py.Plugin`. The `__init__` method sets up the GUI elements,
+such as a combo box to select topics and message fields, and the matplotlib
+figure to plot the data. The `_update_plot` method updates the plot whenever
+a new topic or message field is selected from the combo box. The `_plot_data`
+method is called when new messages are received on the selected topic, and it
+plots the data on the graph.
+
+### Step 3: Add the Plugin XML File
+
+Create a file named `plugin.xml` in the root of your package to define the
+plugin. Add the following content to the `plugin.xml`:
+
+```xml
+
+
+
+ An advanced RQT plugin to plot data from multiple ROS topics.
+
+
+
+ settings
+ Plot data from selected ROS topics.
+
+
+
+```
+
+This XML file defines the plugin class, its description, and the icon
+to be displayed in the RQT GUI.
+
+### Step 4: Build the Plugin
+
+Build your plugin package using `catkin_make`:
+
+```bash
+catkin_make
+```
+
+### Step 5: Launch RQT and Load the Plugin
+
+Launch RQT
+
+ using the command mentioned earlier:
+
+```bash
+rqt
+```
+
+Once RQT is open, navigate to the "Plugins" menu and select "ROS Data Plotter"
+from the list. The plugin will be loaded, and you should see a combo box to
+select topics and fields, and a graph to display the plotted data.
+
+### Adding Custom Functionality
+
+In this more advanced example, we created an RQT plugin that allows users to
+plot data from multiple ROS topics. The example focused on plotting Float32
+messages, but you can modify the `_plot_data` method to handle different
+message types or plot multiple data series on the same graph.
+
+You can further enhance the plugin by adding features like legends, axis
+labels, custom plot styles, data smoothing, and real-time updates.
+Additionally, you can provide options to save the plotted data as CSV
+or images for further analysis.
+
+With custom plugins, you have the freedom to tailor RQT to suit your
+specific application's visualization needs, making it a versatile tool
+for ROS developers.
+
+## External Resources
+
+- [ROS wiki page for rqt](https://wiki.ros.org/rqt)
+- [GeorgiaTech RoboJacket's rqt tutorial](https://www.youtube.com/watch?v=o90IaCRje2I)
+- [Creating Custom rqt Plugins](https://github.com/ChoKasem/rqt_tut)
+
+## Conclusion
+
+Congratulations! You have now learned the basics of using RQT, including
+monitoring information sent over specific topics, configuring RQT to your
+preferences, writing your custom plugins, and understanding the importance
+of using RQT in ROS development. RQT provides a user-friendly and powerful
+graphical interface for ROS applications, making it an essential tool for
+developers and researchers working with ROS-based robotic systems. By
+leveraging RQT's capabilities, you can streamline your development process
+and gain valuable insights into your robot's behavior.
diff --git a/docs/subjugator/index.rst b/docs/subjugator/index.rst
index 7a9416713..e52976cde 100644
--- a/docs/subjugator/index.rst
+++ b/docs/subjugator/index.rst
@@ -18,7 +18,7 @@ SubjuGator 8
PID Controller
Nav Tube
Watercooling
-
+
electrical
RoboSub
diff --git a/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/sub8_driver.py b/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/sub8_driver.py
index 8236940ac..c03b318d2 100755
--- a/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/sub8_driver.py
+++ b/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/sub8_driver.py
@@ -85,9 +85,7 @@ def read_packet(self) -> bool:
self.handles[packet.device].on_data(packet.data)
else:
rospy.logwarn(
- "Message received for device {}, but no handle registered".format(
- packet.device,
- ),
+ f"Message received for device {packet.device}, but no handle registered",
)
return True
diff --git a/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/utils.py b/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/utils.py
index 2d2558f57..0ef0cf9a1 100644
--- a/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/utils.py
+++ b/mil_common/drivers/mil_usb_to_can/mil_usb_to_can/sub8/utils.py
@@ -26,10 +26,7 @@ class ChecksumException(USB2CANException):
def __init__(self, calculated, expected):
super().__init__(
- "Checksum was calculated as {} but reported as {}".format(
- calculated,
- expected,
- ),
+ f"Checksum was calculated as {calculated} but reported as {expected}",
)
diff --git a/mil_common/mil_missions/mil_missions_gui/dashboard.py b/mil_common/mil_missions/mil_missions_gui/dashboard.py
index f3f224243..440dc85b9 100755
--- a/mil_common/mil_missions/mil_missions_gui/dashboard.py
+++ b/mil_common/mil_missions/mil_missions_gui/dashboard.py
@@ -195,11 +195,10 @@ def ui_log(self, string):
"""
self.lock.acquire()
date_time = datetime.datetime.fromtimestamp(rospy.Time.now().to_time())
- time_str = "{}:{}:{}".format(
- date_time.hour,
- date_time.minute,
- date_time.second,
- ).ljust(12, " ")
+ time_str = f"{date_time.hour}:{date_time.minute}:{date_time.second}".ljust(
+ 12,
+ " ",
+ )
formatted = time_str + string
self.feedback_list.addItem(formatted)
self.lock.release()
@@ -242,9 +241,7 @@ def transition_cb(self, goal, handler):
self.current_mission_status = terminal_state
self.current_mission_status_label.setText(self.current_mission_status)
self.ui_log(
- "FINISHED: mission finished ({})".format(
- self.current_mission_status,
- ),
+ f"FINISHED: mission finished ({self.current_mission_status})",
)
def reload_available_missions(self, _):
diff --git a/mil_common/mil_missions/nodes/mission_client b/mil_common/mil_missions/nodes/mission_client
index 2ff81d944..230547f8e 100755
--- a/mil_common/mil_missions/nodes/mission_client
+++ b/mil_common/mil_missions/nodes/mission_client
@@ -30,9 +30,7 @@ class MissionClientCli:
missions = MissionClient.available_missions()
if not missions:
print(
- "{} param not set. Is mission runner running?".format(
- MissionClient.LIST_PARAM,
- ),
+ f"{MissionClient.LIST_PARAM} param not set. Is mission runner running?",
)
return
print("Available Missions:")
diff --git a/mil_common/mil_missions/test/test_import_missions.py b/mil_common/mil_missions/test/test_import_missions.py
index e03fb7e82..c09c8f13b 100755
--- a/mil_common/mil_missions/test/test_import_missions.py
+++ b/mil_common/mil_missions/test/test_import_missions.py
@@ -21,10 +21,7 @@ def test_import_missions(self):
if not hasattr(mission_module, self.base_class):
self.fail(
- msg="{} doesn't have base mission {}".format(
- self.module,
- self.base_class,
- ),
+ msg=f"{self.module} doesn't have base mission {self.base_class}",
)
base_mission = getattr(mission_module, self.base_class)
diff --git a/mil_common/perception/mil_vision/mil_vision_tools/cv_tools.py b/mil_common/perception/mil_vision/mil_vision_tools/cv_tools.py
index dcfb694a1..42b6112b2 100644
--- a/mil_common/perception/mil_vision/mil_vision_tools/cv_tools.py
+++ b/mil_common/perception/mil_vision/mil_vision_tools/cv_tools.py
@@ -79,7 +79,7 @@ def __init__(
self.conversion_code = conversion_code
@classmethod
- def from_dict(cls, d, in_space: str = "BGR", thresh_space: str = None):
+ def from_dict(cls, d, in_space: str = "BGR", thresh_space: Optional[str] = None):
"""
Loads thresholds from a dictionary. See examples for valid dictionaries.
@@ -114,9 +114,7 @@ def from_dict(cls, d, in_space: str = "BGR", thresh_space: str = None):
d.keys(),
),
)
- assert thresh_space in d, "{} color space not in dictionary".format(
- thresh_space,
- )
+ assert thresh_space in d, f"{thresh_space} color space not in dictionary"
inner = d[thresh_space]
if "low" in inner and "high" in inner:
return cls(
diff --git a/mil_common/utils/mil_tools/mil_ros_tools/extract_bag_images.py b/mil_common/utils/mil_tools/mil_ros_tools/extract_bag_images.py
index c243ae950..22e52987c 100755
--- a/mil_common/utils/mil_tools/mil_ros_tools/extract_bag_images.py
+++ b/mil_common/utils/mil_tools/mil_ros_tools/extract_bag_images.py
@@ -150,17 +150,11 @@ def get_camera_model(bag: str, topic: str):
_, msg, _ = next(bag.read_messages(topics=camera_info_topic))
except StopIteration:
raise Exception(
- "no camera info messages found on topic {} in {}".format(
- camera_info_topic,
- bag,
- ),
+ f"no camera info messages found on topic {camera_info_topic} in {bag}",
)
if msg._type != "sensor_msgs/CameraInfo":
raise Exception(
- "msg on topic {} are not camera info in bag {}".format(
- camera_info_topic,
- bag,
- ),
+ f"msg on topic {camera_info_topic} are not camera info in bag {bag}",
)
model = PinholeCameraModel()
model.fromCameraInfo(msg)
@@ -218,10 +212,7 @@ def extract_images(self, source_dir=".", image_dir=".", verbose=False):
"""
if verbose:
print(
- "\tExtracting images from topic {} in {}".format(
- self.topic,
- self.filename,
- ),
+ f"\tExtracting images from topic {self.topic} in {self.filename}",
)
filename = os.path.join(source_dir, self.filename)
b = rosbag.Bag(filename)
diff --git a/mil_common/utils/mil_tools/mil_ros_tools/label_me_bag.py b/mil_common/utils/mil_tools/mil_ros_tools/label_me_bag.py
index d3a98fa67..4e6c7231c 100755
--- a/mil_common/utils/mil_tools/mil_ros_tools/label_me_bag.py
+++ b/mil_common/utils/mil_tools/mil_ros_tools/label_me_bag.py
@@ -263,10 +263,7 @@ def completion_report(self):
total_img_count += i
if total_img_count == 0:
print(
- "{}/{} TOTAL images labeled (0%)".format(
- total_xml_count,
- total_img_count,
- ),
+ f"{total_xml_count}/{total_img_count} TOTAL images labeled (0%)",
)
else:
print(
diff --git a/mil_common/utils/mil_tools/mil_ros_tools/msg_helpers.py b/mil_common/utils/mil_tools/mil_ros_tools/msg_helpers.py
index 9ffa9b2d9..8ab315818 100644
--- a/mil_common/utils/mil_tools/mil_ros_tools/msg_helpers.py
+++ b/mil_common/utils/mil_tools/mil_ros_tools/msg_helpers.py
@@ -58,10 +58,7 @@ def rosmsg_to_numpy(rosmsg, keys=None):
break
assert (
len(output_array) != 0
- ), "Input type {} has none of these attributes {}.".format(
- type(rosmsg).__name__,
- keys,
- )
+ ), f"Input type {type(rosmsg).__name__} has none of these attributes {keys}."
return np.array(output_array).astype(np.float32)
else:
diff --git a/mil_common/utils/mil_tools/nodes/online_bagger.py b/mil_common/utils/mil_tools/nodes/online_bagger.py
index 2580f5cf2..5a0c6bb32 100755
--- a/mil_common/utils/mil_tools/nodes/online_bagger.py
+++ b/mil_common/utils/mil_tools/nodes/online_bagger.py
@@ -71,10 +71,7 @@ def get_subscriber_list(self, status):
sub_list = ""
for topic in self.subscriber_list:
if self.subscriber_list[topic][1] == status:
- sub_list = sub_list + "\n{:13}, {}".format(
- self.subscriber_list[topic],
- topic,
- )
+ sub_list = sub_list + f"\n{self.subscriber_list[topic]:13}, {topic}"
return sub_list
def get_params(self):
diff --git a/mil_common/utils/mil_tools/nodes/tf_fudger.py b/mil_common/utils/mil_tools/nodes/tf_fudger.py
index 58a647757..0ae508c98 100755
--- a/mil_common/utils/mil_tools/nodes/tf_fudger.py
+++ b/mil_common/utils/mil_tools/nodes/tf_fudger.py
@@ -45,10 +45,7 @@
ang_res = ang_range / ang_max
print(
- "Distance resolution: {} meters\nAngular resolution: {} radians".format(
- x_res,
- ang_res,
- ),
+ f"Distance resolution: {x_res} meters\nAngular resolution: {ang_res} radians",
)
cv2.createTrackbar("x", "tf", 0, x_max, lambda x: x)
diff --git a/mil_common/utils/mil_tools/nodes/tf_to_gazebo.py b/mil_common/utils/mil_tools/nodes/tf_to_gazebo.py
index b587dfb88..9363d3c4f 100755
--- a/mil_common/utils/mil_tools/nodes/tf_to_gazebo.py
+++ b/mil_common/utils/mil_tools/nodes/tf_to_gazebo.py
@@ -25,12 +25,7 @@ def main():
if do_cam_fix:
rot = tf.transformations.quaternion_multiply(rot, cam_fix_quat)
print(
- "(qx={}, qy={} , qz={}, qw={})".format(
- rot[0],
- rot[1],
- rot[2],
- rot[3],
- ),
+ f"(qx={rot[0]}, qy={rot[1]} , qz={rot[2]}, qw={rot[3]})",
)
print(f"(x={trans[0]}, y={trans[1]}, z={trans[2]})")
euler = tf.transformations.euler_from_quaternion(rot)
diff --git a/requirements.txt b/requirements.txt
index ae0ac3d2e..90237fb22 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,7 +11,7 @@ pandas==1.4.1
numpy==1.22.3
# Machine Learning
-scipy==1.7.3
+scipy==1.10.0
scikit-learn==1.1.1
# Computer Vision
@@ -22,7 +22,7 @@ PyYAML==6.0
urdf-parser-py==0.0.4
# Internet
-aiohttp==3.8.1
+aiohttp==3.8.5
aiohttp-xmlrpc==1.5.0
# Documentation
diff --git a/ruff.toml b/ruff.toml
index 69b8787be..0ec9c21f1 100644
--- a/ruff.toml
+++ b/ruff.toml
@@ -35,6 +35,7 @@ ignore = [
"D212", # we use second line!
"D200", # one line docstrings are not shortened!
"SIM115", # too many for now...
+ "RUF012", # too many for now, adds obvious ClassVar statements
]
[per-file-ignores]