Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3d5b86a
This is supposed to do something to help pid arm position stuff
Oct 15, 2025
223b75b
rosbag
CPRTSoftwareLead Oct 16, 2025
cd52c80
This is supposed to do something to help pid arm position stuff
Oct 15, 2025
9ca43e7
rosbag
CPRTSoftwareLead Oct 16, 2025
ef502b8
Commited whatever I had changed
Oct 16, 2025
a2a5adb
Updated PID grapher to include all motors and correct mapping (in the…
Oct 16, 2025
06601cf
Optimized running speed of Pid_Grapher
Oct 17, 2025
4ffcac2
Optimized running speed of Pid_Grapher
Oct 17, 2025
fe8fa82
did something with position_testing idk
Oct 17, 2025
d1e8aae
Merge branch 'PIDGraphing' of https://github.com/CPRT/rover into PIDG…
Oct 17, 2025
21a0b6a
Removed comments that were already resolved
Oct 17, 2025
c18774c
This is supposed to do something to help pid arm position stuff
Oct 15, 2025
f904321
rosbag
CPRTSoftwareLead Oct 16, 2025
6e6803a
Commited whatever I had changed
Oct 16, 2025
4e3ab4b
Updated PID grapher to include all motors and correct mapping (in the…
Oct 16, 2025
dff0e12
Optimized running speed of Pid_Grapher
Oct 17, 2025
3b748e4
Optimized running speed of Pid_Grapher
Oct 17, 2025
6e0d2d1
did something with position_testing idk
Oct 17, 2025
6b8ddff
Removed comments that were already resolved
Oct 17, 2025
ffcc695
Added new PID_Control and graphing systems
Oct 28, 2025
fdeece7
Pushed tweaks
Oct 28, 2025
40760a7
Fixed Dialogue box when saving
Oct 28, 2025
f28de2a
Deleted old graph captures
Oct 28, 2025
bb3db72
Switched the buttons being used to operate ... ideally
Oct 29, 2025
bc4b70c
IDK what I fixed
Oct 29, 2025
7ea9b1e
WIP commiting something
CPRTSoftwareLead Oct 29, 2025
1610554
WIP commiting something
CPRTSoftwareLead Oct 29, 2025
ba9e395
changes left on Jetson
CPRTSoftwareLead Oct 31, 2025
5af8505
controller fixes
Oct 31, 2025
2de5764
Left on rover - Connor
CPRTSoftwareLead Nov 5, 2025
84b7487
updated values
CPRTSoftwareLead Nov 6, 2025
48e7e35
updated values
CPRTSoftwareLead Nov 6, 2025
5fe1d20
rounding
Nov 6, 2025
19c85f5
finished merge
Nov 6, 2025
1ddf419
updated pid values
CPRTSoftwareLead Nov 7, 2025
0c3ee94
big guy
ConnorNeed Nov 20, 2025
0f254d9
Cleaning for rebase
Nov 22, 2025
76c33eb
Camera changes
CPRTSoftwareLead Nov 22, 2025
942a608
added reset to grapher function -- FRONTEND
Nov 22, 2025
653e8c9
requested by merge
Nov 22, 2025
080347b
Merge branch 'main' into PIDGraphing
CPRTSoftwareLead Jan 28, 2026
1e52d87
merged with main
CPRTSoftwareLead Jan 28, 2026
c9db362
Configurated PIDGRapher for new arm
Feb 13, 2026
1bb7741
fixed launch file
Feb 13, 2026
3737d84
Removed position testing package
Feb 13, 2026
f424abf
Cleaned up launch files
Feb 13, 2026
fdafeb8
removed unnessescary comments
Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
install*/
log/
build*/
src/kindr
*.vscode
raman/

Expand All @@ -15,4 +16,5 @@ camera_setup/images/*
# macOS Directory Information
.DS_Store

upgrade_pkg.tar.*
upgrade_pkg.tar.*
src/kindr
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ RPi.GPIO
rpi-hardware-pwm
Jetson.GPIO
python-can
matplotlib
ultralytics
onnx
onnxruntime
odrive
odrive
32 changes: 32 additions & 0 deletions rosbag2_2025_10_16-00_10_00/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
rosbag2_bagfile_information:
version: 5
storage_identifier: sqlite3
duration:
nanoseconds: 14330097927
starting_time:
nanoseconds_since_epoch: 1760573403749726640
message_count: 1611
topics_with_message_count:
- topic_metadata:
name: /rover_arm_controller/joint_trajectory
type: trajectory_msgs/msg/JointTrajectory
serialization_format: cdr
offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false"
message_count: 177
- topic_metadata:
name: /joint_states
type: sensor_msgs/msg/JointState
serialization_format: cdr
offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false"
message_count: 1434
compression_format: ""
compression_mode: ""
relative_file_paths:
- rosbag2_2025_10_16-00_10_00_0.db3
files:
- path: rosbag2_2025_10_16-00_10_00_0.db3
starting_time:
nanoseconds_since_epoch: 1760573403749726640
duration:
nanoseconds: 14330097927
message_count: 1611
Binary file not shown.
1 change: 1 addition & 0 deletions src/kindr
Submodule kindr added at 22a19d
19 changes: 19 additions & 0 deletions src/pid_grapher/launch/pid_grapher.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import launch
import launch_ros.actions


def generate_launch_description():
return launch.LaunchDescription(
[
launch_ros.actions.Node(
package="pid_grapher",
executable="base_pid_grapher",
name="base_pid_grapher_node",
),
launch_ros.actions.Node(
package="pid_grapher",
executable="base_pid_grapher",
name="base_pid_grapher_node",
),
]
)
21 changes: 21 additions & 0 deletions src/pid_grapher/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>pid_grapher</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="PSPuttaguna@gmail.com">vscode</maintainer>
<license>TODO: License declaration</license>

<exec_depend>python3-matplotlib</exec_depend>


<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
Empty file.
121 changes: 121 additions & 0 deletions src/pid_grapher/pid_grapher/base_pid_grapher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import math
from datetime import datetime
from .joint_data import JointData
import rclpy
from rclpy.node import Node
import rclpy.time as time
import matplotlib

matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from sensor_msgs.msg import Joy
from sensor_msgs.msg import JointState
from trajectory_msgs.msg import JointTrajectory
import os

plt.rcParams["toolbar"] = "none"


class BasePIDGrapher(Node):
def update_plots(self):
rate = 10
period = 1.0 / rate
for j in self.Joints.values():
j.plotGraph()
self.fig.canvas.draw_idle()
plt.pause(0.000000001)

def __init__(self):
super().__init__("base_pid_grapher")
self.subscription = self.create_subscription(
JointState,
"/joint_states",
self.plotter_callback,
10,
)
self.subscription_targets = self.create_subscription(
JointTrajectory,
"/arm_controller/joint_trajectory",
self.trajectory_callback,
10,
)

plt.ion()

joint_names = ["Joint_1", "Joint_2", "Joint_3", "Joint_4", "Joint_5", "Joint_6"]

n_plots = len(joint_names)
cols = 2
rows = math.ceil(n_plots / cols)

self.fig, self.axs = plt.subplots(rows, cols, figsize=(12, rows * 3))

# self.fig, self.axs = plt.subplots(3, 2, figsize=(12, 8)) # 3 rows × 2 columns
self.fig.subplots_adjust(
left=0.07, right=0.95, top=0.95, bottom=0.07, hspace=0.3, wspace=0.3
)
self.axs = self.axs.flatten() # make it easy to index as a list
self.Joints = {}
for name, ax in zip(joint_names, self.axs):
self.Joints[name] = JointData(name, ax)

plt.show(block=False)

self.timer = self.create_timer(0.1, self.update_plots)

self.subscription

self.beginning = self.get_clock().now()

self.fig.canvas.mpl_connect("key_press_event", self.on_key)

def plotter_callback(self, msg):
now = self.get_clock().now()

delta = now - self.beginning
x = delta.nanoseconds / 1e9
for i in range(0, len(msg.name)):
if msg.name[i] in self.Joints:
Joint = self.Joints[msg.name[i]]
Joint.time = x
Joint.state = msg.position[i]

def _show_plot(self):
plt.show(block=True)

def trajectory_callback(self, msg):
if len(msg.points) > 0:
point = msg.points[-1] # Get the last point in the trajectory
for i in range(0, len(msg.joint_names)):
jointName = msg.joint_names[i]
if jointName in self.Joints:
self.Joints[jointName].target = point.positions[i]

def on_key(self, event):
if event.key == "p":
script_dir = os.path.join(os.getcwd(), "pid_grapher")
plots_dir = os.path.join(script_dir, "plots")
os.makedirs(plots_dir, exist_ok=True)

timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
save_path = os.path.join(plots_dir, f"joint_plot_{timestamp}.png")

plt.savefig(save_path, bbox_inches="tight", dpi=300)
print(f"Plot saved to {save_path}")
elif event.key == "r":
for j in self.Joints.values():
j.reset()
self.beginning = self.get_clock().now()
print("Reset data")


def main(args=None):
rclpy.init(args=args)
node = BasePIDGrapher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions src/pid_grapher/pid_grapher/joint_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class JointData:
def __init__(self, name, ax):
self.name = name
self.time = 0
self.target = 0
self.state = 0
self.times = []
self.states = []
self.targetsHistory = []
self.ax = ax
self.ax.set_xlabel("Time (s)")
self.ax.set_ylabel("State")
self.ax.set_title(name)
self.ax.legend(["State", "Target"])
self.ax.grid()
self.line1 = self.ax.plot([], [], "-", label="State")[0]
self.line2 = self.ax.plot([], [], "--", label="Target")[0]

seconds = 30 # Number of seconds to display on the graph

self.MAX_POINTS = (
seconds * 10
) - 50 # Maximum number of points to display on the graph (gotten from seconds)

self.ax.legend()


def plotGraph(self):
self.targetsHistory.append(round(self.target, 2))
self.times.append(round(self.time, 2))
self.states.append(round(self.state, 2))

if len(self.times) > self.MAX_POINTS:
self.times.pop(0)
self.states.pop(0)
self.targetsHistory.pop(0)

self.line1.set_xdata(self.times)
self.line2.set_xdata(self.times)

self.line1.set_ydata(self.states)
self.line2.set_ydata(self.targetsHistory)

self.ax.relim()
self.ax.autoscale_view()

def reset(self):
self.times = []
self.states = []
self.targetsHistory = []
Binary file added src/pid_grapher/plots/joint_plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
4 changes: 4 additions & 0 deletions src/pid_grapher/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[develop]
script_dir=$base/lib/pid_grapher
[install]
install_scripts=$base/lib/pid_grapher
31 changes: 31 additions & 0 deletions src/pid_grapher/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from setuptools import find_packages, setup
import os
from glob import glob

package_name = "pid_grapher"

setup(
name=package_name,
version="0.0.0",
packages=find_packages(exclude=["test"]),
data_files=[
("share/ament_index/resource_index/packages", ["resource/" + package_name]),
(
os.path.join("share", package_name, "launch"),
glob(os.path.join("launch", "*launch.[pxy][yma]*")),
),
("share/" + package_name, ["package.xml"]),
],
install_requires=["setuptools", "matplotlib"],
zip_safe=True,
maintainer="vscode",
maintainer_email="PSPuttaguna@gmail.com",
description="TODO: Package description",
license="TODO: License declaration",
tests_require=["pytest"],
entry_points={
"console_scripts": [
"pid_grapher=pid_grapher.base_pid_grapher:main",
],
},
)
27 changes: 27 additions & 0 deletions src/pid_grapher/test/test_copyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_copyright.main import main
import pytest


# Remove the `skip` decorator once the source file(s) have a copyright header
@pytest.mark.skip(
reason="No copyright header has been placed in the generated source file."
)
@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=[".", "test"])
assert rc == 0, "Found errors"
25 changes: 25 additions & 0 deletions src/pid_grapher/test/test_flake8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_flake8.main import main_with_errors
import pytest


@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc, errors = main_with_errors(argv=[])
assert rc == 0, "Found %d code style errors / warnings:\n" % len(
errors
) + "\n".join(errors)
23 changes: 23 additions & 0 deletions src/pid_grapher/test/test_pep257.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_pep257.main import main
import pytest


@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=[".", "test"])
assert rc == 0, "Found code style errors / warnings"
Loading