-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate ToF Sensor, Implement emergency Brake, and repellent field #4
base: wifi-with-cytron
Are you sure you want to change the base?
Changes from 44 commits
a507572
358cc26
4bade8e
a61cc99
fe9a3e8
d2a9d44
1d44b5a
d8f340b
5c86e50
3a75ac3
a182f4e
5bf5994
adbe730
942cc71
e5efc7a
e876461
9bdb4ad
197b61d
5c10155
748cf03
ada812e
31155ec
7e9d54b
73ff751
9558472
7d9e460
3960ee3
238624a
755654c
a4fb1c3
fe9119f
aedd093
28c50d2
61e36d8
af7ae5b
84af085
40b25f2
bd32c30
051e7a5
6406555
3da675c
5be0e12
0110972
2adaacc
5e99a2d
88a9f7f
f15a962
3519c60
e41d699
d62ac43
ff45f46
d3d6ab1
5b666d9
efa9bc2
f7e9df5
06e6c31
d0386da
679b873
b6935e6
d4f7255
0fad87f
eabad4e
8ada88f
638f887
1b0ef7d
398ef70
a5ee64f
1f7ddbf
d9af7dd
7e23a16
6f17f5a
904dfd2
b6aa824
6876bcd
4d6cadd
2efcda0
d0f5537
9d725d8
fdd0ca6
56af55b
b62220a
6ed6c3a
ce256c3
711cf49
ff433c3
5875bcf
5dd50b4
697b6c7
61ad4b7
37db21b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
software/build | ||
|
||
/*cache* | ||
|
||
**/__pycache__ | ||
**/*.swp | ||
**/*.swo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# this script run all the necessary setup to run the assisted navigation program using the teleop keyboard | ||
|
||
export ROS_HOSTNAME=192.168.0.124 | ||
# if you use other ip for ros master then do “export ROS_MASTER_URI=http://{ROS_MASTER_IP}:11311” | ||
export ROS_MASTER_URI=http://192.168.0.124:11311 | ||
roscore & | ||
python3 ../src/tof.py & | ||
rosrun teleop_twist_keyboard teleop_twist_keyboard.py |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Assisted Navigation | ||
|
||
## Prerequisites | ||
- install ROS1 Melodic | ||
|
||
## How to Run | ||
Setup ToF according to this documentation : https://devanthro.atlassian.net/wiki/spaces/WS2223/pages/2747269188/AN+Software | ||
|
||
Or | ||
|
||
Use Bagfile provided in this [link](https://drive.google.com/drive/folders/15IJ5Bk0Abo6tRJbfVam8PI72EpSFbLXI?usp=sharing) | ||
|
||
to use the bagfile, run | ||
``` | ||
rosbag play -l ToFSensor_sample.bag /royale_camera_driver/pointcloud:=/tof1_driver/point_cloud & | ||
rosbag play -l ToFSensor_sample2.bag /royale_camera_driver/pointcloud:=/tof2_driver/point_cloud | ||
``` | ||
|
||
And to run the assisted navigation node | ||
``` | ||
# if you havent have any rosmaster running and all the necessary Networking setup if you are using the ToFsensor | ||
roscore & | ||
|
||
# initialize the assisted navigation node with teleop keyboard | ||
python3 ../src/tof.py & | ||
rosrun teleop_twist_keyboard teleop_twist_keyboard.py | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class ManualMode: | ||
""" Manual Mode no modification between user input and output """ | ||
def __init__(self): | ||
return | ||
|
||
def control(self, inputLinear, inputAngular): | ||
return inputLinear,inputAngular |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
class RepelentMode: | ||
""" Repelent field mode get slower as the robot get near an obstacle """ | ||
DistFront = 1 | ||
DistBack = 1 | ||
def __init__(self): | ||
return | ||
|
||
def setDistanceFront(self, distance): | ||
""" Set member variable DistFront """ | ||
self.DistFront = distance | ||
def getDistanceFront(self): | ||
""" Set member variable DistFront """ | ||
return self.DistFront | ||
|
||
def setDistanceBack(self, distance): | ||
""" Set member variable DistBack """ | ||
self.DistBack = distance | ||
def getDistanceBack(self): | ||
""" Set member variable DistFront """ | ||
return self.DistBack | ||
def control(self, inputLinear, inputAngular): | ||
""" give an output based of the distance to an obstacle """ | ||
if inputLinear >= 0: | ||
outputLinear = self.DistFront | ||
elif inputLinear < 0: | ||
outputLinear = -1*self.DistBack | ||
|
||
return outputLinear,inputAngular |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
""" | ||
Usage: | ||
|
||
cd esp-wheelchair | ||
python3 software/tof.py | ||
|
||
""" | ||
import rospy | ||
from std_msgs.msg import Float64, Int16 | ||
from sensor_msgs.msg import PointCloud2 | ||
from geometry_msgs.msg import Twist | ||
import pcl | ||
import ros_numpy | ||
import numpy as np | ||
import pcl.pcl_visualization | ||
from manual_control import * | ||
from repelent_field_control import * | ||
|
||
# Parameters | ||
USEVISUAL = True # USEVISUAL if true will open a window that show the ToF sensor output | ||
PWM_MIN = 5 # PWM minimum value | ||
PWMRANGE = 40 # PWM range value | ||
EMERGENCYSTOPTHRESHOLD = 0.1 # emergency stop threshold roboy will stop if it detect a point below the thereshold | ||
|
||
|
||
# variable initialization | ||
if(USEVISUAL): | ||
viewer_front = pcl.pcl_visualization.PCLVisualizering() | ||
viewer_back = pcl.pcl_visualization.PCLVisualizering() | ||
inputLinear = None | ||
inputAngular = None | ||
manualMode = ManualMode() | ||
repelentMode = RepelentMode() | ||
Mode = manualMode | ||
|
||
sign = lambda a: (a>0) - (a<0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. np.sign(a) ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's actually part of the Alona's old code (software/twist_to_pwm.py) . i think it should work with np.sign(a), i originallky though that it's better to leave the original code. but what do you think is better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe some reason to use this |
||
|
||
def mapPwm(x, out_min, out_max): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent use of snake and camel case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
"""Map the x value 0.0 - 1.0 to out_min to out_max""" | ||
return x * (out_max - out_min) + out_min | ||
|
||
def pointCloudToNumpyArray(point_Cloud): | ||
""" convert a ROS's pointcloud data structure to a numpy array""" | ||
height = point_Cloud.shape[0] | ||
width = point_Cloud.shape[1] | ||
np_points = np.zeros((height * width, 3), dtype=np.float32) | ||
np_points[:, 0] = np.resize(point_Cloud['x'], height * width) | ||
np_points[:, 1] = np.resize(point_Cloud['y'], height * width) | ||
np_points[:, 2] = np.resize(point_Cloud['z'], height * width) | ||
return np_points | ||
|
||
def visualizePointCloud(viewer ,point_Cloud): | ||
""" visualize a numpy point cloud to a viewer """ | ||
p = pcl.PointCloud(np.array(point_Cloud, dtype=np.float32)) | ||
viewer.AddPointCloud(p, b'scene_cloud_front', 0) | ||
viewer.SpinOnce() | ||
viewer.RemovePointCloud( b'scene_cloud_front', 0) | ||
|
||
|
||
def pointCloudFrontCallback(msg): | ||
""" Callback function for front ToF sensor """ | ||
# change from pointcloud2 to numpy | ||
pc = ros_numpy.numpify(msg) | ||
front_Pointcloud_array = pointCloudToNumpyArray(pc) | ||
# visualize if USEVISUAL is TRUE | ||
if(USEVISUAL): | ||
visualizePointCloud(viewer_front, front_Pointcloud_array) | ||
# find the nearest point and store it at repelent Class | ||
minDist_front = np.nanmin(front_Pointcloud_array[:,2]) | ||
repelentMode.setDistanceFront(minDist_front) | ||
|
||
def pointCloudBackCallback(msg): | ||
""" Callback function for back ToF sensor """ | ||
# change from pointcloud2 to numpy | ||
pc = ros_numpy.numpify(msg) | ||
back_Pointcloud_array = pointCloudToNumpyArray(pc) | ||
# visualize if USEVISUAL is TRUE | ||
if(USEVISUAL): | ||
visualizePointCloud(viewer_back, back_Pointcloud_array) | ||
# find the nearest point and store it at repelent Class | ||
minDist_back = np.nanmin(back_Pointcloud_array[:, 2]) | ||
repelentMode.setDistanceBack(minDist_back) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unclear why this functions are almost identical but require to separate function, refactor to a single function |
||
def userInputCallback(msg): | ||
""" Callback funtion for user input. Takes the user input be it Twist_Teleop_Keyboard or joystick and based of variable Mode add moddification to speed """ | ||
# store user input | ||
inputLinear = msg.linear.x | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent use of camel and snake case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
inputAngular = msg.angular.z | ||
|
||
# call the current Mode control function | ||
outputLinear,outputAngular = Mode.control(inputLinear,inputAngular) | ||
|
||
# if the minimum distance is within a certaun threshold then brake | ||
if(repelentMode.getDistanceFront() < EMERGENCYSTOPTHRESHOLD and inputLinear > 0): # this is the front ToF | ||
print ("ABOUT TO COLLIDE FRONT EMERGENCY BRAKE") | ||
outputLinear = 0 | ||
elif (repelentMode.getDistanceBack() < EMERGENCYSTOPTHRESHOLD and inputLinear < 0): # this is the back ToF | ||
print ("ABOUT TO COLLIDE BACK EMERGENCY BRAKE") | ||
outputLinear = 0 | ||
# Check if wheelchair_emergency_stopped is defined | ||
if rospy.has_param('wheelchair_emergency_stopped'): | ||
# check if wheelchair_emergency_stopped is TRUE | ||
if rospy.get_param('wheelchair_emergency_stopped'): | ||
return | ||
|
||
# publish the TWIST output to simulation | ||
twist = Twist() | ||
twist.linear.x = outputLinear | ||
twist.angular.z = outputAngular | ||
assisted_navigation_pub.publish(twist) | ||
|
||
# publish the output to wheels | ||
rospy.loginfo_throttle(5, "Publishing pwm..") | ||
x = max(min(outputLinear, 1.0), -1.0) | ||
z = max(min(outputAngular, 1.0), -1.0) | ||
|
||
l = (outputLinear - outputAngular) / 2.0 | ||
r = (outputLinear + outputAngular) / 2.0 | ||
|
||
lPwm = mapPwm(abs(l), PWM_MIN, PWMRANGE) | ||
rPwm = mapPwm(abs(r), PWM_MIN, PWMRANGE) | ||
print(" left : ", sign(l)*lPwm, ", right : ",sign(r)*rPwm) | ||
pub_l.publish(sign(l)*lPwm) | ||
pub_r.publish(sign(r)*rPwm) | ||
|
||
if __name__ == "__main__": | ||
# init main loop | ||
rospy.init_node('assisted_Navigation') | ||
|
||
# initialize mode with manual mode | ||
Mode = manualMode | ||
|
||
# initialize wheels publisher | ||
pub_l = rospy.Publisher("/roboy/pinky/middleware/espchair/wheels/left", Int16, queue_size=1) | ||
pub_r = rospy.Publisher("/roboy/pinky/middleware/espchair/wheels/right", Int16, queue_size=1) | ||
|
||
# initialize TWIST publisher for simulation | ||
assisted_navigation_pub = rospy.Publisher('/roboy/pinky/middleware/espchair/wheels/assisted_navigation', Twist, queue_size=10) | ||
|
||
# initialize TWIST subscriber for user input | ||
user_input_sub = rospy.Subscriber('/cmd_vel', Twist, userInputCallback) | ||
|
||
# initialize pointlcloud subscriber for ToF sensor | ||
point_cloud_front_sub = rospy.Subscriber('/tof1_driver/point_cloud', PointCloud2, pointCloudFrontCallback) | ||
point_cloud__back_sub = rospy.Subscriber('/tof2_driver/point_cloud', PointCloud2, pointCloudBackCallback) | ||
|
||
print("publishing to /roboy/pinky/middleware/espchair/wheels/assisted_navigation. Spinning...") | ||
rospy.spin() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import unittest | ||
import rospy | ||
|
||
from std_msgs.msg import Float64 | ||
from sensor_msgs.msg import PointCloud2 | ||
from geometry_msgs.msg import Twist | ||
import pcl | ||
import ros_numpy | ||
import numpy as np | ||
import pcl.pcl_visualization | ||
import sys | ||
sys.path.insert(1, '../esp-wheelchair/src') | ||
|
||
from manual_control import * | ||
from repelent_field_control import * | ||
|
||
class Assisted_Navigation_Test(unittest.TestCase): | ||
""" Class to test use cases of assisted navigation functions """ | ||
|
||
def test_Repelent_Field(self): | ||
""" Test the Repelent Field class """ | ||
repelentMode = RepelentMode() | ||
inputLinear = 10 | ||
inputAngular = 11 | ||
minDistFront = 0.01 | ||
minDistBack = 10 | ||
repelentMode.setDistanceFront(minDistFront) | ||
repelentMode.setDistanceBack(minDistBack) | ||
outputLiner,outputAngular = repelentMode.control(inputLinear,inputAngular) | ||
# test speed when going forward | ||
self.assertEqual((outputLiner, outputAngular), (0.01, inputAngular)) | ||
repelentMode = RepelentMode() | ||
inputLinear = -1 | ||
inputAngular = 2 | ||
minDistFront = 2 | ||
minDistBack = 0.1 | ||
repelentMode.setDistanceBack(minDistFront) | ||
repelentMode.setDistanceBack(minDistBack) | ||
outputLiner,outputAngular = repelentMode.control(inputLinear,inputAngular) | ||
# test speed when going backward | ||
self.assertEqual((outputLiner, outputAngular), (-0.1, inputAngular)) | ||
|
||
|
||
def test_Manual_Mode(self): | ||
""" Test Manual Mode should just return the same value """ | ||
manualMode = ManualMode() | ||
inputLinear = 10 | ||
inputAngular = 1 | ||
self.assertEqual(manualMode.control(inputLinear,inputAngular), (inputLinear, inputAngular)) | ||
|
||
inputLinear = -10 | ||
inputAngular = 1 | ||
self.assertEqual(manualMode.control(inputLinear,inputAngular), (inputLinear, inputAngular)) | ||
|
||
inputLinear = 0 | ||
inputAngular = -1 | ||
self.assertEqual(manualMode.control(inputLinear,inputAngular), (inputLinear, inputAngular)) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the logic in this file seems incorrect.
user input comes on the following topics as PWM signal for the right and left wheel:
/roboy/pinky/middleware/espchair/wheels/right
/roboy/pinky/middleware/espchair/wheels/left
as std_msgs/Int16
Your module will become a middleman in this communication. So you will subscribe to listen to the command from the operator coming on the topics /roboy/pinky/middleware/espchair/wheels/right and /roboy/pinky/middleware/espchair/wheels/left. then you module must send the adjusted PWM to the motors on the topic e.g.
/roboy/pinky/middleware/espchair/wheels/right/adjusted
what your code is doing is subscribing to
Twist
. this message is not published from the operator