Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
392b953
controller stuff
Feb 15, 2025
8020573
deleted madgwick
Feb 18, 2025
81b9344
fix: fixes equality statements
shawnkchan Feb 18, 2025
2e6a701
added recentering for imu
Feb 19, 2025
6423dff
added recentring for potentiometer
Feb 19, 2025
6ba7913
i forgot what i did to these but theres a launch file now
Feb 19, 2025
de2b71e
feat: publishes to macros setpoint velocity topic
shawnkchan Feb 22, 2025
9de7ac9
fix: explicitly changes forward_vel and side_vel to python floats
shawnkchan Feb 22, 2025
fbd38ea
feat: adds services
shawnkchan Feb 22, 2025
445e1f7
feat: creates functions for various services
shawnkchan Feb 22, 2025
2b74445
nit: adds TODO
shawnkchan Feb 22, 2025
42a8818
feat: adds button publishers
shawnkchan Feb 24, 2025
58cb8dc
adds button sub
shawnkchan Feb 24, 2025
3d6b01b
started button class
Feb 24, 2025
9367d89
button functionality in
Feb 24, 2025
13f3080
adds button subscriptions
shawnkchan Feb 24, 2025
96a88ef
feat: adds functions to arm and change mode, to takeoff
shawnkchan Feb 24, 2025
97825ff
feat: adds button control
shawnkchan Feb 24, 2025
c2b48dd
fix: stops imu from publishing
shawnkchan Feb 24, 2025
3b83ec3
feat: adds waiting for future
shawnkchan Feb 24, 2025
606c66a
nit: changes
shawnkchan Feb 24, 2025
8002d9c
feat: adds button manager to setup.py
shawnkchan Feb 24, 2025
166d0dc
feat: arm drone function fixed
shawnkchan Feb 24, 2025
4b68506
feat: changes button mappings
Feb 25, 2025
7be65a0
nit: minor changes
Feb 25, 2025
9be4dca
feat: adds left hand controls
Feb 25, 2025
2df3daf
added hand
Feb 24, 2025
c9dd492
launch file
Feb 24, 2025
ac8867d
added drone movement
Feb 24, 2025
c3ba419
added cmaktlist
Feb 24, 2025
246bc80
added timeout for buttons and publisher
Feb 24, 2025
bf717e9
works now
shawnkchan Feb 25, 2025
5f8eb60
added blocking code for when drone is not airborne
shawnkchan Feb 25, 2025
abf6373
added logging
shawnkchan Feb 25, 2025
f407c11
added drone_movement
shawnkchan Feb 25, 2025
bc60ad8
fix: merge conflict
shawnkchan Feb 25, 2025
a3d71f1
feat: adds services
shawnkchan Feb 22, 2025
64354d1
nit: adds TODO
shawnkchan Feb 22, 2025
fbf223c
feat: adds button publishers
shawnkchan Feb 24, 2025
9c59c82
adds button sub
shawnkchan Feb 24, 2025
71e828c
feat: adds functions to arm and change mode, to takeoff
shawnkchan Feb 24, 2025
b479398
fix: fixes button manager imports
shawnkchan Feb 25, 2025
755787c
feat: works now
shawnkchan Feb 11, 2025
5408f9b
added stepper controller
Feb 17, 2025
ddab9a8
added rotation functionality
Feb 17, 2025
24e23fc
changed some oopsies
Feb 17, 2025
7bed45e
added rotation functionality
Feb 17, 2025
f8d0898
deleted stuff
Feb 17, 2025
3145166
renames stepper_id
Feb 17, 2025
33737bd
works now
shawnkchan Feb 17, 2025
5f54db6
added launch file
Feb 24, 2025
4acec5b
launching mavros from apm.launch
Feb 24, 2025
8982fa1
setup.py
Feb 24, 2025
93b3595
ugh
shawnkchan Feb 24, 2025
9a624ef
feat: adds reis launch file set up
shawnkchan Feb 24, 2025
fc4c4fe
Non-pilot GUI
shawnkchan Feb 24, 2025
79106f5
feat: creates controller file
shawnkchan Feb 9, 2025
ac23ff9
feat: writes client nodes
shawnkchan Feb 9, 2025
f561674
feat: implements inputs for changing flight mode
shawnkchan Feb 10, 2025
bba0c99
feat: adds takeoff service
shawnkchan Feb 10, 2025
9fe136b
feat: updates takeoff command
shawnkchan Feb 13, 2025
1872d8e
feat: adds sub for horiz movement
shawnkchan Feb 16, 2025
4e6a96d
feat: uses pynput
shawnkchan Feb 16, 2025
3b67891
feat: updates imports
shawnkchan Feb 16, 2025
181721d
feat: adds horizontal movement commands
shawnkchan Feb 17, 2025
e99e233
feat: adds back clients for services
shawnkchan Feb 17, 2025
5a3efe8
feat: implements vertical movement and setup.py config
shawnkchan Feb 17, 2025
b45f2bc
fix: fixes vertical movement commands
shawnkchan Feb 17, 2025
06df78c
feat: changes setup.py
shawnkchan Feb 17, 2025
12f2097
idk what i changed
shawnkchan Feb 18, 2025
8fbbdc0
fix: fixes stepper code
shawnkchan Feb 19, 2025
06da177
gui mavros test code
DSuen00 Feb 24, 2025
b42e0a4
nit: test
shawnkchan Feb 24, 2025
d28adfa
quarternion to transform3d
shawnkchan Feb 24, 2025
b61189e
controller gui prototype
DSuen00 Feb 24, 2025
07fea22
feat: adds gui_mavros
shawnkchan Feb 24, 2025
c46959e
nonpilot gui
shawnkchan Feb 24, 2025
5dd494a
tesst
shawnkchan Feb 24, 2025
1ff3a38
Minor changes to GUI
shawnkchan Feb 24, 2025
e146c3d
Added imu to nonpilot gui
shawnkchan Feb 24, 2025
a50828f
added imu and left but1 pot
Feb 24, 2025
e2e0fc3
controller stuff
Feb 15, 2025
b0911f5
added cmaktlist
Feb 24, 2025
b519554
added stepper controller
Feb 17, 2025
058ee69
fix: fixes stepper code
shawnkchan Feb 19, 2025
d139fb1
feat: adds gui_mavros
shawnkchan Feb 24, 2025
1a0f01e
controller stuff
Feb 15, 2025
c713f33
added stepper controller
Feb 17, 2025
13db83a
added cmaktlist
Feb 24, 2025
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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
6 changes: 6 additions & 0 deletions src/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"remote.autoForwardPortsSource": "output",
"python.languageServer": "Pylance",
"python.analysis.completeFunctionParens": true,
"python.analysis.diagnosticMode": "workspace",
}
Empty file.
9 changes: 9 additions & 0 deletions src/controller/controller/angle_data.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Angle,Value
0, 220
10, 260
20, 315
30, 370
40, 480
50, 610
60, 770
70, 1150
178 changes: 178 additions & 0 deletions src/controller/controller/button_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import rclpy
from rclpy.node import Node
from std_msgs.msg import Int32
from mavros_msgs.srv import CommandBool, SetMode, CommandLong
from mavros_msgs.msg import State
from geometry_msgs.msg import Twist, Vector3
from controller.helpers.button import Button
from time import sleep
from interfaces.srv import ToggleStepper, SetFloat



