Skip to content
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

Open
wants to merge 90 commits into
base: wifi-with-cytron
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
a507572
adding potential field node and modifying twist_to_pwm to sub to pote…
ronggurmahendra Jan 31, 2023
358cc26
adding potential field as consideration
ronggurmahendra Feb 3, 2023
4bade8e
disable the dynamic speed and implement only the emergency brake
ronggurmahendra Feb 3, 2023
a61cc99
cahnge the emergencyStopThreshold
ronggurmahendra Feb 3, 2023
fe9a3e8
refactor and remove unnecesarry code
ronggurmahendra Feb 3, 2023
d2a9d44
update README.md
ronggurmahendra Feb 3, 2023
1d44b5a
update README.md
ronggurmahendra Feb 6, 2023
d8f340b
changes so that it consider current speed
ronggurmahendra Feb 6, 2023
5c86e50
deleting a unused file
ronggurmahendra Feb 6, 2023
3a75ac3
implement manual mode
ronggurmahendra Feb 6, 2023
a182f4e
fix bug viewer
ronggurmahendra Feb 6, 2023
5bf5994
fix bug at viewer
ronggurmahendra Feb 6, 2023
adbe730
refactor
ronggurmahendra Feb 6, 2023
942cc71
adding repelent field
ronggurmahendra Feb 6, 2023
e5efc7a
implement repelent field
ronggurmahendra Feb 6, 2023
e876461
editting tof_tes
ronggurmahendra Feb 6, 2023
9bdb4ad
adding unit test
ronggurmahendra Feb 6, 2023
197b61d
adding test file
ronggurmahendra Feb 7, 2023
5c10155
add test file cases
ronggurmahendra Feb 8, 2023
748cf03
refactor : move file to different directories
ronggurmahendra Feb 16, 2023
ada812e
refactor : update readme.md, delete cache
ronggurmahendra Feb 16, 2023
31155ec
refactor : add comment to bash file
ronggurmahendra Feb 16, 2023
7e9d54b
refactor : change file name
ronggurmahendra Feb 16, 2023
73ff751
dev : change to numpy min
ronggurmahendra Feb 16, 2023
9558472
refactor : use function at point cloud callback
ronggurmahendra Feb 16, 2023
7d9e460
dev : change tof.py from publishing Twist to publishing pwm
ronggurmahendra Feb 16, 2023
3960ee3
dev : change tof.py from publishing Twist to publishing pwm
ronggurmahendra Feb 16, 2023
238624a
fix minor bug with viewer
ronggurmahendra Feb 16, 2023
755654c
Debug : ignore NaN in min
ronggurmahendra Feb 16, 2023
a4fb1c3
Refactor : adding comment to the code
ronggurmahendra Feb 17, 2023
fe9119f
adding emergency brake from rosparam
ronggurmahendra Feb 17, 2023
aedd093
restore previous version of twist_to_pwm.py
ronggurmahendra Feb 17, 2023
28c50d2
refactor : remove global variable
ronggurmahendra Feb 17, 2023
61e36d8
refactor : move test file
ronggurmahendra Feb 17, 2023
af7ae5b
DEBUG : change the repelentField usage in testfile and change the sys…
ronggurmahendra Feb 17, 2023
84af085
refactor : adding comment to tof_test.py
ronggurmahendra Feb 17, 2023
40b25f2
edit bash file to not run twist_to_pwm.py
ronggurmahendra Feb 23, 2023
bd32c30
Resolving conflict
ronggurmahendra Feb 23, 2023
051e7a5
Delete src/__pycache__ directory
ronggurmahendra Feb 23, 2023
6406555
Delete software/__pycache__ directory
ronggurmahendra Feb 23, 2023
3da675c
Update README.md
ronggurmahendra Mar 1, 2023
5be0e12
Update README.md
ronggurmahendra Mar 1, 2023
0110972
Update README.md
ronggurmahendra Mar 1, 2023
2adaacc
refactor : requested PRchanges
ronggurmahendra Mar 5, 2023
5e99a2d
rename file to be more appropriate and refactor callback function
ronggurmahendra Mar 10, 2023
88a9f7f
fix the logic
ronggurmahendra Mar 10, 2023
f15a962
fix logic and refactor
ronggurmahendra Mar 13, 2023
3519c60
bug fix and add some additinal funciton to reppelent field
ronggurmahendra Mar 13, 2023
e41d699
delete unused code and uncomment import library
ronggurmahendra Mar 13, 2023
d62ac43
make another twist_to_pwm for the branch(skip a few parameter check) …
ronggurmahendra Mar 14, 2023
ff45f46
adding rotation to point cloud based of the installation
ronggurmahendra Mar 14, 2023
d3d6ab1
debug rotation matrix function bbut disable it since it use too many …
ronggurmahendra Mar 14, 2023
5b666d9
refactor and adding functions to repelent field
ronggurmahendra Mar 17, 2023
efa9bc2
resolving conflict
ronggurmahendra Mar 17, 2023
f7e9df5
debug
ronggurmahendra Mar 17, 2023
06e6c31
adding rear camera
ronggurmahendra Mar 17, 2023
d0386da
adding parameter
ronggurmahendra Mar 17, 2023
679b873
changing the quadratic parameter
ronggurmahendra Mar 17, 2023
b6935e6
delete unnessesarry code
ronggurmahendra Mar 19, 2023
d4f7255
adding another quadratic function
ronggurmahendra Mar 19, 2023
0fad87f
resolve conflict
ronggurmahendra Mar 19, 2023
eabad4e
split assisted navigation to 2 node
ronggurmahendra Mar 19, 2023
8ada88f
adding drive mode as a class, refactor assisted navigation iao and dr…
ronggurmahendra Mar 21, 2023
638f887
change parameter to use somulation and add simulation to bash file
ronggurmahendra Mar 22, 2023
1b0ef7d
change namefile of ToF implementation and add a IR sensor version
ronggurmahendra Apr 5, 2023
398ef70
fix a mistake with output linear
ronggurmahendra Apr 5, 2023
a5ee64f
ir publisher overhaul
ronggurmahendra Apr 5, 2023
1f7ddbf
refactor IRversion of assisted navigation
ronggurmahendra Apr 6, 2023
d9af7dd
change file name ro be more consistent
ronggurmahendra Apr 6, 2023
7e23a16
delete previous impelmentation using ToF and change the IR publisher…
ronggurmahendra Apr 6, 2023
6f17f5a
add a stand alone python script all in one for drive controller
ronggurmahendra Apr 6, 2023
904dfd2
remove unnesesarry code and modify according to IRsensor publisher
ronggurmahendra Apr 7, 2023
b6aa824
fix minor bug
ronggurmahendra Apr 7, 2023
6876bcd
resolving conflict
ronggurmahendra Apr 7, 2023
4d6cadd
change the index
ronggurmahendra Apr 7, 2023
2efcda0
add PMWmin to the final published output of the motor and make a simp…
ronggurmahendra Apr 12, 2023
d0f5537
Merge branch 'an_ToF' of https://github.com/Roboy/esp-wheelchair into…
ronggurmahendra Apr 12, 2023
9d725d8
adding USE_EMERGENCYBRAKE TO A ROSPARAM
ronggurmahendra Apr 12, 2023
fdd0ca6
debug minor typo bug
ronggurmahendra Apr 12, 2023
56af55b
change the USE_EMERGENCY BRAKE to rosparam, minor typo bug fix
ronggurmahendra Apr 12, 2023
b62220a
Change the Assisted drive code to a ROS package
ronggurmahendra Apr 13, 2023
6ed6c3a
adding ir_drive_controller to the executable
ronggurmahendra Apr 13, 2023
ce256c3
do recent changes to ir_drive_controller.py
ronggurmahendra Apr 13, 2023
711cf49
adding simulation model and launch file
ronggurmahendra Apr 14, 2023
ff433c3
add setup.py
ronggurmahendra Apr 14, 2023
5875bcf
Merge branch 'an_ToF' of https://github.com/Roboy/esp-wheelchair into…
ronggurmahendra Apr 14, 2023
5dd50b4
adding assisted_Navigation package
ronggurmahendra Apr 14, 2023
697b6c7
update readme.md
ronggurmahendra Apr 14, 2023
61ad4b7
update reamdme.md
ronggurmahendra Apr 14, 2023
37db21b
update readme.md
ronggurmahendra Apr 14, 2023
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
software/build

/*cache*

**/__pycache__
**/*.swp
**/*.swo
8 changes: 8 additions & 0 deletions scripts/assistedNavigation.bash
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
1 change: 0 additions & 1 deletion software/README.md

This file was deleted.

28 changes: 28 additions & 0 deletions src/README.md
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

```
7 changes: 7 additions & 0 deletions src/manual_control.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
28 changes: 28 additions & 0 deletions src/repelent_field_control.py
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
149 changes: 149 additions & 0 deletions src/tof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""
Copy link
Contributor

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

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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.sign(a) ?

Copy link
Collaborator Author

@ronggurmahendra ronggurmahendra Mar 6, 2023

Choose a reason for hiding this comment

The 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?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some reason to use this
@missxa


def mapPwm(x, out_min, out_max):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent use of snake and camel case

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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)

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent use of camel and snake case

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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()

60 changes: 60 additions & 0 deletions test/tof_test.py
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()