Skip to content

Commit faa3c48

Browse files
committed
added xbox controller support
Signed-off-by: AkashK321 <theakashk@gmail.com>
1 parent 1ee7411 commit faa3c48

3 files changed

Lines changed: 149 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ install(DIRECTORY launch config DESTINATION share/${PROJECT_NAME})
2828
install(PROGRAMS
2929
scripts/joy_to_input_f310.py
3030
scripts/joy_to_input_ps4.py
31+
scripts/joy_to_input_xboxone.py
3132
scripts/rviz_model.py
3233
DESTINATION lib/${PROJECT_NAME}
3334
)

launch/electrode.launch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
),
4242

4343
DeclareLaunchArgument('controller',
44-
default_value='f310',
45-
choices=['f310', 'ps4'],
44+
default_value='xboxone',
45+
choices=['xboxone', 'f310', 'ps4'],
4646
description='which controller you are using'
4747
),
4848

scripts/joy_to_input_xboxone.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
from enum import IntEnum
3+
import rclpy
4+
from rclpy.node import Node
5+
6+
from sensor_msgs.msg import Joy
7+
from synapse_msgs.msg import Input
8+
9+
10+
class Channel(IntEnum):
11+
RIGHT_STICK_RIGHT=0
12+
RIGHT_STICK_UP=1
13+
LEFT_STICK_UP=2
14+
LEFT_STICK_RIGHT=3
15+
SWA_DOWN=4
16+
SWB_DOWN=5
17+
SWC_DOWN=6
18+
SWD_DOWN=7
19+
VRA_CW=8
20+
VRB_CCW=9
21+
RIGHT_TRIGGER_IN=10
22+
LEFT_TRIGGER_IN=11
23+
UNUSED_BUTTON_1=12
24+
UNUSED_BUTTON_2=13
25+
26+
class Axes(IntEnum):
27+
LEFT_STICK_LEFT=0
28+
LEFT_STICK_UP=1
29+
LEFT_TRIGGER_OUT=2
30+
RIGHT_STICK_LEFT=3
31+
RIGHT_STICK_UP=4
32+
RIGHT_TRIGGER_OUT=5
33+
DPAD_LEFT=6
34+
DPAD_UP=7
35+
36+
class Button(IntEnum):
37+
A=0
38+
B=1
39+
X=2
40+
Y=3
41+
LEFT_BUMPER=4
42+
RIGHT_BUMPER=5
43+
BACK=6
44+
START=7
45+
CENTER=8
46+
LEFT_STICK=9
47+
RIGHT_STICK=10
48+
49+
50+
SwitchPosUp = -1
51+
SwitchPosDown = 1
52+
SwitchPosMiddle = 0
53+
54+
button_map = {
55+
Button.START: [(Channel.UNUSED_BUTTON_1, SwitchPosDown)], # arm
56+
Button.BACK: [(Channel.UNUSED_BUTTON_2, SwitchPosUp)], # disarm
57+
Button.LEFT_BUMPER: [(Channel.SWD_DOWN, SwitchPosUp)], # topic source input
58+
Button.RIGHT_BUMPER: [(Channel.SWD_DOWN, SwitchPosDown)], # topic source ethernet
59+
Button.A: [(Channel.SWB_DOWN, SwitchPosUp), (Channel.SWC_DOWN, SwitchPosUp)], # attitude
60+
Button.Y: [(Channel.SWB_DOWN, SwitchPosUp), (Channel.SWC_DOWN, SwitchPosMiddle)], # velocity
61+
Button.X: [(Channel.SWB_DOWN, SwitchPosUp), (Channel.SWC_DOWN, SwitchPosDown)], # bezier
62+
Button.CENTER: [(Channel.SWB_DOWN, SwitchPosMiddle), (Channel.SWC_DOWN, SwitchPosUp)], # attitude rate
63+
Button.B: [(Channel.SWB_DOWN, SwitchPosDown), (Channel.SWC_DOWN, SwitchPosDown)], # calibration
64+
}
65+
66+
axes_map= {
67+
Axes.RIGHT_STICK_LEFT: (Channel.RIGHT_STICK_RIGHT, -1),
68+
Axes.RIGHT_STICK_UP: (Channel.RIGHT_STICK_UP, 1),
69+
Axes.LEFT_STICK_UP: (Channel.LEFT_STICK_UP, 1),
70+
Axes.LEFT_STICK_LEFT: (Channel.LEFT_STICK_RIGHT, -1),
71+
Axes.DPAD_LEFT: (Channel.VRA_CW, -1),
72+
Axes.RIGHT_TRIGGER_OUT: (Channel.RIGHT_TRIGGER_IN, -1), #if =1 arm
73+
Axes.LEFT_TRIGGER_OUT: (Channel.LEFT_TRIGGER_IN, -1), #if =1 disarm
74+
}
75+
76+
class JoyToInput(Node):
77+
78+
def __init__(self):
79+
super().__init__('joy_to_input')
80+
self.subscription = self.create_subscription(
81+
Joy,
82+
'/joy',
83+
self.listener_callback,
84+
10)
85+
self.publisher = self.create_publisher(
86+
Input,
87+
'/cerebri/in/input', 10)
88+
89+
self.subscription # prevent unused variable warning
90+
self.msg = Input()
91+
self.set_default()
92+
93+
def set_default(self):
94+
self.msg.channel[Channel.RIGHT_STICK_UP] = 0;
95+
self.msg.channel[Channel.RIGHT_STICK_RIGHT] = 0;
96+
self.msg.channel[Channel.LEFT_STICK_UP] = 0;
97+
self.msg.channel[Channel.LEFT_STICK_RIGHT] = 0;
98+
self.msg.channel[Channel.SWA_DOWN] = SwitchPosUp;
99+
self.msg.channel[Channel.SWB_DOWN] = SwitchPosUp;
100+
self.msg.channel[Channel.SWC_DOWN] = SwitchPosUp;
101+
self.msg.channel[Channel.SWD_DOWN] = SwitchPosUp;
102+
self.msg.channel[Channel.VRA_CW] = 0;
103+
104+
def listener_callback(self, msg_joy):
105+
106+
for i in range(len(msg_joy.buttons)):
107+
if msg_joy.buttons[i] < 0.5:
108+
continue
109+
try:
110+
b = Button(i)
111+
except KeyError as e:
112+
print(e)
113+
continue
114+
print('pressed', b)
115+
ch_list = button_map[b]
116+
for ch, val in ch_list:
117+
print('channel', ch, 'val', val)
118+
self.msg.channel[int(ch)] = val
119+
120+
for i in range(len(msg_joy.axes)):
121+
try:
122+
ax = Axes(i)
123+
except KeyError as e:
124+
print(e)
125+
continue
126+
if ax in axes_map.keys():
127+
ch, scale = axes_map[ax]
128+
self.msg.channel[int(ch)] = scale*msg_joy.axes[i]
129+
if (round(self.msg.channel[Channel.RIGHT_TRIGGER_IN]) == 1):
130+
self.msg.channel[Channel.SWA_DOWN] = SwitchPosDown
131+
if (round(self.msg.channel[Channel.LEFT_TRIGGER_IN]) == 1):
132+
self.msg.channel[Channel.SWA_DOWN] = SwitchPosUp
133+
134+
self.publisher.publish(self.msg)
135+
136+
137+
def main(args=None):
138+
rclpy.init(args=args)
139+
140+
node = JoyToInput()
141+
142+
rclpy.spin(node)
143+
144+
145+
if __name__ == '__main__':
146+
main()

0 commit comments

Comments
 (0)