VALID_MODES = [
'GUIDED',
'LAND',
'STABILIZE',
'LOITER'
]

TAKEOFF_HEIGHT = 1.0
MAV_CMD_NAV_TAKEOFF = 22 # From Mavlink MAV_CMD: https://mavlink.io/en/messages/common.html#mav_commands)
MAX_VELOCITY = 0.10


class ButtonManagerNode(Node):
def __init__(self):
super().__init__('button_manager')

self.declare_parameter('hand', 'right')

self.hand = self.get_parameter('hand').get_parameter_value().string_value

if self.hand not in ['right', 'left']:
self.get_logger().error("Invalid hand parameter passed")
return

if self.hand == 'right':
self.button1 = Button(topic='controller/right/but1', short_callback=self.set_guided_callback)
self.button2 = Button(topic='controller/right/but2', short_callback=self.arm_callback)
self.button3 = Button(topic='controller/right/but3', short_callback=self.takeoff_callback)
self.button4 = Button(topic='controller/right/but4', short_callback=self.land_callback)

self.get_logger().info('Right hand initialized')

else:
self.button1 = Button(topic='controller/left/but1', press_callback=self.vertical_up_movement_callback)
self.button2 = Button(topic='controller/left/but2', press_callback=self.vertical_down_movement_callback)
self.button3 = Button(topic='controller/left/but3', short_callback=self.bag_in_callback)
self.button4 = Button(topic='controller/left/but4', short_callback=self.bag_out_callback)

self.get_logger().info('Left hand initialized')


# RIGHT CONTROLLER BUTTONS
self.but1_subscriber = self.create_subscription(Int32, 'controller/but1', self.button1.data_callback, 10)
self.but2_subscriber = self.create_subscription(Int32, 'controller/but2', self.button2.data_callback, 10)
self.but3_subscriber = self.create_subscription(Int32, 'controller/but3', self.button3.data_callback, 10)
self.but4_subscriber = self.create_subscription(Int32, 'controller/but4', self.button4.data_callback, 10)

self.mavros_clients = {
'mode': self.create_client(SetMode, '/mavros/set_mode'),
'arm': self.create_client(CommandBool, '/mavros/cmd/arming'),
'takeoff': self.create_client(CommandLong, '/mavros/cmd/command'),
'bag': self.create_client(ToggleStepper, '/rotate_stepper'),
'move_vert': self.create_client(SetFloat, '/vert_vel')
}

for service_name, client in self.mavros_clients.items():
while not client.wait_for_service(timeout_sec=1.0):
self.get_logger().warn(f'Waiting for {service_name} service')

self.get_logger().info('All services online')

def arm_takeoff_callback(self):
self.get_logger().info("arm and takeoff")
self.arm_drone()

def land_callback(self):
self._change_mode('LAND')

def set_guided_callback(self):
self._change_mode('GUIDED')

def is_armed_callback(self, msg: State):
self.is_armed = msg.armed

def arm_callback(self):
self.arm_drone()

def takeoff_callback(self):
self.takeoff()

def vertical_up_movement_callback(self):
float_data_req = SetFloat.Request()
float_data_req.data = MAX_VELOCITY
future = self.mavros_clients['move_vert'].call_async(float_data_req)
self.get_logger().info("Positive vertical movement")

def vertical_down_movement_callback(self):
float_data_req = SetFloat.Request()
float_data_req.data = -MAX_VELOCITY
future = self.mavros_clients['move_vert'].call_async(float_data_req)
self.get_logger().info("Negative vertical movement")


def bag_in_callback(self):
bag_in_req = ToggleStepper.Request()
bag_in_req.stepper_id = 1
bag_in_req.speed = float(1)
future = self.mavros_clients['bag'].call_async(bag_in_req)
try:
self.get_logger().info(f"bag_in result: {future.result()}")
except Exception as e:
self.get_logger().error('bag_in: Exception {e} occured')

def bag_out_callback(self):
bag_out_req = ToggleStepper.Request()
bag_out_req.stepper_id = 1
bag_out_req.speed = float(-1)
future = self.mavros_clients['bag'].call_async(bag_out_req)
try:
self.get_logger().info(f"bag_in result: {future.result()}")
except Exception as e:
self.get_logger().error('bag_in: Exception {e} occured')

def _change_mode(self, mode: str):
mode_upper = mode.upper()
if mode_upper not in VALID_MODES:
self.get_logger().error(f"{mode.upper()} is not a valid mode")
return
mode_req = SetMode.Request(custom_mode=mode_upper)
future = self.mavros_clients['mode'].call_async(mode_req)
self.get_logger().info(f"Change mode to {mode_upper} result: {future.result()}")

def arm_drone(self):
arm_req = CommandBool.Request(value=True)
future = self.mavros_clients['arm'].call_async(arm_req)
self.get_logger().info(f"Arm drone result: {future.result()}")

# # rclpy.spin_until_future_complete(self, future)
# if future.exception():
# print('exception raised')
# elif future.result() is None:
# print('result returned none')
# else:
# print(f"{future.result()}")

# self.get_logger().info(f"{future.done()}")
# return future.done()

def takeoff(self):
takeoff_req = CommandLong.Request(
broadcast=False,
command=MAV_CMD_NAV_TAKEOFF,
confirmation=0,
param1=0.0,
param2=0.0,
param3=0.0,
param4=0.0,
param5=0.0,
param6=0.0,
param7=TAKEOFF_HEIGHT
)
future = self.mavros_clients['takeoff'].call_async(takeoff_req)
self.get_logger().info(f"Takeoff command result: {future.result()}")



def main(args=None):
rclpy.init(args=args)
button_manager = ButtonManagerNode()
rclpy.spin(button_manager)
button_manager.destroy_node()

if __name__ == '__main__':
main()


114 changes: 114 additions & 0 deletions src/controller/controller/controller_pub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import time
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Imu
from std_msgs.msg import Int32 # Import Int32 message type
from controller.helpers.serial_helper import SerialHelper

VALID_MODES = [
'GUIDED',
'LAND',
'STABILIZE',
'LOITER'
]



class ControllerPubNode(Node):
def __init__(self):
super().__init__('controller_pub')

# Declare parameters for serial port and baud rate with default values
self.declare_parameter('serial_port', '/dev/ttyUSB0')
self.declare_parameter('baud_rate', 115200)
self.declare_parameter('hand', 'right') # Declare 'hand' parameter


# Get the parameters set at runtime (or use the defaults)
serial_port = self.get_parameter('serial_port').get_parameter_value().string_value
baud_rate = self.get_parameter('baud_rate').get_parameter_value().integer_value
self.hand = self.get_parameter('hand').get_parameter_value().string_value

# Validate 'hand' parameter
if self.hand not in ["right", "left"]:
self.get_logger().warn("Invalid 'hand' parameter! Must be 'right' or 'left'. Defaulting to 'right'.")
self.hand = "right"
print("hello")
# Initialize the SerialHelper with the parameters
self.ser = SerialHelper(serial_port, baud_rate)


# Create publishers
self.imu_publisher = self.create_publisher(Imu, 'imu/' + self.hand + '/data', 10)
self.pot_publisher = self.create_publisher(Int32, 'controller/' + self.hand + '/pot', 10)
self.but1_publisher = self.create_publisher(Int32, 'controller/' + self.hand + '/but1', 10)
self.but2_publisher = self.create_publisher(Int32, 'controller/but2', 10)
self.but3_publisher = self.create_publisher(Int32, 'controller/but3', 10)
self.but4_publisher = self.create_publisher(Int32, 'controller/but4', 10)

# TODO: Subscribe to get state of drone for deconfliction

# Start a timer to update IMU data every 50ms (20 Hz)
self.timer = self.create_timer(0.05, self.update)

def update(self):
try:
# Read and process serial data
line = self.ser.read_from_serial()
# TODO: Send data from ESP in dict format and convert it from its string representation
# TODO: Send data from ESP in dict format and convert it from its string representation
if line:
# print(line)
split = line.split(',')
if len(split) != 11:
return

# Convert data to float (accelerometer and gyroscope data)
accel_data = list(map(float, split[:3])) # accelx, accely, accelz
gyro_data = list(map(lambda x: float(x) * (3.141592653589793 / 180), split[3:6])) # Convert to radians/sec
pot_value = int(split[6]) # Potentiometer value (7th number)
but1_value = int(split[7]) # Button state (8th number)
but2_value = int(split[8])
but3_value = int(split[9])
but4_value = int(split[10])

# Create and populate IMU message
imu_msg = Imu()
imu_msg.linear_acceleration.x = accel_data[0]
imu_msg.linear_acceleration.y = accel_data[1]
imu_msg.linear_acceleration.z = accel_data[2]
imu_msg.angular_velocity.x = gyro_data[0]
imu_msg.angular_velocity.y = gyro_data[1]
imu_msg.angular_velocity.z = gyro_data[2]
self.imu_publisher.publish(imu_msg)

# Create and publish Potentiometer message
pot_msg = Int32()
pot_msg.data = pot_value
self.pot_publisher.publish(pot_msg)

# Create and publish Button message
self.but1_publisher.publish(Int32(data=but1_value))
self.but2_publisher.publish(Int32(data=but2_value))
self.but3_publisher.publish(Int32(data=but3_value))
self.but4_publisher.publish(Int32(data=but4_value))


except Exception as e:
self.get_logger().error(f"Error in update: {e}")






def main(args=None):
rclpy.init(args=args)
controller_pub = ControllerPubNode()
rclpy.spin(controller_pub)
controller_pub.destroy_node()
# rclpy.shutdown()


if __name__ == '__main__':
main()
Loading