This commit is contained in:
Dylan Thrush
2018-07-20 15:04:33 -07:00
92 changed files with 176899 additions and 425 deletions

View File

@@ -0,0 +1,30 @@
\section{Introduction}
\subsection{Who Requested the Project?}
This project was requested by the Oregon State University Robotics Club's (OSURC) Mars Rover team.
\subsection{Why Was the Project Requested?}
This project was requested due to the Mars Rover team's need for a complete and functional ground station in a time frame that the existing software team may not have been able to accomplish. Additionally, new requirements for the ground station meant that the increased complexity was most likely going to require a more dedicated team to complete than the volunteers the club normally has working on the Mars Rover project.
\subsection{What is the Importance of the Project?}
This project solves some of the most common problems for the Mars Rover team in previous competition years. Historically, the team is allowed a 10-15 minute set up time during competition where the Rover must be turned on, ground station connected, and core systems tested before actually competing in a task. In previous years, the Rover's ground station has normally been a laptop with a handful of random pieces of hardware plugged into it over USB. Many times, this hardware has been damaged during transit and caused problems during the setup phase. Additionally, previous software has often been command line driven and extremely specialized meaning the software is difficult to use, difficult to teach, and has ended up getting re-written with each new Rover competition year.
\subsection{Our Client and Their Role}
Our client was Nick McComb, team lead and electrical lead of the Mars Rover team for the 2017-2018 competition year. His role was mainly supervisory, helping our group set up the initial requirements for the software, and occasionally making requests for additional features as the year progressed.
\subsection{Our Team Members and Roles}
\begin{itemize}
\item Chris Pham
\begin{itemize}
\item Rover mapping systems
\end{itemize}
\item Ken Steinfeldt
\begin{itemize}
\item Rover status systems
\end{itemize}
\item Corwin Perren
\begin{itemize}
\item Team Manager
\item Rover video systems
\item Program structure / startup
\end{itemize}
\end{itemize}

View File

@@ -0,0 +1,97 @@
\section{Requirements Document}
\subsection{Original Document}
\includepdf[pages=-, frame=true, scale=0.95]{02-requirementsdoc/group-design-requirements.pdf}
\subsection{Additions}
\subsubsection{Final Revision Document}
\includepdf[pages=-, frame=true, scale=0.95]{02-requirementsdoc/cs_group30_design_requirements_revision1_1.pdf}
\subsubsection{Description of Changes}
Version 1.1 of document was approved by all team members and stakeholder on 4/26/2018.
\begin{itemize}
\item Edited descriptions of competition from University Rover Challenge to Canadian International Rover Challenge
\begin{itemize}
\item The Mars Rover team changed competitions, so we updated the document to reflect this
\end{itemize}
\item Removed user story for manual video quality adjustment
\begin{itemize}
\item Our team implemented automatic quality adjustment, so manual adjustment was no longer necessary
\end{itemize}
\item Removed user story about easily seeing when an autonomous run has completed
\begin{itemize}
\item The new Rover competition does not require this display element
\end{itemize}
\item Removed user story about needing to view live logs
\begin{itemize}
\item Log viewing was determined to not be needed as there would be too many logs to sort through in a short amount of time
\end{itemize}
\item Removed user story about seeing network latency (round trip time)
\begin{itemize}
\item The network connection percentage was determined to be correlated with this value enough that this redundant element was not needed
\end{itemize}
\item Added constraint that the GUI software must have placeholder for GUI elements and software features that can not be completed due to waiting on Rover team progress
\begin{itemize}
\item Our team encountered many situations where we could not continue development due to lack of hardware/software on Rover
\item This new constraint allows us to still meet requirements if the bottleneck is the Rover team
\end{itemize}
\item Changed functional requirement for joystick statuses so that the SpaceNav mouse had its own requirement
\begin{itemize}
\item The team changed to a SpaceNav mouse earlier in the year and it was determined that an individual readout for this input device would be convenient
\end{itemize}
\item Changed functional requirement for battery status to battery voltage
\begin{itemize}
\item The battery status value was determined to be most useful as a voltage, rather than a percentage estimate
\end{itemize}
\item Removed functional requirement for Rover voltage statuses
\begin{itemize}
\item The Rover design changed so that only the battery voltage is measured
\end{itemize}
\item Changed functional requirement for bogie connection statuses to be individual wheel statuses
\begin{itemize}
\item The Rover software team got individual status information for each wheel, rather than a two wheel bogie group, and requested this change
\end{itemize}
\item Removed functional requirement for Rover network round trip time
\begin{itemize}
\item The network connection percentage was determined to be correlated with this value enough that this redundant element was not needed
\end{itemize}
\item Changed functional requirement for Rover arm joystick control to SpaceNav mouse control
\begin{itemize}
\item The team changed to a SpaceNav mouse over a second joystick for arm control
\end{itemize}
\item Removed functional requirement for way-point navigation path
\begin{itemize}
\item Expectations for how autonomy on the Rover would work would not easily give our team this information to display
\end{itemize}
\item Changed functional requirement for the autonomy indicator so that it shows whether autonomy is active rather than when autonomy is finished
\begin{itemize}
\item Changing to the Canadian International Rover Challenge made the old requirement unnecessary
\end{itemize}
\item Removed functional requirement for video FPS counters
\begin{itemize}
\item FPS counters would have been useful for manual video quality adjustment, but are no longer needed with automatic quality adjustment
\end{itemize}
\item Removed functional requirement for log file viewing
\begin{itemize}
\item Log files were too large to usefully sort through in a short amount of time
\end{itemize}
\end{itemize}
\subsection{Final Gantt Chart}
\includepdf[pages=-, frame=true, landscape, scale=0.95]{02-requirementsdoc/final_gantt.pdf}

View File

@@ -0,0 +1,5 @@
\section{Design Document}
\subsection{Original Document}
\includepdf[pages=-, frame=true, scale=0.95]{03-designdoc/group-design-document.pdf}
\subsection{Additions}
There were no additions to our original design document.

View File

@@ -0,0 +1,2 @@
\subsubsection{Chris Pham}
\includepdf[pages=-, frame=true, scale=0.95]{04-techreviewdocs/phamchr-tech-review.pdf}

View File

@@ -0,0 +1,2 @@
\subsubsection{Corwin Perren}
\includepdf[pages=-, frame=true, scale=0.95]{04-techreviewdocs/tech_review_perrenc.pdf}

View File

@@ -0,0 +1,2 @@
\subsubsection{Ken Steinfeldt}
\includepdf[pages=-, frame=true, scale=0.95]{04-techreviewdocs/steinfek-tech-report.pdf}

View File

@@ -0,0 +1,5 @@
\section{Tech Review Document}
\subsection{Original Documents}
\input{04-techreviewdocs/chris}
\input{04-techreviewdocs/ken}
\input{04-techreviewdocs/corwin}

View File

@@ -0,0 +1,4 @@
\section{Individual Blog Posts}
\input{05-blogposts/chris}
\input{05-blogposts/ken}
\input{05-blogposts/corwin}

View File

@@ -0,0 +1,448 @@
\subsection{Chris Pham}
\subsubsection{Fall}
\begin{itemize}
\item Week 1
\begin{itemize}
\item Activities
\begin{itemize}
\item Talked to the client to see if I can get into the OSURC Project
\end{itemize}
\item Summary
\begin{itemize}
\item I went ahead and looked through projects and then found a project that I liked, and then I emailed the stakeholder, Nick McComb, into adding me into his project.
\end{itemize}
\end{itemize}
\item Week 2
\begin{itemize}
\item Activities
\begin{itemize}
\item Emailed client when deciding to meet
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Client happened to be in California
\end{itemize}
\item Summary
\begin{itemize}
\item After being given our projects, we emailed our client and talked to our client over Slack to decide when to do our meeting about the project. We decided on Sunday for meeting up, while we think about the time schedule for the next assignments.
\end{itemize}
\end{itemize}
\item Week 3
\begin{itemize}
\item Activities
\begin{itemize}
\item Finishing Problem Statement
\item Started on Python Mastery 1
\end{itemize}
\item Summary
\begin{itemize}
\item I started working on my personal Problem Statement and was told from our client that we should do the Python Masteries. The client wanted to use it to prove that someone has enough understanding of the subject.
\end{itemize}
\end{itemize}
\item Week 4
\begin{itemize}
\item Activities
\begin{itemize}
\item Finished Problem Statement
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Python Mastery 1 is taking forever
\item Looking into alternatives to Code Academy.
\end{itemize}
\item Summary
\begin{itemize}
\item We worked on our collaborated Problem Statement and I started work on the Python 1 Mastery as suggested, but the actual website is expecting us to be completely new to coding. So in the end, I stopped because I was suffering while doing it.
\end{itemize}
\end{itemize}
\item Week 5
\begin{itemize}
\item Activities
\begin{itemize}
\item Start on rough draft of the Requirements Document
\item Finished the draft, and it is uploaded to GitHub
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Ended up being sick
\item Could not finish the mastery program yet
\end{itemize}
\item Summary
\begin{itemize}
\item We worked on our rough draft of the Requirements Document and that is now submitted to GitHub. Client was wanting some changes on the Problem Statement, but we changed the document to reflect those changes.
\end{itemize}
\end{itemize}
\item Week 6
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish the final draft of the Requirements Document
\item Sent to client for approval
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Issues with making the Gantt chart
\item Might need to start coding soon because the Mechanical team needs code
\end{itemize}
\item Summary
\begin{itemize}
\item We finished the Requirement Document and now I am thinking we are going to start on the project soon because we need to build parts of it for the mechanical engineering capstone group.
\end{itemize}
\end{itemize}
\item Week 7
\begin{itemize}
\item Activities
\begin{itemize}
\item Divide up the section of the Tech Review Document into coherent sections
\end{itemize}
\item Summary
\begin{itemize}
\item After submitting our assignment last week about the Requirements document, we went ahead after our meeting to build our skeleton for the tech review. After build that, I could not do anything else because was finishing other homework and plan on starting more of the assignment during the weekend.
\end{itemize}
\end{itemize}
\item Week 8
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish up rough draft of Tech Review Document
\item Gave our document to other people to review
\end{itemize}
\item Summary
\begin{itemize}
\item On Monday, I went ahead a my rough draft for the personal tech review. Then on Tuesday, we went ahead and reviewed someone else's document and got ours back on Thursday. Will probably finish final draft on Sunday.
\end{itemize}
\end{itemize}
\item Week 9
\begin{itemize}
\item Activities
\begin{itemize}
\item Finished final draft of Tech Review Document
\item Design document going to be started soon after
\end{itemize}
\item Summary
\begin{itemize}
\item On Monday, went and finished most of my tech review substance wise. On Tuesday, went ahead and tried to make it more readable.
\end{itemize}
\end{itemize}
\item Week 10
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish working on Design Document in parts
\item Finish working on recording
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Everything ended up being due on Friday
\item Had to save this for last
\end{itemize}
\item Summary
\begin{itemize}
\item We started working on our design document on Thursday and then completed most of it during Friday because of lack of time and things like that. The video was done around that time too, couldn't do it in my house early in the day because of noises.
\end{itemize}
\end{itemize}
\end{itemize}
\subsubsection{Winter}
\begin{itemize}
\item{Week 1}
\begin{itemize}
\item Activities
\begin{itemize}
\item Created a working schedule because we have to work on campus for direct access
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item When thinking about implementing my area, was thinking of live tiling, or pre-stitched image after.
\end{itemize}
\item Summary
\begin{itemize}
\item After everyone got back from break, after class on Tuesday, we make a work schedule for us to work on the Rover. I was assigned the Mapping functionality of the ground station and some of the design decisions are up to me to choose.
\end{itemize}
\end{itemize}
\item{Week 2}
\begin{itemize}
\item Activities
\begin{itemize}
\item Start on turning last year's code into Object-oriented instead of procedural for ROS
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Issues with big functions and converting them into stateful functions
\item Issues with downloading and speed of combining images into a single image
\end{itemize}
\item Summary
\begin{itemize}
\item I started on working the mapping core but I need to make the UI element and then try to make some speed improvements against PIL
\end{itemize}
\end{itemize}
\item{Week 3}
\begin{itemize}
\item Activities
\begin{itemize}
\item Working on adjusting maps in ways (Zoom/Scaling, Movement)
\item A smaller image can be cut out of the bigger image now
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item PIL zoom does not work like I expected, might have to use QT's zoom
\end{itemize}
\item Summary
\begin{itemize}
\item Mapping is working mostly now with now a moving bounding box that goes in the big image and cuts what is needed.
\end{itemize}
\end{itemize}
\item{Week 4}
\begin{itemize}
\item Activities
\begin{itemize}
\item Map stitching works up to 3 miles before skew happens
\item Got items to place correctly onto the map and display correctly
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Somehow got issues with converting latitude and longitude into x, y coordinates gave me extreme values but got solved by pasting it again with a different name...
\item I was not able to do much work during the week because of the issue above
\end{itemize}
\item Summary
\begin{itemize}
\item In the end, I got mapping to work correctly to around 3 miles before my projection becomes skewed enough to become really inaccurate. I also got a way to map waypoints to a system, however that is my next problem because I actually have no way of removing them from the map itself. I think I might make another image to lay on top of the map and then clear out the box by doing the same command but making it transparent instead of coloring it.
\end{itemize}
\end{itemize}
\item{Week 5}
\begin{itemize}
\item Activities
\begin{itemize}
\item Implemented my mapping to work with the Qt interface
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Had issues with signals and slots for my listener because it would stall out when trying to paste the image, had to create another thread to handle that.
\item Had slight issues with trying to merge my branch because the repository's folder structure had changed so much
\end{itemize}
\item Summary
\begin{itemize}
\item This week I was trying to setup my class to properly implement with my Qt Application. That was real questionable but I think I can fix it using some type of class constructor call in an Qobject instead of instantiating it from the map object itself. Another thing I did was integrated my fork/branch into the main branch and it looks way better now. Feels pretty good actually.
\end{itemize}
\end{itemize}
\item{Week 6}
\begin{itemize}
\item Activities
\begin{itemize}
\item Worked on displaying the map correctly without massive hangups
\item Worked on our capstone paper and video
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Issues with scheduling for working, but in the end we worked on our own mostly and then worked online when we needed to.
\end{itemize}
\item Summary
\begin{itemize}
\item Worked on the poster, video, and displaying images on the GUI. Productive I say.
\end{itemize}
\end{itemize}
\item{Week 7}
\begin{itemize}
\item Activities
\begin{itemize}
\item Nothing
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Was way too busy with midterms
\end{itemize}
\item Summary
\begin{itemize}
\item I did nothing at all. Feels bad man.
\end{itemize}
\end{itemize}
\item{Week 8}
\begin{itemize}
\item Activities
\begin{itemize}
\item Worked on some GPS/Waypoint handling for the map
\item Helped with recording for the club
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Overlay not working correctly, need to fix paste functionality
\end{itemize}
\item Summary
\begin{itemize}
\item Worked on the GPS and waypoint system, and for the club itself, I helped with filming and recording for the system reviews report needed.
\end{itemize}
\end{itemize}
\item{Week 9}
\begin{itemize}
\item Activities
\begin{itemize}
\item Worked on an correctly producing my indicator
\item Fixed on pasting the overlay image
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Was busy during Monday-Wends with plumber issues needing me to be home
\end{itemize}
\item Summary
\begin{itemize}
\item Fixed up my mapping and started working on integrating a waypoint system
\end{itemize}
\end{itemize}
\item{Week 10}
\begin{itemize}
\item Activities
\begin{itemize}
\item Built a intermediate system to handle button presses for way-point lists.
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item QT is really finicky with some function names and changes from Qt4 to Qt5 made some things harder to find
\end{itemize}
\item Summary
\begin{itemize}
\item During the start of the term, I personally did not work on much because of issues with registration
However, we did work out when we are expecting to work and meet up this term, which would be on Thursdays around 10 o'clock in the morning.
\end{itemize}
\end{itemize}
\end{itemize}
\subsubsection{Spring}
\begin{itemize}
\item Week 1
\begin{itemize}
\item Activities
\begin{itemize}
\item Meet up with the group to plan meetup times
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Issues in trying to get into classes
\end{itemize}
\item Summary
\begin{itemize}
\item After meeting up and trying to find
\end{itemize}
\end{itemize}
\item Week 2
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish integrating the GUI elements like buttons
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Issues with DMS conversions
\end{itemize}
\item Summary
\begin{itemize}
\item This week, I worked on completing the integration of the GUI. It turned out last term, that the East/West portion of the DMS conversion was not being set correctly. I spent a bit of time trying to get the correct float value to go down but then my minutes was completely off to the calculated DMS of the GPS location. Turns out that the box that contained the DMS location, would be limited to 0, 90, which actually needs to be 180 because it needs to span a half circle. Once that value was changed, the values were being set correctly.
\end{itemize}
\end{itemize}
\item Week 3
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish up linking up buttons and interactions in the GUI
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item More issues with trying to get into the negative values of the DMS portion
\end{itemize}
\item Summary
\begin{itemize}
\item Turns out that I forgot to set the sign of the longitude and latitude to their corresponding North/South and East/West values. That was easily done using with many possible implementations like equalities, sign function in python, and other things like that.
\end{itemize}
\end{itemize}
\item Week 4
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish up and submitting the poster
\item Edit documents to fit new the new competition
\end{itemize}
\item Summary
\begin{itemize}
\item This week, I was too busy to actually do much, and instead we worked on updating our documents to the new competition that we're going to now. And when that was done, we all just signed it and gave it to our client to sign. Also finished poster and it was sent to student media services.
\end{itemize}
\end{itemize}
\item Week 5
\begin{itemize}
\item Activities
\begin{itemize}
\item Finish working on multithreading and inputs.
\item Work on Midterm Report
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Was getting rate limited, needed to use a semaphore to limit thread usage.
\end{itemize}
\item Summary
\begin{itemize}
\item During Week 5, I worked on two important things, multi-threading the downloading of the image files from Google and integration of inputs into my mapping system. I was hitting issues with trying to use a pool but could not do it, in the end I needed to use processes. This in the ended up needing to be limited with semaphores so downloads could be regulated.
\item I also have started on integrating the GPS data that we are getting and applying that onto the map system that I've created. One problem is I forget how the data is formatted in the system and I need direct access to view the data that is being sent by the rover to parse correctly.
\end{itemize}
\end{itemize}
\item Week 6
\begin{itemize}
\item Activities
\begin{itemize}
\item Did not plan to do anything for Capstone at all.
\end{itemize}
\item Summary
\begin{itemize}
\item Did not do anything pertaining to Capstone at all.
\end{itemize}
\end{itemize}
\item Week 7
\begin{itemize}
\item Activities
\begin{itemize}
\item Present at Expo
\item Hunt for items that we need for Expo
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item Someone actually destroyed some motor nodes, so only 4 or the 6 wheel positions only move.
\item Corwin in the end, converted the connectors to USB and then connected those via USB.
\end{itemize}
\item Summary
\begin{itemize}
\item We met up on Thursday to find out what we actually need for Expo and what will be provided for Expo or not. It turned out that a Electrical team member blew a series of driver nodes, and had to build something to replace it. One method was making an emulator, or just run a newer number of nodes if the complete system cannot boot. It only turned out the controller boards only got fried, and so it had to get rigged to run off of USB. The day of Expo, we went to our position, and realized that we only had one table shared with the other Mars Rover Team Capstone project, and had to find the coordinator to get us another table. That was resolved, and then we went ahead and got all the stuff from the club room, and brought it to our table and presented.
\end{itemize}
\end{itemize}
\item Week 8
\begin{itemize}
\item Activities
\begin{itemize}
\item Nothing at all
\end{itemize}
\item Problems / Solutions
\begin{itemize}
\item With nothing working correctly, because of the blown electronics, other interfaces could not be used like the GPS information.
\end{itemize}
\item Summary
\begin{itemize}
\item Nothing could be done, with the lack of integrating parts from the rest of the rover team.
\end{itemize}
\end{itemize}
\item Week 9
\begin{itemize}
\item Activities
\begin{itemize}
\item Nothing at all
\end{itemize}
\item Summary
\begin{itemize}
\item Did nothing, because of other homework assignments taking my time.
\end{itemize}
\end{itemize}
\item Week 10
\begin{itemize}
\item Activities
\begin{itemize}
\item Started and finished the final video report and started on the final paper.
\end{itemize}
\item Summary
\begin{itemize}
\item We started our video on Thursday and finished on Friday, and then started working on this paper over the weekend. We are expecting to finish this whole document around Tuesday afternoon.
\end{itemize}
\end{itemize}
\end{itemize}

View File

@@ -0,0 +1,590 @@
\subsection{Corwin Perren}
\subsubsection{Fall}
\begin{itemize}
\item Week 1
\begin{itemize}
\item Week 1.3
\begin{itemize}
\item Looked at and selected projects for my preference submission.
\end{itemize}
\item Summary
\begin{itemize}
\item Looked through potential projects and submitted project preferences. I also contacted and got a positive confirmation from Nick McComb, the stakeholder for the OSURC Mars Rover capstone project, that myself and Christopher Pham were requested to work on the project via an email to McGrath.
\end{itemize}
\end{itemize}
\item Week 2
\begin{itemize}
\item Week 2.2
\begin{itemize}
\item Took class notes
\end{itemize}
\item Week 2.3
\begin{itemize}
\item Group wrote an email to send to our client, Nick
\item Sent said email to Nick
\end{itemize}
\item Week 2.7
\begin{itemize}
\item Had a meeting with Nick McComb, Kenneth, and Chris to covered the design guidelines document
\end{itemize}
\item Summary
\begin{itemize}
\item Met with group members Chris and Kenneth, where we exchanged contact information and coordinated contacting our client to set up a meeting. We made contact and set up a video conference call for Sunday to cover project details. We also were added to the rover team's slack, which we'll use for primary communication in the future. For future use, we were also added to the club's GitHub account.
\end{itemize}
\end{itemize}
\item Week 3
\begin{itemize}
\item Week 3.1
\begin{itemize}
\item Worked on and completed the rough draft problem statement
\item Did some initial research on the tools and frameworks our team will be using for this project
\end{itemize}
\item Week 3.2
\begin{itemize}
\item Started communication as a group with our TA to set up a weekly meeting time.
\end{itemize}
\item Summary
\begin{itemize}
\item After communicating with our client on Sunday, I took Sunday night and Monday to work on and complete the problem statement document. I also began to look more into the tools and frameworks we will be using on this project. Our group also started emailing our TA, Andrew, to set up a weekly meeting time, which is still not set in stone as Andrew is having a hard time scheduling so many groups at once. So far, it looks like they'll probably be on Tuesday afternoons as a remote session.
\end{itemize}
\end{itemize}
\item Week 4
\begin{itemize}
\item Week 4.4
\begin{itemize}
\item Made revisions to the problem statement document
\end{itemize}
\item Week 4.5
\begin{itemize}
\item Uploaded the final version of the document to the Github rep
\item Emailed client asking for revisions, and an approval email once ready
\item Embedded PDF in the group OneNote
\end{itemize}
\item Summary
\begin{itemize}
\item Our group used overleaf to group edit a join problem statement document, made revisions, and then emailed our client the final version. We're currently waiting for the approval email. I also embedded our PDF into the group OneNote per the follow-up email from Kirsten
\end{itemize}
\end{itemize}
\item Week 5
\begin{itemize}
\item Week 5.2
\begin{itemize}
\item Had TA Meeting
\end{itemize}
\item Week 5.4
\begin{itemize}
\item Worked on design requirements document with Chris and Ken
\end{itemize}
\item Summary
\begin{itemize}
\item We had a general meeting with our TA, the first of the term. Covered what the TA's role will be in our group. Covered that we seem to have a good plan on what our group will be doing. On Thursday, worked on the design requirements rough draft.
\end{itemize}
\end{itemize}
\item Week 6
\begin{itemize}
\item Week 6.2
\begin{itemize}
\item Had an update meeting with the TA
\end{itemize}
\item Week 6.3
\begin{itemize}
\item Worked on design requirements document with Chris and Ken
\end{itemize}
\item Week 6.4
\begin{itemize}
\item Continued working on design requirements document with Chris and Ken
\item Sent the semi-final draft to the client for potential edits
\end{itemize}
\item Week 6.5
\begin{itemize}
\item Made client edits to requirements document
\item Sent approval request email to client
\end{itemize}
\item Summary
\begin{itemize}
\item We had a general meeting with our TA and otherwise spent the week working on our requirements document. We sent a semi-final draft to our client Nick, who requested some minor edits, which we made and then sent him a final draft for approval.
\end{itemize}
\end{itemize}
\item Week 7
\begin{itemize}
\item Week 7.2
\begin{itemize}
\item Decided on projects to work on
\item Had TA meeting
\end{itemize}
\item Week 7.4
\begin{itemize}
\item Set up the ground station hardware in Graf 306
\end{itemize}
\item Summary
\begin{itemize}
\item Last week, we had a short quick meeting with our TA and then worked with each other to determine who would take what technologies for the tech review document. On Thursday, I set up the hardware our team will be running the ground station on set up in Graf 306. This included an Intel NUC, two joysticks, and two 1080p monitors. For now, it's connected via and ethernet switch to a desktop emulating the Rover.
\end{itemize}
\end{itemize}
\item Week 8
\begin{itemize}
\item Week 8.1
\begin{itemize}
\item Worked on rough draft for the tech review document
\end{itemize}
\item Week 8.2
\begin{itemize}
\item Did peer revisions on tech review documents in class
\item Turned in tech review
\item Met with TA
\end{itemize}
\item Week 8.5
\begin{itemize}
\item Worked on setting up the ROS framework on ground station software
\item Got simple rover drive control data sending to the rover
\item Got three video streams showing on the gui from the rover
\end{itemize}
\item Summary
\begin{itemize}
\item Last week, I finished my peer review, did an in class peer review, and turned it in. Our group also had another short meeting with our TA to make sure we were doing fine. On Friday, I began writing some initial ground station code. I got simple rover drive data sending over the network to the Rover. I also got three camera streams displaying in the GUI.
\end{itemize}
\end{itemize}
\item Week 9
\begin{itemize}
\item Week 9.1
\begin{itemize}
\item Worked on the final draft for the tech review
\end{itemize}
\item Summary
\begin{itemize}
\item I finished the final draft of my tech review on Monday night and submitted it mid-day Tuesday.
\end{itemize}
\end{itemize}
\item Week 10
\begin{itemize}
\item Week 10.2
\begin{itemize}
\item Meeting with TA
\end{itemize}
\item Week 10.3
\begin{itemize}
\item Created latex template doc on overleaf
\item Added initial content
\end{itemize}
\item Week 10.4
\begin{itemize}
\item Worked on final draft of the design document
\end{itemize}
\item Week 10.5
\begin{itemize}
\item Worked on final draft of the design document
\item Turned in the final draft of the design document
\end{itemize}
\item Summary
\begin{itemize}
\item I worked on and finished the final draft of our group design document and turned it int. We also had a 60 second meeting with our TA this week.
\end{itemize}
\end{itemize}
\end{itemize}
\subsubsection{Winter}
\begin{itemize}
\item Week 1
\begin{itemize}
\item Week 1.2
\begin{itemize}
\item We went and checked out our lab space in Graf 306
\item A work schedule was made to work at least five hours on Tuesday, Thursdays, and Saturdays, with the Saturday work overlapping with the Mars Rover weekly meetings
\item Planned to start actual development work Thursday
\end{itemize}
\item Week 1.4
\begin{itemize}
\item I filled out Asana with the core tasks that our software need to produce
\item Initial tasks were divvied up
\item I began continuing my work on processing and displaying video data
\end{itemize}
\item Summary
\begin{itemize}
\item Chris, Ken, and I met up after class on Tuesday so I could show them our lab space in Graf 306. We then came up with a work schedule for the term where we work on Tuesdays, Thursdays, and Saturdays for five hours a day at minimum. Then on Thursday, we all began actual work starting with creating tasks to complete and assigning initial tasks to everyone. I then continued working on the video display systems. I made the processing and display code slightly more efficient, and also added stream labels and fps counters directly on the video stream. On Saturday, I plan to add in auto adjustment of the stream quality based on whether the fps values are too low. Conversely, if the fps is pegged at the maximum, the quality will be increased until it starts to drop.
\end{itemize}
\end{itemize}
\item Week 2
\begin{itemize}
\item Week 2.2
\begin{itemize}
\item Continued working on video systems
\end{itemize}
\item Week 2.4
\begin{itemize}
\item Began refactoring video receiving class after verifying that the core class worked well.
\end{itemize}
\item Week 2.6
\begin{itemize}
\item Began refactoring the core of the ground station launcher file and restructured the program folder to have a cleaner layout.
\end{itemize}
\item Summary
\begin{itemize}
\item Over that week, I mainly worked on the video receiving class, testing it to make sure it was reliable and could show enough video streams at a smooth frame rate. Once that was verified, I began refactoring the class into a new version that would be able to include turning on and off video streams and adjusting what streams were shown in what display box. Later in the week, I began refactoring the ground station launcher file and folder structure so we'll have a cleaner base to work with as the project progresses.
\end{itemize}
\end{itemize}
\item Week 3
\begin{itemize}
\item Week 3.2
\begin{itemize}
\item Finished refactor of the launcher for the ground station
\item Added in check for if the rover is on the network when the software starts
\item Continued refactor of the video classes
\end{itemize}
\item Week 3.4
\begin{itemize}
\item Worked on adding advanced features of the video class refactor.
\item Added sleeping of video streams
\item Made number of cameras and resolutions dynamic
\item Can handle smart changing of what video displays are in each GUI element
\end{itemize}
\item Summary
\begin{itemize}
\item Over this week, I finished the refactor of the launcher for the ground station software. This included an addition to keep the program from launching if the Rover is not present on the network. Also, there is now a shared object that can be passed to all the important classes in the program to allow for easy interfacing with signals from other classes and the GUI. I also made significant progress on the Video class refactoring. At the moment, the GUI can now display a dynamic number of video streams, and also allows for dynamically changing which stream is displayed in each video display element. I've also added the ability to disable video streams where it now actually stops pulling in the video data to lower bandwidth usage. This includes "sleeping" video streams that aren't explicitly disabled, but are not actively being shown. Lastly, I finished the core of the GUI layout using QT Designer so the core widget modules now match the layouts defined in our documents from last term.
\end{itemize}
\end{itemize}
\item Week 4
\begin{itemize}
\item Week 4.2
\begin{itemize}
\item Continued work on video systems
\item Assisted main software team with their GPS and IMU firmware, as well as creating a ROS node to broadcast said data. Goal is to get GPS data sooner so Chris has that data to work with.
\end{itemize}
\item Week 4.4
\begin{itemize}
\item Assisted with firmware programming for drive control micro-controllers
\item Assisted on work on the motor controllers drive nodes for ROS so our team can have a node to send drive commands to.
\end{itemize}
\item Summary
\begin{itemize}
\item This week was mostly spent helping with development on software packages that will help support the ground station software on the Rover. Our group is getting to the point where having working ROS nodes on the Rover and hardware to control is getting more important, so I decided to help speed up the process. I worked on firmware for the micro-controllers that processed raw GPS and IMU data as well as the motor controller. I then also helped write the ROS nodes to interact with these micro-controllers so they could be controller using normal ROS topics.
\end{itemize}
\end{itemize}
\item Week 5
\begin{itemize}
\item Week 5.2
\begin{itemize}
\item Took a break from capstone software to assemble revision 1 of the Mars Rover Iris control board
\item Spent 10 hours assembling Iris
\end{itemize}
\item Week 5.4
\begin{itemize}
\item Spend 7 hours testing and fixing electrical problems with the Iris board
\item Ending board had full functionality on the 12 communication ports
\end{itemize}
\item Summary
\begin{itemize}
\item This week was pretty light for capstone as I had midterms. Additionally, I decided to take some time to help the Mars Rover electrical team, and assembled their Iris central control board. Further development of the ground station will be greatly helped by having missing hardware finally connected to the Rover to talk to, so I figured spending some extra time helping this to happen would be useful for the team overall, and then by proxy, our capstone team. Assembly, testing, and modifications took 17 hours. I also wrote a simple motor driver node for ROS so the motor driver board could be tested using control from ROS topics.
\end{itemize}
\end{itemize}
\item Week 6
\begin{itemize}
\item Week 6.2
\begin{itemize}
\item Worked on and completed rough draft of expo poster
\item Helped Chris with mapping integration
\item Wrote simple joystick control file to be run on ground station
\item Added UI elements
\end{itemize}
\item Week 6.4
\begin{itemize}
\item Worked on midterm report
\end{itemize}
\item Summary
\begin{itemize}
\item This week was spent mostly working on administrative stuff for the capstone project. A rough draft of the expo poster was made, and the later part of the week was spent working on the midterm report and video. However, I also did create a simple test file that takes in joystick data on the ground station, and broadcasts it to a ROS topic, allowing for remote driving of the motor connected to the Rover through Iris.
\end{itemize}
\end{itemize}
\item Week 7
\begin{itemize}
\item Week 7.4
\begin{itemize}
\item Helped with SAR work
\item Made multi-threaded controller code.
\item Tested nimbo\_sender / receiver
\end{itemize}
\item Summary
\begin{itemize}
\item This week I helped out getting things ready for the Rover systems acceptance review, one of the pivotal turning points for the rover team. This involved cleaning up code and getting remote drive systems working. As part of this, I re-wrote the joystick control test file to be multi-threaded and also got drive working with nimbro\_transport, which allows for UDP sending / receiving of ROS topics for packet loss allowable topics such as drive and video.
\end{itemize}
\end{itemize}
\item Week 8
\begin{itemize}
\item Week 8.2
\begin{itemize}
\item Continued work for SAR deadline
\item Wrote drive sender so that sent control data matched the refactored drive\_receiver on the Rover
\item Joystick control speed limiting using throttle axis, shows speed limit and tank output on GUI.
\item Tuned drive sender until remote drive over radio was smooth
\item Helped with refactor of whole GitHub structure
\item Needed for easy access to shared ROS packages due to nimbro addition
\item Made major updates to GUI design, moved closer to matching design document.
\item Arm joint position placeholders
\item Rover status placeholders
\item Added pause feature to drive controller, starts in pause by default
\item Added mock compass image to the GUI, for show during SAR video
\item Tested remote drive and video with no tether! Drove around parking lot from ground station.
\end{itemize}
\item Week 8.3
\begin{itemize}
\item Helped create videos for SAR demo
\end{itemize}
\item Summary
\begin{itemize}
\item Due to the SAR deadline being March 2, I put in extra time this week to make systems work as well as they could for the demo video made on Wednesday. For the demo, we wanted at minimum remote Rover drive working, video systems buttoned up and working well. On this note, wrote a joystickcontrolsender class to match the new rover\_drive system implemented on the rover. This code was the finalized version of the testing joystick code I'd written. I also added the ability to use the throttle stick on the joystick to act as a speed limit for the drive commands sent. So a setting of 50\% would scale the full joystick range to only 50\% speed. Additionally, a pause feature was added so that the rover cannot be accidentally controlled when the GUI first starts up. A button on the joystick disables pause state, and can be used to toggle the state while the gui is running. Also, gui elements on the right monitor were updated to show the speed limit value and the current power output to each of the sides of the rover in tank drive form. After making these changes, I tuned update rates and nimbro transport rates to get driving smooth again.
Due to the addition of nimbro\_network, I refactored the GitHub software repository to make it easier to share packages between the rover and ground station software. This was needed so that both had access to custom designed message formats in ROS. I also worked a lot this week to update the look of the GUI to match our design document. Placeholders images or Qt GUI elements for arm joint positions, rover statuses, and the compass were all added.
On Tuesday night, I and the team's mechanical lead, Ben, tested actual real remote drive of the Rover. We placed the rover in the Graf parking lot where he followed it with a kill switch tether, and I drove the rover over the remote radio link from the 3rd floor of Graf with the antenna sticking out the window. Tests showed that overall everything worked well! Driving was mostly smooth and video responsive enough to feel comfortable driving. Then on Wednesday, we remotely drove the rover on the catwalk of 3rd floor Graf and took videos of both the Rover driving, and of me controlling it at a now largely flushed out looking ground station.
\end{itemize}
\end{itemize}
\item Week 9
\begin{itemize}
\item Week 9.2
\begin{itemize}
\item Tuned some parameters of the drive controller so that driving was a bit smoother after remote drive testing the previous week.
\item Rewrote video\_receiver and coordinator to include new topic to control which video resolution is enabled, side effect of nimbro
\item Added auto resolution adjustment of video\_receivers
\item Tested new end effector video stream
\end{itemize}
\item Week 9.4
\begin{itemize}
\item Added joystick control sending and receiving for selection of cameras in GUI
\item Helped Chris integrate full map system into master branch
\item Added the ability to move compass, just spinning never-ending in one direction.
\end{itemize}
\item Summary
\begin{itemize}
\item After testing real remote driving the previous week, I tuned parameters on the rover and the ground station to get remote drive working just a little bit smoother and with no dropouts. After that, I worked on rewriting my video\_receiver and video\_coordinator threads so that instead of simply opening the designed video stream, the ground station would tell the rover which stream to send. This was a side effect of using the UDP nimbro\_network transports. Because UDP continually spams data at the ground station, it was in effect sending all the different resolution steams to the ground station all the time. This extra bandwidth was definitely something the team didn't want. During the rewrite, I also added automatic resolution adjustment of the video\_receivers based on averages of the received frame rates.
I also tested and integrated receiving and showing the fourth and final video stream from the end\_effector, which worked as intended. Receiving of rover\_statuses was also integrated into the roslaunch file and I tested receiving those statuses from command line. I ended up adding and additional manual update message for that package on the rover so the ground station could request and initial state when the software first starts up, and tested this via rostopic publish from the command line.
As I'd gotten the joystick controlclass cleaned up the previous week, I also wrote and tested joystick control of which video streams were shown on the three gui elements while I was rewriting the receivers and video coordinators. Two sets of two buttons each on the joystick now cyclically change which element is selected via an orange ring around the outside as well as what camera stream is current shown. The trigger on the joystick disables the currently selected stream.
I also helped integrate Chris' mapping system into the master branch of the rover software before ending the week by writing the threaded class needed to make the compass spin. I ended by making the compass spin in one direction indefinitely.
I sat down in the middle of the week and helped the mechanical team lead determine the final design for the quick deployment ground station box, which he then began fabricating.
\end{itemize}
\end{itemize}
\item Week 10
\begin{itemize}
\item Week 10.2
\begin{itemize}
\item Made nice new compass image asset as vector image in Inkscape, exported as PNG
\item Compass now can respond to heading updates (once we get them). For now, left/right clicks change heading
\item Heading and next goal heading update
\item Arm joint positions spin/stop when clicked. Placeholders.
\item Help Ken integrate his system statuses with the GUI
\item Placeholders for waypoint entry/editing
\item Wrote test code to get info from the M2 radios, set channel
\item Refactored M2 radio test code into one module to set radio channel from GUI, added GUI button and setting. Tested and works.
\item Helped Ken debug thread context issues updating GUI
\item Added pitch and roll indicators for Rover for when we get that data
\end{itemize}
\item Week 10.4
\begin{itemize}
\item Made minor changes to statuses so that it looked nicer
\item Added OSURC logo to top left corner of GUI per Nick's request
\item Made numerous UI changes to help clean up look.
\item Refactored joysticksystems to inputsystems
\item Added spacenavsystems to read in spacenav mouse data
\end{itemize}
\item Summary
\begin{itemize}
\item This final week was spent mostly dealing with non-critical updates to the look and feel and glue logic for the GUI. I made a brand new compass asset in inkscape that looks like a real compass and exported it as a high resolution PNG to be imported in the GUI. I then finished off the compass class allowing for the compass to rotate to a heading if one is received from the Rover. As the rover is not currently broadcasting this data, left and right clicking on the GUI element causes it to add or subtract five degrees from a mock heading, whose update is then shown. Additionally, I also made the textual readouts show the current heading to prove we have control of them. I did something similar with the arm joint positions by making them all spin when left clicked and stop spinning when right clicked. These are placeholders until we get arm data later next term.
Ken got his system statuses class ready to integrate into the main GUI and launcher, so I helped him do that and make the minor changes needed to make his thread start and stop properly. I also helped him debug a thread context issue that was causing weird flickering on the status display elements. Afterwards, I added the gui elements for manually entering GPS coordinates for adding to the waypoints listings.
I wrote test code to interact with the Rocket M2 radios we're using to get the status data we'll end up needing, such as signal strength, channel, and throughput. I also got test code working to set the 2.4GHz channel that the radios are set to. Afterwards, I wrote a class to handle the channel updates, made gui elements to control it, added this class to the GUI, and tested it. Now, a channel from 1-11 can be set from the GUI. After pressing the button, radios are reconnected and data returns after 4-10 seconds.
Near the end of the week, I made mostly graphical changes to the GUI such as adding and OSURC logo to the top left corner, and adjusting spacing and layouts to be more pleasing. I ended this week by refactoring the joysticsystems package into inputsystems before adding the spacenavsysystem class. I currently have this class reading in and storing SpaceNav mouse data in a class variable, waiting to be broadcast to appropriate systems once they're ready to accept the control data.
The mechanical team also showed us the assembled quick-deployment box, which should be ready for painting and then hardware install in the next few days.
\end{itemize}
\end{itemize}
\end{itemize}
\subsubsection{Spring}
\begin{itemize}
\item Week 1
\begin{itemize}
\item Week 1.2
\begin{itemize}
\item Talked with group mates over slack about what needs to be completed this term
\item Generally talked about the fact that the team was changing competitions
\item Didn't work on the project as everyone was busy
\end{itemize}
\item Summary
\begin{itemize}
\item Chris, Ken, and I chatted over slack about what needs to be done over the term. Not much is left, and a lot of the work is waiting on Rover hardware. Also, we talked about the fact that the Rover team has switched to the Canadian International Rover Challenge.
\end{itemize}
\end{itemize}
\item Week 2
\begin{itemize}
\item Week 2.4
\begin{itemize}
\item Did a work day on Thursday, I worked on SpaceNav mouse integration.
\item I registered the team for EXPO
\item I also dealt with sending in expo release forms
\item We made a list of all tasks left to be completed
\item Went over design requirements and found a few that need to be modified due to Rover design changes over the year.
\end{itemize}
\item Summary
\begin{itemize}
\item We all met on Thursday for a work day. I worked on continued integration of the SpaceNav mouse, getting values normalized and broadcasting to Chris could use it to pan the map. I also registered the team for EXPO and dealt with sending in the media release forms for our team. I also went over the design requirements document with Chris and Ken and found some elements that need to be changed as they're either no longer pertinent, or have changed enough that it would be better to update their descriptions. We'll be making these changes in the following week. Part of this was looking at the differences in the rules for University Rover Challenge vs the Canadian competition the team is now going to.
\end{itemize}
\end{itemize}
\item Week 3
\begin{itemize}
\item Week 3.4
\begin{itemize}
\item Worked on Rover side software to help get more statuses for Ken
\item Made new and worked on new rover package rover\_arm to get rover arm integrated with ROS
\end{itemize}
\item Summary
\begin{itemize}
\item This week I worked on Rover code rather than ground station code as we're now mostly waiting on features of the Rover to be finished before we can implement the appropriate ground station side code. This involved me getting rover battery status monitoring working and sending to the ground station as well as helping another member get GPS statuses doing the same. I also made and began work on the Rover's rover\_arm package to interface with the IONI motor controllers used to move the arm. I got a simple package compiling with both the ROS packages and simplemotion library and got an arm motor moving with it.
\end{itemize}
\end{itemize}
\item Week 4
\begin{itemize}
\item Week 4.2
\begin{itemize}
\item Finished final draft of poster
\end{itemize}
\item Week 4.6
\begin{itemize}
\item Worked on and submitted a revised version of our design requirements document.
\end{itemize}
\item Summary
\begin{itemize}
\item This week, I worked with my team to make the final changes to our poster for expo. I then submitted the poster for printing. I also worked with my team to make modifications to our design requirements document to reflect the team's change to the Canadian International Rover Challenge. We got the revised document signed off by all group members and the stakeholder and emailed it to the professor, TA, and stakeholder.
\end{itemize}
\end{itemize}
\item Week 5
\begin{itemize}
\item Week 5.6
\begin{itemize}
\item Worked on midterm progress report
\end{itemize}
\item Summary
\begin{itemize}
\item This was a busy midterm week, so I just worked on the midterm report and presentation.
\end{itemize}
\end{itemize}
\item Week 6
\begin{itemize}
\item Summary
\begin{itemize}
\item No work was done this week
\end{itemize}
\end{itemize}
\item Week 7
\begin{itemize}
\item Week 7.5
\begin{itemize}
\item Went to expo!
\end{itemize}
\item Summary
\begin{itemize}
\item Our team was at expo! Things went well! The demo was smooth.
\end{itemize}
\end{itemize}
\item Week 8
\begin{itemize}
\item Week 8.6
\begin{itemize}
\item Got the ground station controlling the pan/tilt node
\end{itemize}
\item Summary
\begin{itemize}
\item I finished the firmware for a prototype pan/tilt node and wrote the code for the GUI to control it using the joystick. Tested and worked!
\end{itemize}
\end{itemize}
\item Week 9
\begin{itemize}
\item Summary
\begin{itemize}
\item No work was done this week
\end{itemize}
\end{itemize}
\item Week 10
\begin{itemize}
\item Week 10.2
\begin{itemize}
\item Started final report document
\end{itemize}
\item Week 10.4
\begin{itemize}
\item Worked on final video presentation.
\item Recorded my portion of presentation.
\end{itemize}
\item Summary
\begin{itemize}
\item This week, I've worked on the final video and started the final report document to finish off capstone.
\end{itemize}
\end{itemize}
\end{itemize}

View File

@@ -0,0 +1,355 @@
\subsection{Ken Steinfeldt}
\subsubsection{Fall}
\begin{longtable}{ | l | l | p{14cm} | }
\hline
Week & Day & Post \\
\hline
1 & Tuesday & Learned about capstone. \\
& Wednesday & Considered capstone projects. \\
& Friday & Decided on a capstone project. \\
\cline{2-3}
& Summary & Introduction to Capstone.
I spent most of the week getting my feet wet and understanding the project ahead of me. \\
\hline
2 & Tuesday & Learned of project assignment. Met group members. Got the project I wanted. Very excited. \\
& Sunday & Met with client 2:30 - 3:30 to discuss requirements and scope of project. \\
\cline{2-3}
& Summary &
Struggled with \LaTeX makefile. Still don't really understand how to make it go.
Big part of this week was learning about capstone project assignment.
Met with client for first time. \\
\hline
3 & Monday & Wrote and submitted project problem statement document. \\
\cline{2-3}
& Summary &
Wrote Problem Statement.
It was difficult to grasp the entire project so early but with help from the client and group members it went smoothly enough.
Good learning experience for getting to know the project and what we were getting ourselves into. Going forward we will need to become more acquainted. \\
\hline
4 & Summary & Weekly meeting set for Tuesdays at 3:50 starting next week. \\
\hline
5 & Tuesday & First meeting with TA Andrew Fielding at 3:50. \\
\hline
6 & Tuesday & Weekly meeting with TA Andrew. \\
\hline
7 & Tuesday & Weekly meeting with TA Andrew. \\
\cline{2-3}
& Summary & Requirement document individual assignments made and agreed upon. \\
\hline
8 & Tuesday & Weekly meeting with Andrew. \\
\cline{2-3}
& Summary & Worked on requirements document rough draft. \\
\hline
9 & Tuesday & Weekly meeting with Andrew. \\
\cline{2-3}
& Summary & This week is defined by the requirements document final draft. \\
\hline
10 & Tuesday & Weekly meeting with Andrew. Short meeting, we don't have much to discuss. Everything is well. \\
& Wednesday & Design document is created on Overleaf for group to work on. \\
& Thursday & Work on design document, research and some writing. \\
& Friday & Finish up design document. \\
\cline{2-3}
& Summary & This is the final week of classes. Thursday class is an optional workshop.
On Friday the design document is due. Team works on document all day. The document feels rushed and we're a little overwhelmed with it. We also have a difficult time figuring out roles and ownership as no code has yet been written. \\
\hline
\end{longtable}
\subsubsection{Winter}
\begin{longtable}{ | l | l | p{14cm} | }
\hline
Week & Day & Post \\
\hline
1 & Tuesday & First day of class.
First time group meets since winter break
\begin{itemize}
\item We meet in our lab
\item Discuss current state of code base
\item Discuss rover progress
\begin{itemize}
\item Current state
\item Issues, etc.
\end{itemize}
\item We set group work times for the term
\begin{itemize}
\item Tuesday and Thursday all day (more or less)
\item First group work day will be Thursday 1/11/18
\end{itemize}
\item Group needs to complete ROS tutorial before next work day
\item Winter term TA meeting time has not yet been determined
\begin{itemize}
\item Will likely be on Tuesday or Thursday as group will be together on those days
\end{itemize}
\end{itemize}
\\
\cline{2-3}
& Summary &
First week of class after winter break.
Group reviews state of current code base for the project.
Group meeting times are determined by group. These are Tuesdays and Thursdays. \\
\hline
2 & Tuesday &
Meet with group to work on project all day.
Begin writing StatusCore module that will grab system status information from the rover and display it to the user.
The rover will create publishers for the necessary statuses.
StatusCore module creates subscribers that read from the rover publishers.
Minimal processing should be done on the information coming from the rover.\\
\cline{2-3}
& Summary & Week spent primarily getting started with coding. Met with group at OSURC club meeting on Saturday. \\
\hline
3 & Tuesday & Met team at 10:00AM to work on system statuses \\
& Thursday & We reach out to project leader Nick McComb for approval of our capstone documents.
Nick responds later in the day with approval. \\
& Friday & Hear from TA Andrew to setup this terms weekly meetings.
Corwin responds with the preferred group meeting times which are 9-11 and 1-4 on Tuesdays. \\
& Saturday &
Unable to make the OSURC Mars Rover team meeting this Saturday due to not feeling well.
Work on system statuses from home. I am unable to finish SensorCore.py this week like I had hoped. However, I get a basic mockup going and generally make progress.
Repository is cleaned up and reorganized for clarity between different rover teams. This is much needed and well done but causes me difficulty as my branch becomes significantly diverged from the main branch. I am unable to quickly fix the divergence and will have to do so next week. \\
\cline{2-3}
& Summary &
Goal is to finish system statuses by the end of this week.
Not feeling well this week. \\
\hline
4 & Tuesday & TA meeting with Andrew
Learn about PyQt framework in order to create UI layout. It is more complicated than I thought it was. \\
& Thursday & Implemented clock into StatusCore and tested that it worked - this was one of the ways I attempted to learn the PyQT
Began working on the stopwatch module for the ground station. This is not as trivial as I thought it would be. \\
& Saturday & Went to Corvallis for OSURC Rover team meeting.
Worked with team in lab after meeting. Got stop watch working as an individual module, but not integrated with StatusCore. Includes push buttons, but needs to be left - right mouse clicks for the final product.\\
\hline
5 & Tuesday & TA meeting with Andrew
Midterms galore this week. I manage to design a very basic visual framework for the UI.\\
& Thursday & Visual framework is shown to team members and is not liked. I agree with the criticism and the UI for SensorCore is redesigned with the group. Instead of being text based it is now boxed labels that are either red, orange, or green depending on the values that are given. For simple on/off alerts, such as various module connectivity, the box is simply red or green.
Red - something is wrong - usually ~80\% and greater (if applicable) or a False boolean value
Orange - warning, approaching read usually ~70\%-80\% (if applicable)
Green - everything is great or a True boolean value \\
\hline
6 & Tuesday & TA meeting with Andrew.
Meet with team in Corvallis to work in lab. Group picture is taken and poster rough draft is created. We are happy with the poster and don't foresee a large redesign in the future.\\
& Thursday & Group works on midterm report for the class. Report is written together and videos are recorded. Videos are then stiched together. \\
\hline
7 & Tuesday & TA meeting with Andrew. Nothing of note
\begin{itemize}
\item Team gives status update
\item Nothing of concern
\item Team gets back to work on ground station software
\item Code base cleanup
\end{itemize}
\\
& Thursday & A little stymied by system status module on the rover side.
I help out with some random things while waiting for rover team. It's not a big deal. \\
\cline{2-3}
& Summary & This week is primarily dominated by the need to produce the SAR video for the competition. If this video doesn't get done the rover cannot compete.
This takes up a lot of the group's work time but work still gets done on the ground station.
Work is done on the joystick and Corwin integrates the nimbro packages to cutdown on latency between the ground station and rover. \\
\hline
8 & Tuesday & TA meeting - All is well. Project is progressing.
Work with rover team to get system values from rover
StatusCore values (green, orange, red) decided. \\
& Thursday & Meet team in Corvallis to work in lab.
Corwin has created scripts to ease the build process of the ground station. I sit down with Corwin to learn how to use these new scripts.
Work on StatusCore now that rover statuses are available for final integration. \\
\cline{2-3}
& Summary & A lot happens in week 8
\begin{itemize}
\item Repository is reorganized
\item New startup script is written for ground station
\begin{itemize}
\item This greatly helps development
\end{itemize}
\item Create some more placeholders for statuses in the UI
\item Rover drives!
\end{itemize} \\
\hline
9 & Tuesday & TA meeting. All is well and progressing.
Do not work from lab, will meet with team on Thursday.
StatusCore:
\begin{itemize}
\item Work on callback functions
\begin{itemize}
\item pickup rover statuses, evaluate their return functions
\end{itemize}
\item New system statuses added to rover
\begin{itemize}
\item System statuses are still likely to change
\end{itemize}
\item Needs to ask for update from rover on startup
\begin{itemize}
\item Peripheral systems, such as cameras, will not automatically update until a change has happened. I will need to poll the rover at startup to get initial status
\item This requires a subscriber on rover and a publisher in StatusCore
\end{itemize}
\end{itemize}
\\
& Thursday & Met with team in lab.
Begin push to meet Beta by week 10.
StatusCore work:
\begin{itemize}
\item Finalize UI look in QT designer
\item Properly lable/organize UI elements
\item Prepare for main branch integration
\end{itemize}
\\
\cline{2-3}
& Summary & I make some serious progress on StatusCore this week, plan to fully integrate into ground station next week to meet beta requirement.
\begin{itemize}
\item StatusCore look is (somewhat) finalized
\item Rover team gets their status module sending signals I can use
\item I clean up StatusCore in preparation for integration next week
\end{itemize}
\\
\hline
10 & Tuesday & Worked late to finally integrate StatusCore with main branch.
StatusCore module hit beta status and, with the help of Corwin, integrated into the project.
Some bugs were noticed, namely some flickering in the status alerts. The source of the bug was identified but not fixed.
\begin{itemize}
\item Problem: Did not think StatusCore would require the use of Slots
\item Solution: StatusCore needs to use QT slots in order to refresh nicely
\end{itemize}
\\
& Thursday & Last class of the term
After class we worked in the lab.
I hooked up the clock and fixed the StatusCore bugs that were identified on Tuesday \\
\cline{2-3}
& Summary & Final week of the term
\begin{itemize}
\item No TA meeting this week
\item Finally got StatusCore integrated into ground station
\begin{itemize}
\item Very successful, only a few bugs
\end{itemize}
\item Some bugs exist in StatusCore and are squashed (as far as we know)
\item Ground station hits beta!
\end{itemize}
All in all a very exciting week. Everyone is happy with our progress. \\
\hline
\end{longtable}
\subsubsection{Spring}
\begin{longtable}{ | l | l | p{14cm} | }
\hline
Week & Day & Post \\
\hline
1 & Tuesday & Code review of Spring break work on Rover and Ground Station.\\
& Wednesday & Class Meeting. \\
& Thursday & Team meets and goes over project. Change in venue discussed and new meeting times and requirements are discussed and agreed upon. \\
\cline{2-3}
& Summary &
This is the first week of Spring term.
First class meeting.
First week after Spring break so the team meets to go over the status of the project and any of the changes that occurred during the last two weeks.
The Rover team is no longer going to compete in the University Mars Rover Challenge, but instead will now compete in the Canadian International Rover Challenge.
Change in competition means that that some minor changes in requirements are needed, these changes are discussed.
The team also reviews the general status and progress of the project. New meeting times are discussed and agreed upon for the coming term.
\\
\hline
2 & Tuesday & Code review. \\
& Thursday &
Team meets.
Changes to design document are discussed. Work will need to be finalized and signed off on by TA Andrew.
StatusCore module changes will be small. Mostly subscribing to new publishers. Will need to wait for Rover team to get publishers up and running. May be most beneficial for me to do as much as I can to help Rover team.
\\
\cline{2-3}
& Summary &
Group deals with EXPO bureaucracy. This entails registering, requesting necessary equipment/power and signing the proper release forms.
Changes in competition requirements again discussed as they will require that the design document be amended to match the changes in software. The changes are minor but are changes none the less.
Changes to StatusCore module look to be minor. \\
\hline
3 & Tuesday &
Code review. Rover and Ground Station.
Work on stubbing out subscribers. \\
& Wednesday & Wired Article due. \\
& Thursday & Team meets.
Rover team expected to be ready to broadcast new statuses by the end of the week. Will work on them on Saturday. \\
\cline{2-3}
& Summary &
Beginning of week three the Rover team is still not ready for new statuses. This is not unexpected considering the quick change of design requirements.
Despite the lack of broadcasted statuses I can and do begin working on the foundational subscribers, etc. so that when the Rover team is ready I can pick them up in StatusCore.
Some Statuses are also deprecated as they will no longer be used. This includes Bogies. New subscribers are stubbed out.
WIRED! Article is due this week.
\\
\hline
4 & Tuesday & Code Review. StatusCore work. \\
& Thursday & Team meets.
EXPO poster is finalized, approved, and submitted.
Final changes to requirements document made and approved.
\\
\cline{2-3}
& Summary &
Continue to work on changes to StatusCore.
Final poser is due this week so it is finalized and submitted after being approved.
By the end of this week we deem software to be EXPO ready.
Amendments to project requirements are made, agreed upon, and signed-off on. This includes all team members as well as client/stakeholder.
\\
\hline
5 & Wednesday & Class meets. \\
& Thursday & Team meets, works on Spring midterm report/presentation. \\
\cline{2-3}
& Summary & This is week 5 and is mid-term season. Every member of the team is swamped with work.
I am not able to get very much done on the ground station software.
Team meets and works on Spring midterm report. Report and presentation are finished by the end of the week (of course).
\\
\hline
6 & Summary & Not much capstone work. Midterms and projects in other classes. \\
\hline
7 & Wednesday & Class meeting to prepare for EXPO. \\
& Friday & EXPO! Goes great. Our team does really well and has a lot of fun. \\
\cline{2-3}
& Summary & Expo happens this week. \\
\hline
8 & Summary & Did not work on ground station. \\
\hline
9 & Summary & Did not work on ground station. \\
\hline
10 & Thursday & Final video work begins. \\
& Friday & Final video presentation completed and submitted. \\
\cline{2-3}
& Summary & Team is all busy with finals and final projects.
Final presentation is due and completed by the team.\\
\hline
\end{longtable}

View File

@@ -0,0 +1,2 @@
\section{Poster}
\includepdf[pages=-, scale=1.04, landscape]{06-poster/poster_undergrad_expo_48x36_eecs.pdf}

View File

@@ -0,0 +1,79 @@
\section{Documentation}
\subsection{How the Project Works}
\subsubsection{Overview}
This project works by combining the power of Robot Operating System (ROS), the Qt framework, and Python to make a simple (for what it is), fast, easy to modify, and easier to understand piece of software to act as the front end for the Mars Rover. PyQt is used to load and show the UI and give the software programmatic access to GUI elements. It also provides intelligent multi-threading with easy to use cross-thread communication pipelines. The use of python has allowed our team to write this code in a fraction of the time it would have taken in another language such as C++. ROS is the framework that the Rover is running and is based around the concept of abstracting everything into ROS topics. These topics allow for easy sending and receiving of control and status information in a way that is standardized instead of having to use a custom network control protocol.
\subsubsection{Low bandwidth communications}
As the Rover is going to be at long range at many times, having as small of communication packets as possible is a priority. This helps keep control, video, and status information smooth and consistent. In order to facilitate this, the ground station and Rover both are using a package called nimbro\_network that allows for compression of ROS topics and also allows for us to choose between a TCP and UDP transport layer. Non-critical data that we want fast gets sent with compression over UDP and includes video data from the Rover and live drive commands from the ground station. Important and/or infrequent data is sent with or without compression depending and using the TCP transport. Data sent using TCP includes status information from the Rover and, in the future, waypoints and autonomy control.
\subsubsection{Program Logical Structuring}
To make the ground station software easier to modify and understand, it has been broken down into logical hierarchies. At the core of the ground station source folder are the Framework and Resources folders. The Resources folder contains all of the static assets for the application such as image files and the UI files that determine how the GUI visually looks. The Framework folder contains all of the logical sub-systems of the software such as VideoSystems, InputSystems, and MappingSystems. By separating all systems out like this, it makes it easy to know which files need to be looked at when fixing or adding to the ground station. At the root of the source folder is "ground\_station.py", the launcher file for the whole application. Under normal conditions, this file only needs to be modified if a new threaded class is being added for additional functionality. This file handles launching of the software, displaying the GUI, and management of the startup and shutdown of all running threads.
\subsubsection{Threading \& Adding Classes}
As this is a very large program with many systems running concurrently it has required the use of many threaded classes. To abstract away many of the complexities of this, the ground station main launcher file contains a method called add\_thread used to spin up a new threaded class from the Framework folder when one is made. This method also handles the graceful shutdown of the software when quit. In order for graceful shutdown, all of the program's child threads must not be running when the main launcher thread exits. If this is not the case, the application may hang or maintain unwanted connections in the background after it seems like it has exited. Anyone wishing to add new functionality to the GUI in a new file should copy an existing threaded class to use as a baseline.
\subsubsection{ROS in Python}
ROS is a large part of why this software has been much easier to write than it could have been. In Python, the ROS subsystem can be accessed by importing rospy and calling rospy.init to tell ROS that we have a new node running. At this point, topics can be subscribed to by giving it the topic path and a the data type of the topic. Conversely, to broadcast a new topic message, a publisher is made by providing the topic path, data type, and queue size. To actually broadcast a message, you create a new instance of the data type, fill it with the desired data, and use the publisher's ".publish()" method to send it. In our software, these topics are used for all communications to and from the Rover. Absolutely no custom communication methods are used to interface between the Rover and ground station. An important note about the ROS messages used are that most of these messages are custom defined. In order for rospy and python to know the required information about a custom message, the ROS package containing the message define must be built as part of the local catkin workspace. This is the reason that the ground station's setup script includes many packages other than for just launching the ground station. The extra packages give us access to custom messages needed for understanding status messages and sending drive commands.
\subsubsection{ROS Topic / Classes Block Diagram}
\includepdf[pages=-, scale=0.98]{07-documentation/Rover_Ground_Station_Block_Diagram.pdf}
\subsection{System Requirements}
\subsubsection{Hardware}
\begin{itemize}
\item 1x Computer running Ubuntu 16.04 LTS
\begin{itemize}
\item Intel core i5 or i7 equivalent processor
\item 4GB+ Ram
\item Minimum two display outputs
\end{itemize}
\item 2x 1080p Monitors
\item 1x USB Joystick
\item 1x SpaceNav Mouse
\item 1x USB Keyboard
\item 1x USB Mouse
\end{itemize}
\subsubsection{Software / OS}
\begin{itemize}
\item \href{http://wiki.ros.org/kinetic/Installation}{ROS Kinetic}
\item \href{https://www.python.org/}{Python 2.X} with the following packages:
\begin{itemize}
\item PyQt5
\item qdarkstyle
\item inputs
\item spnav
\item Pillow
\item paramiko
\item OpenCV 2
\item qimage2ndarray
\item numpy
\end{itemize}
\item Set the computer to an IP address of 192.168.1.15
\end{itemize}
\subsection{How to Install}
\begin{enumerate}
\item Create and \href{http://wiki.ros.org/catkin/Tutorials/create_a_workspace}{setup a catkin workspace} at "$\sim$/catkin\_workspace"
\item Add "source /home/[username]/catkin\_workspace/devel/setup.bash" to the end of your ".bashrc" file, replacing "[username]" with your account's username
\item Create the directory "$\sim$/Github" and clone the \href{https://github.com/OSURoboticsClub/Rover_2017_2018}{Rover\_2017\_2018} repository into it
\item Run the setup script at "$\sim$/Github/Rover\_2017\_2018/software/ground\_station\_setup.sh" to have the proper packages symbolically linked into your new catkin environment and for catkin\_make to be run automatically against it
\item If the catkin\_make process at the end of the script completes with no errors, you're ready to launch the ground station
\end{enumerate}
\subsection{How to Run}
\begin{enumerate}
\item Ensure all hardware from the requirements section above is plugged in
\item Ensure there is a network connection between the ground station computer and Rover
\item Open a terminal and run "roslaunch rover\_main ground\_station.launch"
\item The ground station should launch in full screen across both monitors
\item Press "ctrl-q" at any time to quit the application
\end{enumerate}
\subsection{User Guides}
\subsubsection{Client Requested Quickstart Guide}
\includepdf[pages=-, frame=true, scale=0.95]{07-documentation/Ground_Station_Quickstart.pdf}

View File

@@ -0,0 +1,5 @@
\section{Technical Resources}
\begin{itemize}
\item \href{http://wiki.ros.org/}{ROS Documentation}
\item \href{http://doc.qt.io/}{QT Documentation}
\end{itemize}

View File

@@ -0,0 +1,28 @@
\subsection{Chris Pham}
\subsubsection{Technical Knowledge Gained}
From this project, I learned quite a bit of technical knowledge doing this project over the year.
I learned how to use Qt and ROS enough to build a system using it, do not think I can do it well though.
After working with a large code base, I've learned to link projects in a tried grouped faction, close to the next object.
I think I know how to remove very stateful code, and breaking it into helper functions and the main class.
With the use of image manipulation, I feel that I know how to exactly how to use PIL, OpenCV, and other image software to stitch a bunch of files.
\subsubsection{Non-Technical Knowledge Gained}
For this project, I think I gained more of a time regulating skill, where I would have to say, I could not contribute to a project for a period of time, instead of doing half time on both projects or more.
Another point, is learning to ask for expectations and goals, and then slightly passing them because extra work would be unnoticed, and/or unused by the team, but too little would produce an incomplete product.
\subsubsection{Knowledge Gained of Project Work}
From this project, I learned that when you expect that something will take a period of time, expect truly to take double that.
Another take from this project is that, with some other projects that rely in other parts by other people, try to make something, and you could always try to fix it later.
Goals will always change, and be ready to change on a dime.
And in the end, without any type of leader or management, time will always be hard to come by, and everything will possibly be late.
\subsubsection{Knowledge Gained of Project Management}
For our group, there was not much of a need to police what someone did or did not do.
In the end, our biggest problem was the time commitments from having a group member in Hillsboro and only down Tuesday or Thursday which made some activities like weekend meetings near impossible.
\subsubsection{Knowledge Gained of Working in Teams}
For the group, we all had fun with each other and everyone else in the team and that made communications very easy to do.
Everyone was free form, and we did whatever we needed and the others would be accommodating in trying to fit that schedule.
When we could not meetup, we could at least use other means of communications like Slack to talk about expectations and code, which allowed for us do to whatever we need to do.
\subsubsection{Changes to Make if Starting Over}
If we were to start this over, I would like to some things to allow us to succeed further.
I think we would all make better plans at the start to allow for missteps of the team, and allow for easier prototyping.
That prototyping can allow for delays in the team, and then can be built with expected values instead of waiting for the system to be built.
Another thing would be getting testing data that we can use for integrated tests like GPS, Video, and Statuses, so we can just build a system using that data then it should be the same on the complete system.
I think for my section, I would actually use OpenGL instead of the PIL mapping system, because translating numbers randomly feels very weird, compared to my graphics history.

View File

@@ -0,0 +1,4 @@
\section{Conclusions and Reflections}
\input{09-conclusions/chris}
\input{09-conclusions/ken}
\input{09-conclusions/corwin}

View File

@@ -0,0 +1,38 @@
\subsection{Corwin Perren}
\subsubsection{Technical Knowledge Gained}
\begin{itemize}
\item Real-world ROS usage
\item Multi-monitor PyQt interfaces
\item Some nuances of low-bandwidth networking
\item Multiple video steam coordination
\item Better organization of large software projects
\end{itemize}
\subsubsection{Non-Technical Knowledge Gained}
\begin{itemize}
\item Better project time management skills
\end{itemize}
\subsubsection{Knowledge Gained of Project Work}
\begin{itemize}
\item Projects almost always take longer than expected
\item Requirements almost always change, even if only a little bit
\item Lack of proper organization can make working on a project near impossible
\end{itemize}
\subsubsection{Knowledge Gained of Project Management}
\begin{itemize}
\item Scheduling times to do things like project work where everyone can show up can be difficult
\end{itemize}
\subsubsection{Knowledge Gained of Working in Teams}
\begin{itemize}
\item Continuous communication makes working in teams much much easier
\item So long as everyone is on the same page teams can get as much done when in the same room as they can separately
\end{itemize}
\subsubsection{Changes to Make if Starting Over}
\begin{itemize}
\item Make better plans at the beginning for handling delays in the Rover side of the project
\item Work out a more empirical way of testing how much bandwidth is needed / can be handled by the radio links so we would have known what we had to work with
\end{itemize}

View File

@@ -0,0 +1,39 @@
\subsection{Ken Steinfeldt}
\subsubsection{Technical Knowledge Gained}
I gained a lot of technical knowledge and experience from the project.
First and foremost I learned how to use frameworks that I had never experienced before in ROS and PyQT5.
Not only had I never used Qt before, but I had never worked with any real UI framework before this project.
The ROS framework was also a unique challenge.
This project was an example of a very real use case for ROS, doing exactly what it was designed to do.
The framework was more complicated than I had anticipated, and required a significant learning curve.
Using both ROS and PyQt5 on a project such as this required using the frameworks according to best practice, and was fairly complicated at first, but simpler as time went on.
\subsubsection{Non-Technical Knowledge Gained}
I think that the most important non-technical knowledge I gained was the experience of working on a large, complicated project.
As the ground station was only a small portion of the of the Mars Rover project, it relied on many modules in many different locations.
As a group we were forced to manage these problems as they arose.
Even the organization of the repository was a challenge and had to be redone multiple times.
Development was also a problem as it could be a challenge to set up a functional development and testing environment.
This was alleviated with the development of a startup script that Corwin wrote.
\subsubsection{Knowledge Gained of Project Work}
Software engineering is notorious for delays and missed deadlines, and I am beginning to understand why.
At the beginning of the project many modules seemed to have a clear path to completion and simple solutions.
We thought that it would be easy for us to learn the two frameworks and then just start working with them, after all, that's what frameworks are for.
As it turns out, both ROS and Qt have not insignificant learning curves.
Learning how to use these frameworks took me much longer than I thought it would, and that hampered me throughout the process.
I also learned to trust my teammates, which is not something that someone expects to learn from a group project.
My teammates were great at always came through, even when I was not always able to.
By the end of the project I had learned that I could trust them completely.
\subsubsection{Knowledge Gained of Project Management}
The biggest thing that I learned about project management was how much easier it is to do when the project is well defined and planned at the beginning.
The work that we were required to do at the beginning of the year defined the scope of the project, the goals, the problems, and their planned solutions.
Having this already done due to the requirements of the class was hugely beneficial in implementing our solutions and a lesson in project management for the future.
\subsubsection{Knowledge Gained of Working in Teams}
Our group had great communication which was very beneficial to the team.
This open communication helped to make clear what was expected of each group member and helped our group to have fun and get along.
\subsubsection{Changes to Make if Starting Over}
\begin{itemize}
\item Take more time to understand the frameworks before trying to code with them.
\item Spend more time with the group personally.
\item Spend more time working with the rover team on rover stuff.
\item Take the time to create the testing environment before starting.
\end{itemize}

View File

@@ -0,0 +1,214 @@
\lstset{ %
backgroundcolor=\color{white}, % choose the background color
basicstyle=\footnotesize, % size of fonts used for the code
breaklines=true, % automatic line breaking only at whitespace
captionpos=b, % sets the caption-position to bottom
commentstyle=\color{gray}, % comment style
escapeinside={\%*}{*)}, % if you want to add LaTeX within your code
keywordstyle=\color{blue}, % keyword style
stringstyle=\color{purple}, % string literal style
}
\section{Appendix 1: Interesting Code Listings}
\subsection{Drive Test}
\subsubsection{Code}
\begin{lstlisting}[language=python]
class DriveTest(QtCore.QThread):
def __init__(self):
super(DriveTest, self).__init__()
self.not_abort = True
self.message = None
self.publisher = rospy.Publisher("/cmd_vel", Twist, queue_size=10)
rospy.init_node("test")
def run(self):
while self.not_abort:
self.message = Twist()
self.message.linear.x = 1.0
self.message.angular.z = 1.0
self.publisher.publish(self.message)
self.msleep(100)
\end{lstlisting}
\subsubsection{Description}
This QThread example class starts a ROS publishing node on the "/cmd\_vel" topic to send raw drive control commands to the Rover.
In this case, it is sending a command to drive the Rover forward and to the right, essentially causing it to drive in a never-ending circle.
\subsection{Video Test}
\subsubsection{Code}
\begin{lstlisting}[language=python]
class VideoTest(QtCore.QThread):
image_ready_signal = QtCore.pyqtSignal()
def __init__(self, screen_label, video_size=None, sub_path=None):
super(VideoTest, self).__init__()
self.not_abort = True
self.screen_label = screen_label
self.video_size = video_size
self.message = None
self.publisher = rospy.Subscriber(sub_path, CompressedImage, self.__receive_message)
self.raw_image = None
self.cv_image = None
self.pixmap = None
self.bridge = CvBridge()
self.image_ready_signal.connect(self.__on_image_update_ready)
def run(self):
while self.not_abort:
if self.raw_image:
self.cv_image = self.bridge.compressed_imgmsg_to_cv2(self.raw_image, "rgb8")
if self.video_size:
self.cv_image = cv2.resize(self.cv_image, self.video_size)
self.pixmap = QtGui.QPixmap.fromImage(qimage2ndarray.array2qimage(self.cv_image))
self.image_ready_signal.emit()
self.msleep(20)
def __on_image_update_ready(self):
self.screen_label.setPixmap(self.pixmap)
def __receive_message(self, message):
self.raw_image = message
\end{lstlisting}
\subsubsection{Description}
This example subscribes to the ROS topic that is passed in under the sub\_path argument in order to get video stream data.
An example of this topic might be "/cam1/usb\_cam1/image\_raw/compressed".
Inside of the body of the thread, it checks if there is image data, and if so decompresses it into a raw 8-bit image using ROS's OpenCV bridge libraries.
Finally, it converts the OpenCV image into a QImage and then into a QPixmap before broadcasting an update signal so the main GUI thread can show the image on the QLabel.
It is important to note that any direct GUI updates must happen in the main GUI thread, otherwise the QApplication will crash.
\subsection{Joystick ROS Drive Test}
\subsubsection{Code}
\begin{lstlisting}[language=python]
rospy.init_node("drive_tester")
self.pub = rospy.Publisher("/drive/motoroneandtwo", RoverMotorDrive, queue_size=1)
def __get_controller_data(self):
if (self.controller_aquired):
events = self.gamepad.read()
for event in events:
if event.code in self.raw_mapping_to_class_mapping:
self.controller_states[self.raw_mapping_to_class_mapping[event.code]] = event.state
# print "Logitech: %s" % self.controller_states
def __broadcast_if_ready(self):
drive = RoverMotorDrive()
axis = self.controller_states["left_stick_y_axis"]
drive.first_motor_direction = 1 if axis <= 512 else 0
drive.first_motor_speed = min(abs(self.controller_states["left_stick_y_axis"] - 512) * 128, 65535)
self.pub.publish(drive)
\end{lstlisting}
\subsubsection{Description}
These two methods and supporting lines above, taken from the testing class LogitechJoystick, contained in the file joystick\_drive\_test.py are the core of what is needed to get joystick data and broadcast it to the rover over a ROS topic.
These two methods are called on after another in a QThread. \_\_get\_controller\_data() reacts to motion events from the joystick and stores the current value of all axes and buttons in self.controller\_states. Then, in \_\_broadcast\_if\_ready(), and instantiation of the custom ROS message type, RoverMotorDrive, is made and values set to a scaled version of the raw values provided by the joystick. Finally, this data is published to the motor drive node and causes the ROS receiving node to see the data, send a message to the motor driver, and cause the motor to spin.
\subsection{Video Test}
\subsubsection{Code}
\begin{lstlisting}[language=python]
def toggle_video_display(self):
if self.video_enabled:
if self.video_subscriber:
self.video_subscriber.unregister()
self.new_frame = True
self.video_enabled = False
else:
new_topic = self.camera_topics[self.current_camera_settings["resolution"]]
self.video_subscriber = rospy.Subscriber(new_topic, CompressedImage, self.__image_data_received_callback)
self.video_enabled = True
\end{lstlisting}
\subsubsection{Description}
This very simple snippet is in the VideoReceiver class in VideoSystems. It is a demonstration of what is needed to properly disable the receiving of video data on a stream. Looking at the Subscriber line, you can see that there is an image callback associated with the subscription to a topic in ROS. This means that if you don't actually unsubscribe (or in this case, unregister) from a topic, as can be seen a few lines above, the data will continue being received even if you are not actively using it. Not doing this would cause unwanted bandwidth to be used.
\subsection{Ubiquiti Channel Change}
\subsubsection{Code}
\begin{lstlisting}[language=python]
GET_CURRENT_CHANNEL_COMMAND = "iwlist ath0 channel"
SET_CHANNEL_COMMAND = "iwconfig ath0 channel"
def setup_and_connect_ssh_client(self):
self.ssh_client = paramiko.SSHClient()
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh_client.connect(ACCESS_POINT_IP, username=ACCESS_POINT_USER, password=ACCESS_POINT_PASSWORD,
compress=True)
def apply_channel_if_needed(self):
if self.channel_change_needed:
self.show_channel__signal.emit(0)
self.set_gui_elements_enabled__signal.emit(False)
self.ssh_client.exec_command(SET_CHANNEL_COMMAND + " %02d" % self.new_channel)
self.get_and_show_current_channel()
self.channel_change_needed = False
def get_and_show_current_channel(self):
channel = 0
ssh_stdin, ssh_stdout, ssh_stderr = self.ssh_client.exec_command(GET_CURRENT_CHANNEL_COMMAND)
output = ssh_stdout.read()
for line in output.split("\n"):
if "Current Frequency:" in line:
channel = line.strip("()").split("Channel ")[1]
break
self.msleep(500) # From the gui, this helps show something is actually happening
self.show_channel__signal.emit(int(channel))
self.set_gui_elements_enabled__signal.emit(True)
\end{lstlisting}
\subsubsection{Description}
This code shows how we change the 2.4GHz radio channel on the Rocket M2 radios. In the first method, the class initializes an ssh connection to the access point radio. Then, in the thread's main loop, apply\_channel\_if\_needed runs on a regular basis waiting for a channel change to happen. Once it does, it executes an ssh command to change the channel via command line before running a second command via the third method to get the new channel from the radio, parse it, and show it in the GUI. This ensures that if the channel does not get set, the value shown in the GUI will alert the user to this fact.
\subsection{Compass Rotation}
\subsubsection{Code}
\begin{lstlisting}[language=python]
ROTATION_SPEED_MODIFIER = 2.5
def rotate_compass_if_needed(self):
heading_difference = abs(int(self.shown_heading) - self.current_heading)
should_update = False
if heading_difference > ROTATION_SPEED_MODIFIER:
self.shown_heading += self.rotation_direction * ROTATION_SPEED_MODIFIER
self.shown_heading %= 360
should_update = True
elif heading_difference != 0:
self.shown_heading = self.current_heading
should_update = True
if should_update:
self.current_heading_shown_rotation_angle = int(self.shown_heading)
if self.current_heading_shown_rotation_angle != self.last_current_heading_shown:
new_compass_image = self.main_compass_image.rotate(self.current_heading_shown_rotation_angle, resample=PIL.Image.BICUBIC)
self.last_current_heading_shown = self.current_heading_shown_rotation_angle
self.compass_pixmap = QtGui.QPixmap.fromImage(ImageQt(new_compass_image))
self.show_compass_image__signal.emit()
def update_heading_movement(self):
current_minus_shown = (self.current_heading - self.shown_heading) % 360
if current_minus_shown >= 180:
self.rotation_direction = -1
else:
self.rotation_direction = 1
\end{lstlisting}
\subsubsection{Description}
This code shows how movement updates to the compass are made. The second method gets called when a new heading change is made, setting whichever rotation direction is shorter to reach the desired goal. Then, in the main loop, the upper method calculates the difference between the current shown heading and the actual heading determining whether it should be moving or not. If it should, the main compass image that was loaded during init is rotated via a bicubic sampling algorithm to maintain image clarity, converted to a QPixmap, before an update signal is broadcast to the main thread to show the image.

View File

@@ -0,0 +1,29 @@
\section{Appendix 2}
\subsection{Ground Station Final Photos}
\begin{figure}[h!]
\centering
\captionsetup{justification=centering}
\includegraphics[width=0.9\textwidth]{misc_media/ground_station_framed}
\caption{Finished Ground Station Hardware}
\end{figure}
\begin{figure}[h!]
\centering
\captionsetup{justification=centering}
\includegraphics[width=0.9\textwidth]{misc_media/ground_station_screenshot}
\caption{Finished Ground Station Screenshot}
\end{figure}
\begin{figure}[h!]
\centering
\captionsetup{justification=centering}
\includegraphics[width=0.9\textwidth]{misc_media/ground_station_expo}
\caption{Finished Ground Station at Expo}
\end{figure}
\begin{figure}[h!]
\centering
\captionsetup{justification=centering}
\includegraphics[width=0.9\textwidth]{misc_media/ground_station_expo_2}
\caption{Chatting with TA at Expo}
\end{figure}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

View File

@@ -0,0 +1,165 @@
\documentclass[onecolumn, draftclsnofoot, 10pt, compsoc]{IEEEtran}
\usepackage{graphicx}
\graphicspath{{./figures/}}
\usepackage{url}
\usepackage{setspace}
\usepackage{multicol}
\usepackage{pdflscape}
\usepackage{pdfpages}
\usepackage[british]{babel}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{listings}
\usepackage{hyperref}
\usepackage{subfig}
\usepackage{pdfpages}
\usepackage{longtable}
\usepackage{geometry}
\geometry{textheight=9.5in, textwidth=7in}
\hypersetup{
colorlinks=true,
linkcolor=blue,
filecolor=magenta,
urlcolor=cyan,
}
% \overfullrule=2in
% 1. Fill in these details
\def \CapstoneTeamName{ Ground Station Software Team}
\def \CapstoneTeamNumber{ 30}
\def \GroupMemberOne{ Kenneth Steinfeldt}
\def \GroupMemberTwo{ Christopher Pham}
\def \GroupMemberThree{ Corwin Perren}
\def \CapstoneProjectName{ OSU Robotics Club\\Mars Rover Ground Station}
\def \CapstoneSponsorCompany{ OSU Robotics Club}
\def \CapstoneSponsorPerson{ Nick McComb}
%Personal \newcommands
\newcommand{\functRequ}[4]{
\item #1%
\par
\begin{itemize}
\item \textit{Description:} #2.%
\item \textit{Rationale:} #3.%
\item \textit{Dependencies:} #4%
\end{itemize}
}
\definecolor{backcolor}{rgb}{0.95,0.95,0.92}
\lstset{basicstyle=\ttfamily,
backgroundcolor=\color{backcolor},
showstringspaces=false,
commentstyle=\color{red},
keywordstyle=\color{blue},
columns=fullflexible,
breaklines=true,
postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space},
}
% 2. Uncomment the appropriate line below so that the document type works
\def \DocType{ %Problem Statement
%Requirements Document
%Technology Review
%Design Document
Final Report
}
\newcommand{\NameSigPair}[1]{
\par
\makebox[2.75in][r]{#1}
\hfill
\makebox[3.25in]{
\makebox[2.25in]{\hrulefill}
\hfill
\makebox[.75in]{\hrulefill}
}
\par\vspace{-12pt}
\textit{
\tiny\noindent
\makebox[2.75in]{}
\hfill
\makebox[3.25in]{
\makebox[2.25in][r]{Signature}
\hfill
\makebox[.75in][r]{Date}
}
}
}
% 3. If the document is not to be signed, uncomment the command below
\renewcommand{\NameSigPair}[1]{#1}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\begin{titlepage}
\pagenumbering{gobble}
\begin{singlespace}
% 4. If you have a logo, use this includegraphics command to put it on the coversheet.
\begin{minipage}{7in}
\centering
\hspace*{-.7in}
$\vcenter{\hbox{\includegraphics[height=4cm]{Oregon_State_College_of_Engineering_Logo}}}$
\hspace*{.2in}
$\vcenter{\hbox{\includegraphics[height=2.5cm]{OSURCLogoOrange}}}$
\end{minipage}
\par\vspace{.35in}
\centering
\scshape{
\huge CS Capstone \DocType \par
{\large\today}\par
\vspace{.5in}
\textbf{\Huge\CapstoneProjectName}\par
\vfill
{\large Prepared for}\par
\Huge \CapstoneSponsorCompany\par
\vspace{5pt}
{\Large\NameSigPair{\CapstoneSponsorPerson}\par}
{\large Prepared by }\par
Group\CapstoneTeamNumber\par
% 5. comment out the line below this one if you do not wish to name your team
\CapstoneTeamName\par
\vspace{5pt}
{\Large
\NameSigPair{\GroupMemberOne}\par
\NameSigPair{\GroupMemberTwo}\par
\NameSigPair{\GroupMemberThree}\par
}
\vspace{20pt}
\begin{abstract}
% 6. Fill in your abstract
This document contains all of the useful documentation about the OSURC Mars Rover ground station project as it has progressed throughout the year. This includes all technical documents (with revisions), weekly blog posts, code documentation, and lessons learned.
\end{abstract}
}
\end{singlespace}
\end{titlepage}
\newpage
\pagenumbering{arabic}
\tableofcontents
\clearpage
% Write stuff here....
\input{01-introduction/introduction}
\input{02-requirementsdoc/requirementsdoc}
\input{03-designdoc/designdoc}
\input{04-techreviewdocs/techreviewdocs}
\input{05-blogposts/blogposts}
\input{06-poster/poster}
\input{07-documentation/documentation}
\input{08-technicalresources/technicalresources}
\input{09-conclusions/conclusions}
\input{10-codelistings/codelistings}
\input{11-miscellaneous/miscellaneous}
\end{document}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
# This config is copied from overleaf so output there will match compiled output here
# support for the glossaries package:
add_cus_dep('glo', 'gls', 0, 'makeglossaries');
add_cus_dep('acn', 'acr', 0, 'makeglossaries');
sub makeglossaries {
system("makeglossaries \"$_[0]\"");
}
# support for the nomencl package:
add_cus_dep('nlo', 'nls', 0, 'makenlo2nls');
sub makenlo2nls {
system("makeindex -s nomencl.ist -o \"$_[0].nls\" \"$_[0].nlo\"");
}
# from the documentation for V. 2.03 of asymptote:
sub asy {return system("asy \"$_[0]\"");}
add_cus_dep("asy","eps",0,"asy");
add_cus_dep("asy","pdf",0,"asy");
add_cus_dep("asy","tex",0,"asy");
# metapost rule from http://tex.stackexchange.com/questions/37134
add_cus_dep('mp', '1', 0, 'mpost');
sub mpost {
my $file = $_[0];
my ($name, $path) = fileparse($file);
pushd($path);
my $return = system "mpost $name";
popd();
return $return;
}

View File

@@ -0,0 +1,18 @@
# Makefile created by Corwin Perren
# Generic makefile for all LaTeX projects downloaded from overleaf
#
# All this makefile does is call perl against latexmkrc, which is
# the latex equivalent of make
LATEXMK_COMPILE_FLAGS = -pdf
LATEXMK_CLEAN_FLAGS = -c
.DEFAULT_GOAL := all
all: latexmk_output clean
latexmk_output:
perl latexmk.pl $(LATEXMK_COMPILE_FLAGS)
mv final_report.pdf cs_group30_capstone_final_report.pdf
clean:
perl latexmk.pl $(LATEXMK_CLEAN_FLAGS)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

@@ -7,8 +7,9 @@ The project contains the following nodes:
- [IRIS](#iris-node)
- [Motor](#motor-node)
- [Tower](#tower-node)
- [Tower Alt](#tower-alt)
- Pan-Tilt
- Grasping
- [Grasping](#grasp-node)
- Science
- Arm Breakout 1 / 2
@@ -35,3 +36,27 @@ This node controls the motors that turn Rover's wheels.
This node is placed outside of the main electronics box on the chassis and collects data from various sensors.
[Find more documentation here.](tower.md)
### Tower alt
<img src="files/tower_alt_render.png" width="300">
This board acts as an alternate for Rover's Tower Node when both of the boards we had didn't work. It really just breaks out a Teensy 3.2 to a few different connectors.
[Find more documentation here.](tower-alt.md)
### Grasp Node
<img src="files/grasp.JPG" width="300">
Controls 4 DC motors with quadrature encoder and current feedback. Has four analog inputs intended to be used with force resistors.
[Find more documentation here.](grasp.md)
### Science Node
<img src="files/Science.PNG" width="300">
Powers, and comunicates with the Soil Probe via RS-485. Uses simple logic to power a drill motor (24V,5A, full forward, full back, stop). Outputs 5V to power a digital camera. Sends a signal to the didgital camera to control shoot and zoom.
[Find more documentation here.](science.md)

View File

@@ -0,0 +1,40 @@
Science Node
============
![Science](files/Science.PNG)
Designed by Anthony Grana.
Summary
-------
Made for the science end affector for the 2018 rover. Powers, and
comunicates with the [Soil
Probe](https://www.fondriest.com/pdf/stevens_hydra_manual.pdf) via
RS-485. Uses simple logic to power a drill motor (24V,5A, full forward,
full back, stop). Outputs 5V to power a [digital
camera.](https://www.amazon.com/Canon-PowerShot-Stabilized-2-7-Inch-Black/dp/B0035FZJJ4)
Sends a signal to the didgital camera to control shoot and zoom.
### Bill of Materials
[Bill of
Materials V1.1](https://docs.google.com/spreadsheets/d/1dY48bTzPCWO-qP4mQwElCYWBNv3Bg6SGOoxIt3NPqik/edit?usp=sharing)
#### Design Files
[Science node on circuit
maker](https://workspace.circuitmaker.com/Projects/Details/Anthony-Grana/Sience-Node)
[V1.1](files/SienceNode.Zip)
### Known Issues
V1.1
1. The Drill motor controller will pull 5A but the curent power connector for the board can only handle 3A.
2. For both RS 485 chips, the RX and TX pins need to be reversed.
3. The RS 485 for the soil sensor is connected to the CAN RX TX instead of the Serial RX TX.
V1.2
No Known Issues

View File

@@ -0,0 +1,30 @@
# Tower Alt
![Tower Alt Render](files/tower_alt_render.png)
Designed by [Nick McComb](www.nickmccomb.net) for OSURC Mars Rover.
## Summary
Made for MR1718 as part of the OSU Robotics Club.
This board acts as an alternate for Rover's [Tower Node](tower.md) when both of the boards we had didn't work. It really just breaks out a Teensy 3.2 to a few different connectors.
### Bill of Materials
[Bill of Materials V1](https://docs.google.com/spreadsheets/d/19i468ReDNzdnqVHqhV62DnraayI0Lsa_KynYnzvA6o8/edit?usp=sharing)
#### Design files
[Tower Alt on CircuitMaker](https://workspace.circuitmaker.com/Projects/Details/Nick-McComb/MR1718-Tower-Alt)
### Downloads
[Schematic V1](files/tower-alt-v1-schematic.pdf)
[3D Model V1 (STEP)](files/tower-alt.step)
### Known Issues
#### Version 1

View File

@@ -1,4 +1,4 @@
# Motor Node
# Tower Node
<!-- ![Iris](files/iris.jpg) -->

View File

@@ -1,27 +1,30 @@
# Examples
#########################
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="FTU9EU0I", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyDEV0"
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="FTU9EU0I", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyDEV1"
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="FTU9EU0I", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyCompass"
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="FTU9EU0I", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyGPS"
#########################
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2NFZVA", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyIRIS_0_0"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2NFZVA", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyIRIS_0_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2NFZVA", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyIRIS_0_2"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2NFZVA", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyIRIS_0_3"
# IRIS Board Mappings
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2CZLZF", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyREAR"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2CZLZF", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyLEFT"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2CZLZF", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyRIGHT"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2CZLZF", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyIRIS_0_3"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2RJ8G5", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyIRIS_1_0"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2RJ8G5", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyIRIS_1_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2RJ8G5", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyIRIS_1_2"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2RJ8G5", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyIRIS_1_3"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2HPA6V", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyIRIS_1_0"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2HPA6V", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyTowerAndPanTilt"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2HPA6V", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyOdometry"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2HPA6V", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyChassisPanTilt"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2UFVE1", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyIRIS_2_0"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2UFVE1", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyIRIS_2_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2UFVE1", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyIRIS_2_2"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM2UFVE1", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyIRIS_2_3"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM34WE0R", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="rover/ttyIRIS_2_0"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM34WE0R", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="rover/ttyIRIS_2_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM34WE0R", ENV{ID_USB_INTERFACE_NUM}=="02", SYMLINK+="rover/ttyIRIS"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", ATTRS{serial}=="NM34WE0R", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="rover/ttyExtraUART"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="AH05K2Y8", SYMLINK+="rover/ttyLEFT"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A906H89E", SYMLINK+="rover/ttyRIGHT"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A906H7P0", SYMLINK+="rover/ttyREAR"
##### Mappings from OMSI / EXPO driving with individual adapters
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="AH05K2Y8", SYMLINK+="rover/ttyLEFT"
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A906H89E", SYMLINK+="rover/ttyRIGHT"
# SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A906H7P0", SYMLINK+="rover/ttyREAR"

View File

@@ -1,9 +1,154 @@
////////// Includes //////////
#include <ModbusRtu.h>
#include <Servo.h>
////////// Hardware / Data Enumerations //////////
enum HARDWARE {
RS485_EN = 2,
RS485_RX = 7,
RS485_TX = 8,
SERVO_PAN = 5,
SERVO_TILT = 4,
LED_RED = 1,
LED_GREEN = 32,
LED_BLUE = 6,
LED_BLUE_EXTRA = 13
};
enum MODBUS_REGISTERS {
CENTER_ALL = 0, // Input/Output
PAN_ADJUST_POSITIVE = 1, // Input/Output
PAN_ADJUST_NEGATIVE = 2, // Input/Output
TILT_ADJUST_POSITIVE = 3, // Input/Output
TILT_ADJUST_NEGATIVE = 4, // Input/Output
};
////////// Global Variables //////////
const uint8_t node_id = 1;
const uint8_t mobus_serial_port_number = 3;
uint16_t modbus_data[] = {0, 0, 0, 0, 0};
uint8_t num_modbus_registers = 0;
int8_t poll_state = 0;
bool communication_good = false;
uint8_t message_count = 0;
// Pan/tilt hard limits
const int pan_min = 530;
const int pan_center = 1590;
const int pan_max = 2460;
const int tilt_min = 620;
const int tilt_center = 1670;
const int tilt_max = 2380;
// Pan/tilt positions
int pan_position = pan_center;
int tilt_position = tilt_center;
////////// Class Instantiations //////////
Modbus slave(node_id, mobus_serial_port_number, HARDWARE::RS485_EN);
Servo pan_servo;
Servo tilt_servo;
void setup() {
// put your setup code here, to run once:
// Serial.begin(9600);
// while(!Serial);
setup_hardware();
num_modbus_registers = sizeof(modbus_data) / sizeof(modbus_data[0]);
slave.begin(115200);
slave.setTimeOut(150);
}
void loop() {
// put your main code here, to run repeatedly:
poll_modbus();
set_leds();
set_pan_tilt_adjustments();
}
void setup_hardware() {
// Setup pins as inputs / outputs
pinMode(HARDWARE::RS485_EN, OUTPUT);
pinMode(HARDWARE::SERVO_PAN, OUTPUT);
pinMode(HARDWARE::SERVO_TILT, OUTPUT);
pan_servo.attach(HARDWARE::SERVO_PAN);
tilt_servo.attach(HARDWARE::SERVO_TILT);
pan_servo.writeMicroseconds(pan_center);
tilt_servo.writeMicroseconds(tilt_center);
pinMode(HARDWARE::LED_RED, OUTPUT);
pinMode(HARDWARE::LED_GREEN, OUTPUT);
pinMode(HARDWARE::LED_BLUE, OUTPUT);
pinMode(HARDWARE::LED_BLUE_EXTRA, OUTPUT);
// Set default pin states
digitalWrite(HARDWARE::LED_RED, LOW);
digitalWrite(HARDWARE::LED_GREEN, HIGH);
digitalWrite(HARDWARE::LED_BLUE, HIGH);
digitalWrite(HARDWARE::LED_BLUE_EXTRA, LOW);
}
void poll_modbus() {
poll_state = slave.poll(modbus_data, num_modbus_registers);
communication_good = !slave.getTimeOutState();
}
void set_leds() {
if (poll_state > 4) {
message_count++;
if (message_count > 2) {
digitalWrite(HARDWARE::LED_BLUE_EXTRA, !digitalRead(HARDWARE::LED_BLUE_EXTRA));
message_count = 0;
}
digitalWrite(HARDWARE::LED_GREEN, LOW);
digitalWrite(HARDWARE::LED_RED, HIGH);
} else if (!communication_good) {
digitalWrite(HARDWARE::LED_BLUE_EXTRA, LOW);
digitalWrite(HARDWARE::LED_GREEN, HIGH);
digitalWrite(HARDWARE::LED_RED, LOW);
}
}
void set_pan_tilt_adjustments() {
if (communication_good) {
if (modbus_data[MODBUS_REGISTERS::CENTER_ALL]) {
pan_servo.writeMicroseconds(constrain(pan_position, pan_min, pan_max));
tilt_servo.writeMicroseconds(constrain(tilt_position, tilt_min, tilt_max));
pan_position = pan_center;
tilt_position = tilt_center;
modbus_data[MODBUS_REGISTERS::CENTER_ALL] = 0;
}
pan_position = constrain(pan_position + modbus_data[MODBUS_REGISTERS::PAN_ADJUST_POSITIVE] - modbus_data[MODBUS_REGISTERS::PAN_ADJUST_NEGATIVE], pan_min, pan_max);
tilt_position = constrain(tilt_position + modbus_data[MODBUS_REGISTERS::TILT_ADJUST_POSITIVE] - modbus_data[MODBUS_REGISTERS::TILT_ADJUST_NEGATIVE], tilt_min, tilt_max);
pan_servo.writeMicroseconds(pan_position);
tilt_servo.writeMicroseconds(tilt_position);
// Serial.print(pan_position);
// Serial.print("\t");
// Serial.println(tilt_position);
modbus_data[MODBUS_REGISTERS::PAN_ADJUST_POSITIVE] = 0;
modbus_data[MODBUS_REGISTERS::PAN_ADJUST_NEGATIVE] = 0;
modbus_data[MODBUS_REGISTERS::TILT_ADJUST_POSITIVE] = 0;
modbus_data[MODBUS_REGISTERS::TILT_ADJUST_NEGATIVE] = 0;
}
}

View File

@@ -46,7 +46,7 @@ uint8_t message_count = 0;
uint8_t failSafe;
uint16_t lostFrames = 0;
uint16_t telem_24v_scalar = 37500;
uint16_t telem_24v_scalar = 36680;
////////// Class Instantiations //////////
SBUS x8r(SBUS_HARDWARE_PORT);

View File

@@ -34,7 +34,7 @@ enum MODBUS_REGISTERS {
};
////////// Global Variables //////////
const uint8_t node_id = 1;
const uint8_t node_id = 2;
const uint8_t mobus_serial_port_number = 3;
uint16_t modbus_data[] = {0, 0, 0, 0, 0, 0};

View File

@@ -1,8 +1,7 @@
#include <NMEAGPS.h>
#include <ublox/ubxGPS.h>
#include <NeoGPS_cfg.h>
////////// Includes //////////
#include <ModbusRtu.h>
#include <Adafruit_BNO055_t3.h>
#include <ArduinoJson.h>
/*
Imu/data (Imu)
@@ -26,60 +25,95 @@
temp (deg c)
*/
Adafruit_BNO055 bno = Adafruit_BNO055(WIRE_BUS, -1, BNO055_ADDRESS_A, I2C_MASTER, I2C_PINS_29_30, I2C_PULLUP_INT, I2C_RATE_100, I2C_OP_MODE_ISR);
////////// Hardware / Data Enumerations //////////
enum HARDWARE {
GPS_IMU_RS485_EN = 3,
GPS_IMU_RS485_RX = 9,
GPS_IMU_RS485_TX = 10,
COMMS_RS485_EN = 2,
COMMS_RS485_RX = 0,
COMMS_RS485_TX = 1,
#define gpsPort Serial2
#define commsPort Serial3
#define GPS_PORT_NAME "Serial2"
GPS_UART_RX = 7,
GPS_UART_TX = 8,
const unsigned char ubxRate5Hz[] = { 0x06, 0x08, 0x06, 0x00, 200, 0x00, 0x01, 0x00, 0x01, 0x00 };
const unsigned char ubxRate10Hz[] = { 0x06, 0x08, 0x06, 0x00, 100, 0x00, 0x01, 0x00, 0x01, 0x00 };
IMU_SDA = 18,
IMU_SCL = 19,
const char baud38400 [] = "PUBX,41,1,3,3,38400,0";
const char baud57600 [] = "PUBX,41,1,3,3,57600,0";
const char baud115200[] = "PUBX,41,1,3,3,115200,0";
WHITE_LED_CONTROL = 11,
C02_SENSOR = A7,
MISC_PIN = A8,
LED_BLUE_EXTRA = 13
};
enum MODBUS_REGISTERS {
LED_CONTROL = 0, // Input
CO2_READING_PPM = 1
};
enum LIGHT_STATES {
NO_CHANGE = 0,
LIGHT_OFF = 1,
LIGHT_FLASH = 2,
LIGHT_MED = 3,
LIGHT_HIGH = 4
};
#define GPS_SERIAL_PORT Serial3
#define GPS_IMU_STREAMING_PORT Serial2
////////// Global Variables //////////
///// Modbus
const uint8_t node_id = 1;
const uint8_t mobus_serial_port_number = 1;
uint16_t modbus_data[] = {0, 0};
uint8_t num_modbus_registers = 0;
int8_t poll_state = 0;
bool communication_good = false;
uint8_t message_count = 0;
///// IMU
imu::Vector<3> linear_accel;
imu::Vector<3> angular_vel;
imu::Quaternion quat;
char current_byte = '$';
String nmea_sentence = "";
String output_sentence;
char float_decimal_places = 8;
void sendUBX( const unsigned char *progmemBytes, size_t len )
{
gpsPort.write( 0xB5 ); // SYNC1
gpsPort.write( 0x62 ); // SYNC2
///// GPS
char current_byte = '$';
String nmea_sentence = "";
char gps_buffer[255];
unsigned char buffer_count = 0;
uint8_t a = 0, b = 0;
while (len-- > 0) {
uint8_t c = pgm_read_byte( progmemBytes++ );
a += c;
b += a;
gpsPort.write( c );
}
////////// Class Instantiations //////////
Modbus slave(node_id, mobus_serial_port_number, HARDWARE::COMMS_RS485_EN);
gpsPort.write( a ); // CHECKSUM A
gpsPort.write( b ); // CHECKSUM B
Adafruit_BNO055 bno = Adafruit_BNO055(WIRE_BUS, -1, BNO055_ADDRESS_A, I2C_MASTER, I2C_PINS_18_19, I2C_PULLUP_INT, I2C_RATE_100, I2C_OP_MODE_IMM);
}
const char baud115200[] = "PUBX,41,1,3,3,115200,0";
void setup() {
// Debugging
Serial.begin(9600);
while (!Serial);
Serial.println("Booting up");
// put your setup code here, to run once:
commsPort.begin(115200);
commsPort.transmitterEnable(3);
// while (!Serial);
delay(250);
gpsPort.begin(9600);
// sendUBX( ubxRate5Hz, sizeof(ubxRate5Hz) );
// Raw pin setup
setup_hardware();
// Setup modbus serial communication
num_modbus_registers = sizeof(modbus_data) / sizeof(modbus_data[0]);
slave.begin(115200); // baud-rate at 19200
slave.setTimeOut(150);
// GPS & IMU serial streaming setup
GPS_IMU_STREAMING_PORT.begin(115200);
GPS_IMU_STREAMING_PORT.transmitterEnable(HARDWARE::GPS_IMU_RS485_EN);
// IMU Setup
Serial.println("Setting up IMU");
if (!bno.begin()) {
/* There was a problem detecting the BNO055 ... check your connections */
@@ -90,75 +124,166 @@ void setup() {
bno.setExtCrystalUse(true);
Serial.println("IMU Configured.");
// GPS Setup
GPS_SERIAL_PORT.begin(9600);
}
void loop() {
// Reset JSON for next loop
StaticJsonBuffer<1000> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
if (gpsPort.available() > 0 ) {
nmea_sentence = "";
do {
if (gpsPort.available() > 0) {
current_byte = gpsPort.read();
// Do normal polling
poll_modbus();
set_leds();
send_imu_stream_line(root);
process_gps_and_send_if_ready(root);
process_white_led_command();
get_co2_data();
if (current_byte != '\r' and current_byte != '\n') {
nmea_sentence += current_byte;
}
}
} while (current_byte != '\r');
commsPort.println(nmea_sentence);
}
// Print JSON and newline
root.printTo(GPS_IMU_STREAMING_PORT);
GPS_IMU_STREAMING_PORT.println();
}
void setup_hardware() {
// Setup pins as inputs / outputs
pinMode(HARDWARE::WHITE_LED_CONTROL, OUTPUT);
pinMode(HARDWARE::C02_SENSOR, INPUT);
pinMode(HARDWARE::MISC_PIN, OUTPUT);
pinMode(HARDWARE::LED_BLUE_EXTRA, OUTPUT);
// Set default pin states
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
digitalWrite(HARDWARE::LED_BLUE_EXTRA, LOW);
// Set teensy to increased analog resolution
analogReadResolution(13);
}
void send_imu_stream_line(JsonObject &root) {
JsonObject& imu_object = root.createNestedObject("imu");
quat = bno.getQuat();
linear_accel = bno.getVector(Adafruit_BNO055::VECTOR_LINEARACCEL);
angular_vel = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE);
commsPort.print("ox:");
commsPort.print(quat.x(), float_decimal_places);
commsPort.print(",");
commsPort.print("oy:");
commsPort.print(quat.y(), float_decimal_places);
commsPort.print(",");
commsPort.print("oz:");
commsPort.print(quat.z(), float_decimal_places);
commsPort.print(",");
commsPort.print("ow:");
commsPort.print(quat.w(), float_decimal_places);
imu_object["ox"] = quat.x();
imu_object["oy"] = quat.y();
imu_object["oz"] = quat.z();
imu_object["ow"] = quat.w();
commsPort.print(",");
commsPort.print("lax:");
commsPort.print(linear_accel.x(), float_decimal_places);
commsPort.print(",");
commsPort.print("lay:");
commsPort.print(linear_accel.y(), float_decimal_places);
commsPort.print(",");
commsPort.print("laz:");
commsPort.print(linear_accel.z(), float_decimal_places);
imu_object["lax"] = linear_accel.x();
imu_object["lay"] = linear_accel.y();
imu_object["laz"] = linear_accel.z();
commsPort.print(",");
commsPort.print("avx:");
commsPort.print(angular_vel.x(), float_decimal_places);
commsPort.print(",");
commsPort.print("avy:");
commsPort.print(angular_vel.y(), float_decimal_places);
commsPort.print(",");
commsPort.print("avz:");
commsPort.print(angular_vel.z(), float_decimal_places);
commsPort.println();
imu_object["avx"] = angular_vel.x();
imu_object["avy"] = angular_vel.y();
imu_object["avz"] = angular_vel.z();
//
// /* Display calibration status for each sensor. */
// uint8_t system, gyro, accel, mag = 0;
// bno.getCalibration(&system, &gyro, &accel, &mag);
// commsPort.print("CALIBRATION: Sys=");
// commsPort.print(system, DEC);
// commsPort.print(" Gyro=");
// commsPort.print(gyro, DEC);
// commsPort.print(" Accel=");
// commsPort.print(accel, DEC);
// commsPort.print(" Mag=");
// commsPort.print(mag, DEC);
// Serial.print("CALIBRATION: Sys=");
// Serial.print(system, DEC);
// Serial.print(" Gyro=");
// Serial.print(gyro, DEC);
// Serial.print(" Accel=");
// Serial.print(accel, DEC);
// Serial.print(" Mag=");
// Serial.print(mag, DEC);
}
void process_gps_and_send_if_ready(JsonObject &root) {
root["gps"] = "";
char num_in_bytes = GPS_SERIAL_PORT.available();
if (num_in_bytes > 0) {
for (char i = 0 ; i < num_in_bytes ; i++) {
char in_byte = GPS_SERIAL_PORT.read();
if (in_byte != '\n' && in_byte != '\r') {
gps_buffer[buffer_count] = in_byte;
buffer_count++;
}
if (in_byte == '\r') {
gps_buffer[buffer_count] = '\0';
root["gps"] = gps_buffer;
buffer_count = 0;
}
}
}
}
void process_white_led_command() {
uint16_t light_command = modbus_data[MODBUS_REGISTERS::LED_CONTROL];
if (light_command == LIGHT_STATES::LIGHT_MED) {
digitalWrite(HARDWARE::WHITE_LED_CONTROL, HIGH);
delay(100); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
delay(100); // wait for a second
} else if (light_command == LIGHT_STATES::LIGHT_FLASH) {
digitalWrite(HARDWARE::WHITE_LED_CONTROL, HIGH);
delay(100); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
delay(100); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, HIGH);
delay(100); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
delay(100);
} else if (light_command == LIGHT_STATES::LIGHT_HIGH) {
digitalWrite(HARDWARE::WHITE_LED_CONTROL, HIGH);
delay(750); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
delay(100);
} else if (light_command == LIGHT_STATES::LIGHT_OFF) {
digitalWrite(HARDWARE::WHITE_LED_CONTROL, HIGH);
delay(2000); // wait for a second
digitalWrite(HARDWARE::WHITE_LED_CONTROL, LOW);
delay(2000);
}
modbus_data[MODBUS_REGISTERS::LED_CONTROL] = LIGHT_STATES::NO_CHANGE;
}
void get_co2_data() {
int voltage = ((analogRead(HARDWARE::C02_SENSOR) / 8192.0) * 3300);
if (voltage < 400 || voltage > 2000) {
modbus_data[MODBUS_REGISTERS::CO2_READING_PPM] = 9999;
} else {
modbus_data[MODBUS_REGISTERS::CO2_READING_PPM] = map(voltage, 400, 2000, 0, 5000);
}
// Serial.println(modbus_data[MODBUS_REGISTERS::CO2_READING_PPM]);
}
void poll_modbus() {
poll_state = slave.poll(modbus_data, num_modbus_registers);
communication_good = !slave.getTimeOutState();
}
void set_leds() {
if (poll_state > 4) {
message_count++;
if (message_count > 2) {
digitalWrite(HARDWARE::LED_BLUE_EXTRA, !digitalRead(HARDWARE::LED_BLUE_EXTRA));
message_count = 0;
}
} else if (!communication_good) {
digitalWrite(HARDWARE::LED_BLUE_EXTRA, LOW);
}
}

View File

@@ -27,7 +27,7 @@ enum MODBUS_REGISTERS {
};
////////// Global Variables //////////
const uint8_t node_id = 1;
const uint8_t node_id = 2;
const uint8_t mobus_serial_port_number = 3;
uint16_t modbus_data[] = {0, 0, 0, 0, 0};
@@ -38,12 +38,12 @@ bool communication_good = false;
uint8_t message_count = 0;
// Pan/tilt hard limits
const int pan_min = 1415;
const int pan_center = 1538;
const int pan_max = 1665;
const int pan_min = 1470;
const int pan_center = 1600;
const int pan_max = 1725;
const int tilt_min = 0;
const int tilt_center = 1900;
const int tilt_min = 1020;
const int tilt_center = 1820;
const int tilt_max = 2400;
// Pan/tilt positions
@@ -57,6 +57,8 @@ Servo pan_servo;
Servo tilt_servo;
void setup() {
// Serial.begin(9600);
// while(!Serial);
setup_hardware();
num_modbus_registers = sizeof(modbus_data) / sizeof(modbus_data[0]);
@@ -82,8 +84,8 @@ void setup_hardware() {
pan_servo.attach(HARDWARE::SERVO_PAN);
tilt_servo.attach(HARDWARE::SERVO_TILT);
pan_servo.write(pan_center);
tilt_servo.write(tilt_center);
pan_servo.writeMicroseconds(pan_center);
tilt_servo.writeMicroseconds(tilt_center);
pinMode(HARDWARE::LED_RED, OUTPUT);
pinMode(HARDWARE::LED_GREEN, OUTPUT);
@@ -124,8 +126,8 @@ void set_leds() {
void set_pan_tilt_adjustments() {
if (communication_good) {
if (modbus_data[MODBUS_REGISTERS::CENTER_ALL]) {
pan_servo.write(constrain(pan_position, pan_min, pan_max));
tilt_servo.write(constrain(tilt_position, tilt_min, tilt_max));
pan_servo.writeMicroseconds(constrain(pan_position, pan_min, pan_max));
tilt_servo.writeMicroseconds(constrain(tilt_position, tilt_min, tilt_max));
pan_position = pan_center;
tilt_position = tilt_center;
@@ -133,11 +135,15 @@ void set_pan_tilt_adjustments() {
modbus_data[MODBUS_REGISTERS::CENTER_ALL] = 0;
}
pan_position = pan_position - modbus_data[MODBUS_REGISTERS::PAN_ADJUST_POSITIVE] + modbus_data[MODBUS_REGISTERS::PAN_ADJUST_NEGATIVE];
tilt_position = tilt_position + modbus_data[MODBUS_REGISTERS::TILT_ADJUST_POSITIVE] - modbus_data[MODBUS_REGISTERS::TILT_ADJUST_NEGATIVE];
pan_position = constrain(pan_position - modbus_data[MODBUS_REGISTERS::PAN_ADJUST_POSITIVE] + modbus_data[MODBUS_REGISTERS::PAN_ADJUST_NEGATIVE], pan_min, pan_max);
tilt_position = constrain(tilt_position + modbus_data[MODBUS_REGISTERS::TILT_ADJUST_POSITIVE] - modbus_data[MODBUS_REGISTERS::TILT_ADJUST_NEGATIVE], tilt_min, tilt_max);
pan_servo.write(constrain(pan_position, pan_min, pan_max));
tilt_servo.write(constrain(tilt_position, tilt_min, tilt_max));
pan_servo.writeMicroseconds(pan_position);
tilt_servo.writeMicroseconds(tilt_position);
// Serial.print(pan_position);
// Serial.print("\t");
// Serial.println(tilt_position);
modbus_data[MODBUS_REGISTERS::PAN_ADJUST_POSITIVE] = 0;
modbus_data[MODBUS_REGISTERS::PAN_ADJUST_NEGATIVE] = 0;

View File

@@ -16,9 +16,10 @@ from rover_control.msg import DriveCommandMessage, TowerPanTiltControlMessage
GAME_CONTROLLER_NAME = "Logitech Logitech Extreme 3D Pro"
DEFAULT_DRIVE_COMMAND_TOPIC = "/rover_control/command_control/ground_station_drive"
DEFAULT_PAN_TILT_COMMAND_TOPIC = "/tower/control"
DEFAULT_TOWER_PAN_TILT_COMMAND_TOPIC = "/rover_control/tower/pan_tilt/control"
DEFAULT_CHASSIS_PAN_TILT_COMMAND_TOPIC = "/rover_control/chassis/pan_tilt/control"
DRIVE_COMMAND_HERTZ = 15
DRIVE_COMMAND_HERTZ = 20
Y_AXIS_DEADBAND = 0.05
X_AXIS_DEADBAND = 0.05
@@ -31,8 +32,12 @@ CAMERA_CHANGE_TIME = 0.2
GUI_ELEMENT_CHANGE_TIME = 0.2
CAMERA_TOGGLE_CHANGE_TIME = 0.35
PAN_TILT_X_AXIS_SCALAR = 5
PAN_TILT_Y_AXIS_SCALAR = 10
TOWER_PAN_TILT_X_AXIS_SCALAR = 2
TOWER_PAN_TILT_Y_AXIS_SCALAR = 15
CHASSIS_PAN_TILT_X_AXIS_SCALAR = 15
CHASSIS_PAN_TILT_Y_AXIS_SCALAR = 15
#####################################
# Controller Class Definition
@@ -66,12 +71,12 @@ class LogitechJoystick(QtCore.QThread):
"five_pressed": 0,
"six_pressed": 0,
"seven_pressed": 0,
"eight_pressed": 0,
"nine_pressed": 0,
"ten_pressed": 0,
"eleven_pressed": 0,
"twelve_pressed": 0,
"thirteen_pressed": 0,
"fourteen_pressed": 0,
"fifteen_pressed": 0,
"sixteen_pressed": 0,
}
self.raw_mapping_to_class_mapping = {
@@ -90,12 +95,12 @@ class LogitechJoystick(QtCore.QThread):
"BTN_TOP2": "five_pressed",
"BTN_PINKIE": "six_pressed",
"BTN_BASE": "seven_pressed",
"BTN_BASE2": "eight_pressed",
"BTN_BASE3": "nine_pressed",
"BTN_BASE4": "ten_pressed",
"BTN_BASE5": "eleven_pressed",
"BTN_BASE6": "twelve_pressed",
"BTN_BASE3": "thirteen_pressed",
"BTN_BASE4": "fourteen_pressed",
"BTN_BASE": "fifteen_pressed",
"BTN_BASE2": "sixteen_pressed",
"BTN_BASE6": "twelve_pressed"
}
self.ready = False
@@ -124,6 +129,7 @@ class LogitechJoystick(QtCore.QThread):
events = self.gamepad.read()
for event in events:
# print event.code
if event.code in self.raw_mapping_to_class_mapping:
self.controller_states[self.raw_mapping_to_class_mapping[event.code]] = event.state
@@ -147,6 +153,7 @@ class JoystickControlSender(QtCore.QThread):
# ########## Reference to class init variables ##########
self.shared_objects = shared_objects
self.video_coordinator = self.shared_objects["threaded_classes"]["Video Coordinator"]
self.right_screen = self.shared_objects["screens"]["right_screen"]
self.speed_limit_progress_bar = self.right_screen.speed_limit_progress_bar # type: QtWidgets.QProgressBar
self.left_drive_progress_bar = self.right_screen.left_drive_progress_bar # type: QtWidgets.QProgressBar
@@ -166,7 +173,15 @@ class JoystickControlSender(QtCore.QThread):
# ########## Class Variables ##########
# Publishers
self.drive_command_publisher = rospy.Publisher(DEFAULT_DRIVE_COMMAND_TOPIC, DriveCommandMessage, queue_size=1)
self.tower_pan_tilt_command_publisher = rospy.Publisher(DEFAULT_PAN_TILT_COMMAND_TOPIC, TowerPanTiltControlMessage, queue_size=1)
self.tower_pan_tilt_command_publisher = rospy.Publisher(DEFAULT_TOWER_PAN_TILT_COMMAND_TOPIC,
TowerPanTiltControlMessage, queue_size=1)
self.chassis_pan_tilt_command_publisher = rospy.Publisher(DEFAULT_CHASSIS_PAN_TILT_COMMAND_TOPIC,
TowerPanTiltControlMessage, queue_size=1)
self.current_pan_tilt_selection = "no_pan_tilt"
self.last_hat_x_was_movement = False
self.last_hat_y_was_movement = False
self.wait_time = 1.0 / DRIVE_COMMAND_HERTZ
@@ -179,7 +194,6 @@ class JoystickControlSender(QtCore.QThread):
def run(self):
while self.run_thread_flag:
start_time = time()
self.check_and_set_pause_state()
@@ -194,6 +208,8 @@ class JoystickControlSender(QtCore.QThread):
self.set_left_drive_output__signal.connect(self.left_drive_progress_bar.setValue)
self.set_right_drive_output__signal.connect(self.right_drive_progress_bar.setValue)
self.video_coordinator.pan_tilt_selection_changed__signal.connect(self.on_pan_tilt_selection_changed__slot)
def check_and_set_pause_state(self):
thumb_pressed = self.joystick.controller_states["thumb_pressed"]
if thumb_pressed and (time() - self.last_pause_state_time) > PAUSE_STATE_CHANGE_TIME:
@@ -245,14 +261,31 @@ class JoystickControlSender(QtCore.QThread):
self.last_camera_toggle_time = time()
def publish_pan_tilt_control_commands(self):
button_eight = self.joystick.controller_states["seven_pressed"]
hat_x = self.joystick.controller_states["hat_x_axis"]
hat_y = self.joystick.controller_states["hat_y_axis"]
pan_tilt_message = TowerPanTiltControlMessage()
pan_tilt_message.relative_pan_adjustment = hat_x * PAN_TILT_X_AXIS_SCALAR
pan_tilt_message.relative_tilt_adjustment = -(hat_y * PAN_TILT_Y_AXIS_SCALAR)
if (hat_x == 0 and not self.last_hat_x_was_movement) and (
hat_y == 0 and not self.last_hat_y_was_movement) and not button_eight:
return
self.tower_pan_tilt_command_publisher.publish(pan_tilt_message)
self.last_hat_x_was_movement = True if hat_x != 0 else False
self.last_hat_y_was_movement = True if hat_y != 0 else False
pan_tilt_message = TowerPanTiltControlMessage()
if button_eight:
pan_tilt_message.should_center = 1
if self.current_pan_tilt_selection == "tower_pan_tilt":
pan_tilt_message.relative_pan_adjustment = hat_x * TOWER_PAN_TILT_X_AXIS_SCALAR
pan_tilt_message.relative_tilt_adjustment = -(hat_y * TOWER_PAN_TILT_Y_AXIS_SCALAR)
self.tower_pan_tilt_command_publisher.publish(pan_tilt_message)
elif self.current_pan_tilt_selection == "chassis_pan_tilt":
pan_tilt_message.relative_pan_adjustment = hat_x * CHASSIS_PAN_TILT_X_AXIS_SCALAR
pan_tilt_message.relative_tilt_adjustment = -(hat_y * CHASSIS_PAN_TILT_Y_AXIS_SCALAR)
self.chassis_pan_tilt_command_publisher.publish(pan_tilt_message)
def get_drive_message(self, throttle_axis):
drive_message = DriveCommandMessage()
@@ -274,6 +307,9 @@ class JoystickControlSender(QtCore.QThread):
return drive_message
def on_pan_tilt_selection_changed__slot(self, selection):
self.current_pan_tilt_selection = selection
def setup_signals(self, start_signal, signals_and_slots_signal, kill_signal):
start_signal.connect(self.start)
signals_and_slots_signal.connect(self.connect_signals_and_slots)

View File

@@ -110,7 +110,7 @@ class SpaceNavControlSender(QtCore.QThread):
time_diff = time() - start_time
self.msleep(max(int(self.wait_time - time_diff), 0))
self.msleep(max(int((self.wait_time - time_diff) * 1000), 0))
def process_spnav_events(self):
event = spnav.spnav_poll_event()

View File

@@ -5,6 +5,7 @@
from PyQt5 import QtCore, QtWidgets, QtGui
from PIL.ImageQt import ImageQt
from PIL import Image
import numpy
import logging
@@ -12,12 +13,15 @@ import rospy
# Custom Imports
import RoverMap
from sensor_msgs.msg import NavSatFix
#####################################
# Global Variables
#####################################
# put some stuff here later so you can remember
GPS_POSITION_TOPIC = "/rover_odometry/fix"
class RoverMapCoordinator(QtCore.QThread):
pixmap_ready_signal = QtCore.pyqtSignal()
@@ -36,6 +40,8 @@ class RoverMapCoordinator(QtCore.QThread):
self.logger = logging.getLogger("groundstation")
self.gps_position_subscriber = rospy.Subscriber(GPS_POSITION_TOPIC, NavSatFix, self.gps_position_updated_callback)
self.run_thread_flag = True
self.setup_map_flag = True
@@ -47,6 +53,9 @@ class RoverMapCoordinator(QtCore.QThread):
self.map_pixmap = None
self.last_map_pixmap_cache_key = None
self.longitude = None
self.latitude = None
def run(self):
self.logger.debug("Starting Map Coordinator Thread")
@@ -72,13 +81,13 @@ class RoverMapCoordinator(QtCore.QThread):
def _map_setup(self):
self.google_maps_object = RoverMap.GMapsStitcher(1280,
720,
44.567161,
-123.278432,
18,
44.5675721667,
-123.2750535,
20, # FIXME: Used to be 18
'satellite',
None, 20)
self.overlay_image_object = (
RoverMap.OverlayImage(44.567161, -123.278432,
RoverMap.OverlayImage(44.5675721667, -123.2750535,
self.google_maps_object.northwest,
self.google_maps_object.southeast,
self.google_maps_object.big_image.size[0],
@@ -136,15 +145,23 @@ class RoverMapCoordinator(QtCore.QThread):
return temp_list
def update_overlay(self):
longitude = 44.567161
latitude = -123.278432
navigation_list = self._get_table_elements(self.navigation_label)
# landmark_list = self._get_table_elements(self.landmark_label)
landmark_list = []
self.overlay_image = self.overlay_image_object.update_new_location(
latitude,
longitude,
70,
navigation_list,
landmark_list)
# self.overlay_image.save("something.png")
if self.latitude and self.longitude:
if not numpy.isnan(self.latitude) and not numpy.isnan(self.longitude):
longitude = self.latitude
latitude = self.longitude
navigation_list = self._get_table_elements(self.navigation_label)
# landmark_list = self._get_table_elements(self.landmark_label)
landmark_list = []
self.overlay_image = self.overlay_image_object.update_new_location(
latitude,
longitude,
70,
navigation_list,
landmark_list)
# self.overlay_image.save("something.png")
def gps_position_updated_callback(self, data):
self.latitude = data.latitude
self.longitude = data.longitude

View File

@@ -7,6 +7,8 @@ from PyQt5 import QtWidgets, QtCore, QtGui, uic
from std_msgs.msg import Empty
import PIL.Image
from PIL.ImageQt import ImageQt
from std_msgs.msg import UInt16
# import Timer
REQUEST_UPDATE_TOPIC = "/rover_status/update_requested"
@@ -18,6 +20,12 @@ FRSKY_TOPIC_NAME = "/rover_status/frsky_status"
GPS_TOPIC_NAME = "/rover_status/gps_status"
JETSON_TOPIC_NAME = "/rover_status/jetson_status"
MISC_TOPIC_NAME = "/rover_status/misc_status"
BATTERY_TOPIC_NAME = "/rover_status/battery_status"
CO2_TOPIC_NAME = "/rover_control/tower/status/co2"
COLOR_GREEN = "background-color: darkgreen; border: 1px solid black;"
COLOR_ORANGE = "background-color: orange; border: 1px solid black;"
COLOR_RED = "background-color: darkred; border: 1px solid black;"
class SensorCore(QtCore.QThread):
@@ -35,6 +43,13 @@ class SensorCore(QtCore.QThread):
bogie_connection_2_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
bogie_connection_3_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
gps_fix_update_ready__signal = QtCore.pyqtSignal(str)
gps_heading_valid_update_ready__signal = QtCore.pyqtSignal(str)
gps_num_satellites_update_ready__signal = QtCore.pyqtSignal(str)
gps_accuracy_update_ready__signal = QtCore.pyqtSignal(str)
co2_levels_update_ready__signal = QtCore.pyqtSignal(str)
camera_zed_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
camera_under_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
camera_chassis_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
@@ -44,6 +59,9 @@ class SensorCore(QtCore.QThread):
frsky_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
battery_voltage_update_ready__signal = QtCore.pyqtSignal(str)
battery_voltage_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
def __init__(self, shared_objects):
super(SensorCore, self).__init__()
@@ -62,7 +80,10 @@ class SensorCore(QtCore.QThread):
self.frsky = self.screen_main_window.frsky # type: QtWidgets.QLabel
self.nav_mouse = self.screen_main_window.nav_mouse # type: QtWidgets.QLabel
self.joystick = self.screen_main_window.joystick # type: QtWidgets.QLabel
self.gps = self.screen_main_window.gps # type: QtWidgets.QLabel
self.gps_fix_label = self.screen_main_window.gps_fix_label # type: QtWidgets.QLabel
self.gps_heading_valid_label = self.screen_main_window.gps_heading_valid_label # type: QtWidgets.QLabel
self.gps_num_satellites_label = self.screen_main_window.gps_num_satellites_label # type: QtWidgets.QLabel
self.gps_accuracy_label = self.screen_main_window.gps_accuracy_label # type: QtWidgets.QLabel
self.zed = self.screen_main_window.zed # type: QtWidgets.QLabel
self.main_cam = self.screen_main_window.main_cam # type: QtWidgets.QLabel
self.chassis_cam = self.screen_main_window.chassis_cam # type: QtWidgets.QLabel
@@ -72,6 +93,8 @@ class SensorCore(QtCore.QThread):
self.ram = self.screen_main_window.ram # type: QtWidgets.QLabel
self.gpu_temp = self.screen_main_window.gpu_temp # type: QtWidgets.QLabel
self.emmc = self.screen_main_window.emmc # type: QtWidgets.QLabel
self.battery = self.screen_main_window.battery_voltage_status_label # type: QtWidgets.QLabel
self.co2_levels_label = self.screen_main_window.co2_levels_label # type: QtWidgets.QLabel
# ########## subscriptions pulling data from system_statuses_node.py ##########
self.camera_status = rospy.Subscriber(CAMERA_TOPIC_NAME, CameraStatuses, self.__camera_callback)
@@ -79,13 +102,16 @@ class SensorCore(QtCore.QThread):
self.gps_status = rospy.Subscriber(GPS_TOPIC_NAME, GPSInfo, self.__gps_callback)
self.jetson_status = rospy.Subscriber(JETSON_TOPIC_NAME, JetsonInfo, self.__jetson_callback)
self.misc_status = rospy.Subscriber(MISC_TOPIC_NAME, MiscStatuses, self.__misc_callback)
self.battery_status = rospy.Subscriber(BATTERY_TOPIC_NAME, BatteryStatusMessage, self.__battery_callback)
self.co2_status = rospy.Subscriber(CO2_TOPIC_NAME, UInt16, self.__co2_callback)
self.camera_msg = CameraStatuses()
self.bogie_msg = BogieStatuses()
self.bogie_msg = None # BogieStatuses()
self.FrSky_msg = FrSkyStatus()
self.GPS_msg = GPSInfo()
self.jetson_msg = JetsonInfo()
self.misc_msg = MiscStatuses()
self.battery_msg = BatteryStatusMessage()
self.update_requester = rospy.Publisher(REQUEST_UPDATE_TOPIC, Empty, queue_size=10)
@@ -102,84 +128,94 @@ class SensorCore(QtCore.QThread):
if data.camera_zed is False:
# self.zed.setStyleSheet("background-color: red;")
self.camera_zed_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.camera_zed_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
# self.zed.setStyleSheet("background-color: darkgreen;")
self.camera_zed_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
# self.zed.setStyleSheet(COLOR_GREEN)
self.camera_zed_stylesheet_change_ready__signal.emit(COLOR_GREEN)
if data.camera_undercarriage is False:
# self.under_cam.setStyleSheet("background-color: darkred;")
self.camera_under_stylesheet_change_ready__signal.emit("background-color: darkred;")
# self.under_cam.setStyleSheet(COLOR_RED)
self.camera_under_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
# self.under_cam.setStyleSheet("background-color: darkgreen;")
self.camera_under_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
# self.under_cam.setStyleSheet(COLOR_GREEN)
self.camera_under_stylesheet_change_ready__signal.emit(COLOR_GREEN)
if data.camera_chassis is False:
# self.chassis_cam.setStyleSheet("background-color: darkred;")
self.camera_chassis_stylesheet_change_ready__signal.emit("background-color: darkred;")
# self.chassis_cam.setStyleSheet(COLOR_RED)
self.camera_chassis_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
# self.chassis_cam.setStyleSheet("background-color: darkgreen;")
self.camera_chassis_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
# self.chassis_cam.setStyleSheet(COLOR_GREEN)
self.camera_chassis_stylesheet_change_ready__signal.emit(COLOR_GREEN)
if data.camera_main_navigation is False:
# self.main_cam.setStyleSheet("background-color: darkred;")
self.camera_main_stylesheet_change_ready__signal.emit("background-color: darkred;")
# self.main_cam.setStyleSheet(COLOR_RED)
self.camera_main_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
# self.main_cam.setStyleSheet("background-color: darkgreen;")
self.camera_main_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
# self.main_cam.setStyleSheet(COLOR_GREEN)
self.camera_main_stylesheet_change_ready__signal.emit(COLOR_GREEN)
def __frsky_callback(self, data):
self.FrSky_msg.FrSky_controller_connection_status = data.FrSky_controller_connection_status
if self.FrSky_msg.FrSky_controller_connection_status is False:
self.frsky_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.frsky_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
self.frsky_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
self.frsky_stylesheet_change_ready__signal.emit(COLOR_GREEN)
def __jetson_callback(self, data):
self.jetson_cpu_update_ready__signal.emit("TX2 CPU\n" + str(data.jetson_CPU) + "%")
self.jetson_cpu_update_ready__signal.emit("TX2 CPU\n" + str(data.jetson_CPU) + " %")
if data.jetson_CPU > 85:
self.jetson_cpu_stylesheet_change_ready__signal.emit("background-color: orange;")
self.jetson_cpu_stylesheet_change_ready__signal.emit(COLOR_ORANGE)
elif data.jetson_CPU > 95:
self.jetson_cpu_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.jetson_cpu_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
self.jetson_cpu_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
self.jetson_cpu_stylesheet_change_ready__signal.emit(COLOR_GREEN)
self.jetson_ram_update_ready__signal.emit("TX2 RAM\n" + str(data.jetson_RAM) + "%")
self.jetson_ram_update_ready__signal.emit("TX2 RAM\n" + str(data.jetson_RAM) + " %")
if data.jetson_RAM > 79:
self.jetson_ram_stylesheet_change_ready__signal.emit("background-color: orange;")
self.jetson_ram_stylesheet_change_ready__signal.emit(COLOR_ORANGE)
elif data.jetson_RAM > 89:
self.jetson_ram_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.jetson_ram_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
self.jetson_ram_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
self.jetson_ram_stylesheet_change_ready__signal.emit(COLOR_GREEN)
self.jetson_gpu_temp_update_ready__signal.emit("TX2 TEMP\n" + str(data.jetson_GPU_temp) + "°C")
self.jetson_gpu_temp_update_ready__signal.emit("TX2 TEMP\n" + str(data.jetson_GPU_temp) + " °C")
if data.jetson_GPU_temp > 64:
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit("background-color: orange;")
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit(COLOR_ORANGE)
elif data.jetson_GPU_temp > 79:
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
self.jetson_gpu_temp_stylesheet_change_ready__signal.emit(COLOR_GREEN)
self.jetson_emmc_update_ready__signal.emit("TX2 EMMC\n" + str(data.jetson_EMMC) + "%")
self.jetson_emmc_update_ready__signal.emit("TX2 EMMC\n" + str(data.jetson_EMMC) + " %")
if data.jetson_EMMC > 79:
self.jetson_emmc_stylesheet_change_ready__signal.emit("background-color: orange;")
self.jetson_emmc_stylesheet_change_ready__signal.emit(COLOR_ORANGE)
elif data.jetson_EMMC > 89:
self.jetson_emmc_stylesheet_change_ready__signal.emit("background-color: darkred;")
self.jetson_emmc_stylesheet_change_ready__signal.emit(COLOR_RED)
else:
self.jetson_emmc_stylesheet_change_ready__signal.emit("background-color: darkgreen")
self.jetson_emmc_stylesheet_change_ready__signal.emit(COLOR_GREEN)
def __gps_callback(self, data):
self.GPS_msg.UTC_GPS_time = data.UTC_GPS_time
if not data.GPS_connection_status:
self.gps_stylesheet_change_ready__signal.emit("background-color: darkred;")
if data.gps_connected:
self.gps_fix_update_ready__signal.emit("GPS Fix\nTrue")
self.gps_stylesheet_change_ready__signal.emit(COLOR_GREEN)
else:
self.gps_stylesheet_change_ready__signal.emit("background-color: darkgreen;")
self.gps_stylesheet_change_ready__signal.emit(COLOR_RED)
self.gps_fix_update_ready__signal.emit("GPS Fix\nFalse")
if data.gps_heading != -1:
self.gps_heading_valid_update_ready__signal.emit("GPS Heading Valid\nTrue")
else:
self.gps_heading_valid_update_ready__signal.emit("GPS Heading Valid\nFalse")
self.gps_num_satellites_update_ready__signal.emit("GPS Satellites\n%s" % data.num_satellites)
self.gps_accuracy_update_ready__signal.emit("GPS Accuracy\n%2.2f m" % data.horizontal_dilution)
def __misc_callback(self, data):
self.misc_msg.arm_connection_status = data.arm_connection_status
@@ -188,6 +224,22 @@ class SensorCore(QtCore.QThread):
self.misc_msg.tower_connection_status = data.tower_connection_status
self.misc_msg.chassis_pan_tilt_connection_status = data.chassis_pan_tilt_connection_status
def __battery_callback(self, data):
voltage = data.battery_voltage / 1000.0
if voltage >= 20:
self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_GREEN)
else:
self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_RED)
self.battery_voltage_update_ready__signal.emit("Battery Voltage\n" + str(voltage) + " V")
def __co2_callback(self, data):
if data.data != 9999:
self.co2_levels_update_ready__signal.emit("CO2 Levels\n%d ppm" % data.data)
else:
self.co2_levels_update_ready__signal.emit("CO2 Levels\n--- ppm")
def __display_time(self):
time = QtCore.QTime.currentTime()
temp = time.toString('hh:mm')
@@ -212,9 +264,19 @@ class SensorCore(QtCore.QThread):
self.camera_under_stylesheet_change_ready__signal.connect(self.under_cam.setStyleSheet)
self.camera_chassis_stylesheet_change_ready__signal.connect(self.chassis_cam.setStyleSheet)
self.camera_main_stylesheet_change_ready__signal.connect(self.main_cam.setStyleSheet)
self.gps_stylesheet_change_ready__signal.connect(self.gps.setStyleSheet)
self.gps_stylesheet_change_ready__signal.connect(self.gps_fix_label.setStyleSheet)
self.frsky_stylesheet_change_ready__signal.connect(self.frsky.setStyleSheet)
self.gps_fix_update_ready__signal.connect(self.gps_fix_label.setText)
self.gps_heading_valid_update_ready__signal.connect(self.gps_heading_valid_label.setText)
self.gps_num_satellites_update_ready__signal.connect(self.gps_num_satellites_label.setText)
self.gps_accuracy_update_ready__signal.connect(self.gps_accuracy_label.setText)
self.co2_levels_update_ready__signal.connect(self.co2_levels_label.setText)
self.battery_voltage_update_ready__signal.connect(self.battery.setText)
self.battery_voltage_stylesheet_change_ready__signal.connect(self.battery.setStyleSheet)
def setup_signals(self, start_signal, signals_and_slots_signal, kill_signal):
start_signal.connect(self.start)
signals_and_slots_signal.connect(self.connect_signals_and_slots)

View File

@@ -0,0 +1,126 @@
# coding=utf-8
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets
import logging
from time import time
import paramiko
from pprint import pprint
import json
#####################################
# Global Variables
#####################################
THREAD_HERTZ = 5
ACCESS_POINT_IP = "192.168.1.20" # The channel only has to be set on the access point. The staion will adjust.
ACCESS_POINT_USER = "ubnt"
ACCESS_POINT_PASSWORD = "rover4lyfe^" # We don't care about this password, don't freak out. Wifi is open anyways...
GENERAL_WIRELESS_INFO_COMMAND = "wstalist"
#####################################
# UbiquitiRadioSettings Class Definition
#####################################
class UbiquitiStatus(QtCore.QThread):
connection_quality_update_ready__signal = QtCore.pyqtSignal(str)
successful_transmission_update_ready__signal = QtCore.pyqtSignal(str)
radio_rates_update_ready__signal = QtCore.pyqtSignal(str)
radio_latency_update_ready__signal = QtCore.pyqtSignal(str)
def __init__(self, shared_objects):
super(UbiquitiStatus, self).__init__()
# ########## Reference to class init variables ##########
self.shared_objects = shared_objects
self.left_screen = self.shared_objects["screens"]["left_screen"]
self.connection_quality_label = self.left_screen.connection_quality_label
self.successful_transmit_label = self.left_screen.successful_transmit_label
self.radio_rates_label = self.left_screen.radio_rates_label
self.radio_latency_label = self.left_screen.radio_latency_label
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("groundstation")
# ########## Thread Flags ##########
self.run_thread_flag = True
# ########## Class Variables ##########
self.wait_time = 1.0 / THREAD_HERTZ
self.ssh_client = None
def run(self):
try:
self.setup_and_connect_ssh_client()
except Exception:
return
while self.run_thread_flag:
start_time = time()
try:
self.get_and_show_ubiquiti_status()
except Exception, e:
print e
time_diff = time() - start_time
self.msleep(max(int((self.wait_time - time_diff) * 1000), 0))
def setup_and_connect_ssh_client(self):
self.ssh_client = paramiko.SSHClient()
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh_client.connect(ACCESS_POINT_IP, username=ACCESS_POINT_USER, password=ACCESS_POINT_PASSWORD,
compress=True)
def get_and_show_ubiquiti_status(self):
ssh_stdin, ssh_stdout, ssh_stderr = self.ssh_client.exec_command(GENERAL_WIRELESS_INFO_COMMAND)
try:
output_json = json.loads(ssh_stdout.read())[0]
transmit_percent = output_json["ccq"]
quality = output_json["airmax"]["quality"]
# capacity = output_json["airmax"]["capacity"]
rx_rate = output_json["rx"]
tx_rate = output_json["tx"]
ground_tx_latency = output_json["tx_latency"]
rover_tx_latency = output_json["remote"]["tx_latency"]
except IndexError:
transmit_percent = 0
quality = 0
# capacity = output_json["airmax"]["capacity"]
rx_rate = 0
tx_rate = 0
ground_tx_latency = "----"
rover_tx_latency = "----"
self.connection_quality_update_ready__signal.emit("Connection Quality\n%s %%" % quality)
self.successful_transmission_update_ready__signal.emit("Successful Transmit\n%s %%" % transmit_percent)
self.radio_rates_update_ready__signal.emit("TX Rate: %s Mbps\nRX Rate: %s Mbps" % (tx_rate, rx_rate))
self.radio_latency_update_ready__signal.emit(
"TX Latency: %s ms\nRX Latency: %s ms" % (ground_tx_latency, rover_tx_latency))
def connect_signals_and_slots(self):
self.connection_quality_update_ready__signal.connect(self.connection_quality_label.setText)
self.successful_transmission_update_ready__signal.connect(self.successful_transmit_label.setText)
self.radio_rates_update_ready__signal.connect(self.radio_rates_label.setText)
self.radio_latency_update_ready__signal.connect(self.radio_latency_label.setText)
def setup_signals(self, start_signal, signals_and_slots_signal, kill_signal):
start_signal.connect(self.start)
signals_and_slots_signal.connect(self.connect_signals_and_slots)
kill_signal.connect(self.on_kill_threads_requested__slot)
def on_kill_threads_requested__slot(self):
self.run_thread_flag = False

View File

@@ -36,6 +36,8 @@ class RoverVideoCoordinator(QtCore.QThread):
update_element_stylesheet__signal = QtCore.pyqtSignal()
pan_tilt_selection_changed__signal = QtCore.pyqtSignal(str)
def __init__(self, shared_objects):
super(RoverVideoCoordinator, self).__init__()
@@ -81,6 +83,9 @@ class RoverVideoCoordinator(QtCore.QThread):
self.msleep(3000)
# Setup cameras
self.main_nav_index = -1
self.chassis_index = -1
self.__get_cameras()
self.__setup_video_threads()
@@ -105,6 +110,8 @@ class RoverVideoCoordinator(QtCore.QThread):
def run(self):
self.logger.debug("Starting Video Coordinator Thread")
self.__broadcast_current_pan_tilt_selection()
while self.run_thread_flag:
self.__set_max_resolutions()
self.__toggle_background_cameras_if_needed()
@@ -115,6 +122,22 @@ class RoverVideoCoordinator(QtCore.QThread):
self.logger.debug("Stopping Video Coordinator Thread")
def __broadcast_current_pan_tilt_selection(self):
setting = None
if self.current_label_for_joystick_adjust == 0: # primary
setting = self.primary_label_current_setting
elif self.current_label_for_joystick_adjust == 1: # secondary
setting = self.secondary_label_current_setting
elif self.current_label_for_joystick_adjust == 2: # tertiary
setting = self.tertiary_label_current_setting
if setting == self.main_nav_index:
self.pan_tilt_selection_changed__signal.emit("tower_pan_tilt")
elif setting == self.chassis_index:
self.pan_tilt_selection_changed__signal.emit("chassis_pan_tilt")
else:
self.pan_tilt_selection_changed__signal.emit("no_pan_tilt")
def __set_max_resolutions(self):
if self.set_max_resolutions_flag:
self.camera_threads[self.valid_cameras[self.primary_label_current_setting]].set_hard_max_resolution(PRIMARY_LABEL_MAX)
@@ -178,7 +201,23 @@ class RoverVideoCoordinator(QtCore.QThread):
if camera in names:
names.remove(camera)
self.valid_cameras = list(names)
self.valid_cameras = []
current_count = 0
if "main_navigation" in names:
self.valid_cameras.append("main_navigation")
self.main_nav_index = current_count
current_count += 1
if "chassis" in names:
self.valid_cameras.append("chassis")
self.chassis_index = current_count
if "undercarriage" in names:
self.valid_cameras.append("undercarriage")
if "end_effector" in names:
self.valid_cameras.append("end_effector")
def __setup_video_threads(self):
for camera in self.valid_cameras:
@@ -275,16 +314,20 @@ class RoverVideoCoordinator(QtCore.QThread):
else:
self.current_label_for_joystick_adjust = new_selection
self.__broadcast_current_pan_tilt_selection()
self.update_element_stylesheet__signal.emit()
def on_camera_selection_for_current_gui_element_changed(self, direction):
if self.current_label_for_joystick_adjust == 0: # primary
if self.current_label_for_joystick_adjust == 0: # primary
self.primary_label_current_setting = (self.primary_label_current_setting + direction) % len(self.valid_cameras)
elif self.current_label_for_joystick_adjust == 1: # secondary
self.secondary_label_current_setting = (self.secondary_label_current_setting + direction) % len(self.valid_cameras)
elif self.current_label_for_joystick_adjust == 2: # tertiary
self.tertiary_label_current_setting = (self.tertiary_label_current_setting + direction) % len(self.valid_cameras)
self.__broadcast_current_pan_tilt_selection()
self.set_max_resolutions_flag = True
self.update_element_stylesheet__signal.emit()

View File

@@ -251,33 +251,31 @@
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="2">
<widget class="QLabel" name="ram">
<property name="styleSheet">
<string notr="true">background-color: darkred;</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;TX2 RAM&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<item row="5" column="3">
<widget class="QLabel" name="emmc">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;TX2 EMMC&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>TX2 EMMC
-- %</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="9" column="5">
<item row="13" column="4">
<widget class="QLabel" name="chassis_cam">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -311,7 +309,7 @@
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>Chassis Camera
@@ -322,7 +320,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="9" column="4">
<item row="13" column="3">
<widget class="QLabel" name="under_cam">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -356,7 +354,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>Undercarriage Camera
@@ -395,7 +393,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -443,7 +441,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>FrSky
@@ -454,7 +452,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="0" column="4">
<item row="0" column="3">
<widget class="QLabel" name="nav_mouse">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -485,7 +483,7 @@ Connected</string>
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>3D Nav Mouse
@@ -496,7 +494,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="0" column="5">
<item row="0" column="4">
<widget class="QLabel" name="joystick">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -524,7 +522,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -538,7 +536,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="9" column="2">
<item row="13" column="2">
<widget class="QLabel" name="main_cam">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -566,7 +564,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -580,49 +578,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="gps">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
</property>
<property name="text">
<string>GPS
Fix</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="4">
<item row="8" column="3">
<widget class="QLabel" name="middle_left_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -653,18 +609,18 @@ Fix</string>
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>Middle Left Wheel
Disconnected</string>
Connected</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="5">
<item row="8" column="4">
<widget class="QLabel" name="rear_left_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -692,7 +648,7 @@ Disconnected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -706,7 +662,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="4" column="2">
<item row="8" column="2">
<widget class="QLabel" name="front_left_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -734,7 +690,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -748,7 +704,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="13" column="1">
<widget class="QLabel" name="zed">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -776,7 +732,7 @@ Connected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -790,33 +746,55 @@ Disconnected</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="5" column="1">
<widget class="QLabel" name="cpu">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;TX2 CPU&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>TX2 CPU
-- %</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="5">
<item row="5" column="4">
<widget class="QLabel" name="gpu_temp">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:9pt; font-weight:600;&quot;&gt;TX2 TEMP&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>TX2 Temp
-- °C</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="6" column="2">
<item row="10" column="2">
<widget class="QLabel" name="front_right_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -844,7 +822,7 @@ Disconnected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -858,49 +836,7 @@ Connected</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="battery_voltage_status_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Battery Voltage
N/A</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="6" column="4">
<item row="10" column="3">
<widget class="QLabel" name="middle_right_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -928,21 +864,45 @@ N/A</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkred;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Middle Right Wheel
Disconnected</string>
Connected</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="6" column="5">
<item row="5" column="2">
<widget class="QLabel" name="ram">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>TX2 RAM
-- %</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="10" column="4">
<widget class="QLabel" name="rear_right_wheel_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -970,7 +930,7 @@ Disconnected</string>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen;</string>
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
@@ -984,6 +944,426 @@ Connected</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="connection_quality_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Connection Quality
-- %</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="radio_rates_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>RX Rate: --- Mbps
TX Rate: --- Mbps</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="successful_transmit_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Successful Transmit
-- %</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="radio_latency_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>TX Latency: ----- ms
RX Latency: ----- ms</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="gps_accuracy_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>GPS Accuracy
-- m</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="gps_num_satellites_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>GPS Satellites
0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="gps_heading_valid_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>GPS Heading Valid
False</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="gps_fix_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="text">
<string>GPS Fix
False</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="battery_voltage_status_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Battery Voltage
N/A</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="co2_levels_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>9999999</width>
<height>9999999</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color:darkgreen; border: 0.5px solid black;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>CO2 Levels
--- ppm</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@@ -20,6 +20,7 @@ import Framework.NavigationSystems.SpeedAndHeadingIndication as SpeedAndHeading
import Framework.NavigationSystems.WaypointsCoordinator as WaypointsCoordinator
import Framework.ArmSystems.ArmIndication as ArmIndication
import Framework.StatusSystems.StatusCore as StatusCore
import Framework.StatusSystems.UbiquitiStatusCore as UbiquitiStatusCore
import Framework.SettingsSystems.UbiquitiRadioSettings as UbiquitiRadioSettings
import Framework.InputSystems.SpaceNavControlSender as SpaceNavControlSender
@@ -108,6 +109,7 @@ class GroundStation(QtCore.QObject):
self.__add_thread("Speed and Heading", SpeedAndHeading.SpeedAndHeadingIndication(self.shared_objects))
self.__add_thread("Arm Indication", ArmIndication.ArmIndication(self.shared_objects))
self.__add_thread("Rover Status", StatusCore.SensorCore(self.shared_objects))
self.__add_thread("Ubiquiti Status", UbiquitiStatusCore.UbiquitiStatus(self.shared_objects))
self.__add_thread("Ubiquiti Radio Settings", UbiquitiRadioSettings.UbiquitiRadioSettings(self.shared_objects))
self.__add_thread("Waypoints Coordinator", WaypointsCoordinator.WaypointsCoordinator(self.shared_objects))
self.__add_thread("Spacenav Sender", SpaceNavControlSender.SpaceNavControlSender(self.shared_objects))

View File

@@ -10,22 +10,24 @@ from time import time, sleep
import serial.rs485
import minimalmodbus
from std_msgs.msg import UInt8, UInt16
# Custom Imports
from rover_control.msg import TowerPanTiltControlMessage
#####################################
# Global Variables
#####################################
NODE_NAME = "drive_control"
NODE_NAME = "chassis_pan_tilt_control"
DEFAULT_PORT = "/dev/ttyUSB0"
DEFAULT_PORT = "/dev/rover/ttyChassisPanTilt"
DEFAULT_BAUD = 115200
DEFAULT_INVERT = False
DEFAULT_TOWER_PAN_TILT_CONTROL_TOPIC = "tower/control"
DEFAULT_PAN_TILT_CONTROL_TOPIC = "chassis/pan_tilt/control"
NODE_ID = 1
PAN_TILT_NODE_ID = 1
COMMUNICATIONS_TIMEOUT = 0.01 # Seconds
@@ -34,7 +36,7 @@ TX_DELAY = 0.01
DEFAULT_HERTZ = 20
MODBUS_REGISTERS = {
PAN_TILT_MODBUS_REGISTERS = {
"CENTER_ALL": 0,
"PAN_ADJUST_POSITIVE": 1,
@@ -48,39 +50,42 @@ PAN_TILT_CONTROL_DEFAULT_MESSAGE = [
0, # No pan positive adjustment
0, # No pan negative adjustment
0, # No tilt positive adjustment
0 # No tilt negative adjustement
0 # No tilt negative adjustement
]
PAN_TILT_LAST_SEEN_TIMEOUT = 2 # seconds
NODE_LAST_SEEN_TIMEOUT = 2 # seconds
#####################################
# DriveControl Class Definition
#####################################
class TowerPanTiltControl(object):
class ChassisPanTiltControl(object):
def __init__(self):
rospy.init_node(NODE_NAME)
self.port = rospy.get_param("~port", DEFAULT_PORT)
self.baud = rospy.get_param("~baud", DEFAULT_BAUD)
self.node_id = rospy.get_param("~node_id", NODE_ID)
self.pan_tilt_node_id = rospy.get_param("~pan_tilt_node_id", PAN_TILT_NODE_ID)
self.pan_tilt_control_subscriber_topic = rospy.get_param("~pan_tilt_control_topic", DEFAULT_TOWER_PAN_TILT_CONTROL_TOPIC)
self.pan_tilt_control_subscriber_topic = rospy.get_param("~pan_tilt_control_topic",
DEFAULT_PAN_TILT_CONTROL_TOPIC)
self.wait_time = 1.0 / rospy.get_param("~hertz", DEFAULT_HERTZ)
self.pan_tilt_node = None
self.tower_node = None
self.connect_to_pan_tilt()
self.connect_to_pan_tilt_and_tower()
self.pan_tilt_control_subscriber = \
rospy.Subscriber(self.pan_tilt_control_subscriber_topic, TowerPanTiltControlMessage, self.pan_tilt_control_callback)
self.pan_tilt_control_subscriber = rospy.Subscriber(self.pan_tilt_control_subscriber_topic,
TowerPanTiltControlMessage,
self.pan_tilt_control_callback)
self.pan_tilt_control_message = None
self.new_control_message = False
self.new_pan_tilt_control_message = False
self.pan_tilt_last_seen = time()
self.modbus_nodes_seen_time = time()
self.run()
@@ -98,55 +103,62 @@ class TowerPanTiltControl(object):
try:
self.send_pan_tilt_control_message()
self.pan_tilt_last_seen = time()
self.modbus_nodes_seen_time = time()
except Exception, error:
print "Error occurred:", error
pass
# print "Error occurred:", error
if (time() - self.pan_tilt_last_seen) > PAN_TILT_LAST_SEEN_TIMEOUT:
print "Tower pan/tilt not seen for", PAN_TILT_LAST_SEEN_TIMEOUT, "seconds. Exiting."
if (time() - self.modbus_nodes_seen_time) > NODE_LAST_SEEN_TIMEOUT:
print "Chassis pan/tilt not seen for", NODE_LAST_SEEN_TIMEOUT, "seconds. Exiting."
return # Exit so respawn can take over
time_diff = time() - start_time
sleep(max(self.wait_time - time_diff, 0))
def connect_to_pan_tilt(self):
self.pan_tilt_node = minimalmodbus.Instrument(self.port, int(self.node_id))
def connect_to_pan_tilt_and_tower(self):
self.pan_tilt_node = minimalmodbus.Instrument(self.port, int(self.pan_tilt_node_id))
self.__setup_minimalmodbus_for_485()
def send_startup_centering_command(self):
registers = list(PAN_TILT_CONTROL_DEFAULT_MESSAGE)
registers[MODBUS_REGISTERS["CENTER_ALL"]] = 1
self.pan_tilt_node.write_registers(0, registers)
try:
registers = list(PAN_TILT_CONTROL_DEFAULT_MESSAGE)
registers[PAN_TILT_MODBUS_REGISTERS["CENTER_ALL"]] = 1
self.pan_tilt_node.write_registers(0, registers)
except:
pass
def send_pan_tilt_control_message(self):
if self.new_control_message:
if self.new_pan_tilt_control_message:
pan_tilt_control_message = self.pan_tilt_control_message # type: TowerPanTiltControlMessage
registers = list(PAN_TILT_CONTROL_DEFAULT_MESSAGE)
registers[MODBUS_REGISTERS["CENTER_ALL"]] = int(pan_tilt_control_message.should_center)
registers[PAN_TILT_MODBUS_REGISTERS["CENTER_ALL"]] = int(pan_tilt_control_message.should_center)
if pan_tilt_control_message.relative_pan_adjustment >= 0:
registers[MODBUS_REGISTERS["PAN_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_pan_adjustment
registers[
PAN_TILT_MODBUS_REGISTERS["PAN_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_pan_adjustment
else:
registers[MODBUS_REGISTERS["PAN_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_pan_adjustment
registers[PAN_TILT_MODBUS_REGISTERS[
"PAN_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_pan_adjustment
if pan_tilt_control_message.relative_tilt_adjustment >= 0:
registers[MODBUS_REGISTERS["TILT_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_tilt_adjustment
registers[PAN_TILT_MODBUS_REGISTERS[
"TILT_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_tilt_adjustment
else:
registers[MODBUS_REGISTERS["TILT_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_tilt_adjustment
registers[PAN_TILT_MODBUS_REGISTERS[
"TILT_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_tilt_adjustment
self.pan_tilt_node.write_registers(0, registers)
self.new_control_message = False
self.new_pan_tilt_control_message = False
else:
self.pan_tilt_node.write_registers(0, PAN_TILT_CONTROL_DEFAULT_MESSAGE)
def pan_tilt_control_callback(self, pan_tilt_control):
self.pan_tilt_control_message = pan_tilt_control
self.new_control_message = True
self.new_pan_tilt_control_message = True
if __name__ == "__main__":
TowerPanTiltControl()
ChassisPanTiltControl()

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python
import rospy
import time
from rover_control.msg import TowerPanTiltControlMessage
DEFAULT_TOWER_PAN_TILT_CONTROL_TOPIC = "chassis/pan_tilt/control"
rospy.init_node("chassis_pan_tilt_tester")
publisher = rospy.Publisher(DEFAULT_TOWER_PAN_TILT_CONTROL_TOPIC, TowerPanTiltControlMessage, queue_size=1)
time.sleep(2)
message = TowerPanTiltControlMessage()
message.should_center = 1
publisher.publish(message)
time.sleep(1)
message = TowerPanTiltControlMessage()
message.relative_pan_adjustment = -100
message.relative_tilt_adjustment = -500
publisher.publish(message)

View File

@@ -71,7 +71,6 @@ class DriveCoordinator(object):
self.left_bogie_publisher = rospy.Publisher(self.left_bogie_topic, DriveControlMessage, queue_size=1)
self.right_bogie_publisher = rospy.Publisher(self.right_bogie_topic, DriveControlMessage, queue_size=1)
self.last_message_time = time()
# ########## Run the Class ##########
@@ -91,10 +90,10 @@ class DriveCoordinator(object):
sleep(max(self.wait_time - time_diff, 0))
def process_drive_commands(self):
# if not self.drive_command_data["iris"]["message"].ignore_drive_control:
# self.send_drive_control_command(self.drive_command_data["iris"])
# else:
self.send_drive_control_command(self.drive_command_data["ground_station"])
if not self.drive_command_data["iris"]["message"].ignore_drive_control:
self.send_drive_control_command(self.drive_command_data["iris"])
else:
self.send_drive_control_command(self.drive_command_data["ground_station"])
def send_drive_control_command(self, drive_command_data):

View File

@@ -34,7 +34,7 @@ COMMUNICATIONS_TIMEOUT = 0.01 # Seconds
RX_DELAY = 0.01
TX_DELAY = 0.01
DEFAULT_HERTZ = 20
DEFAULT_HERTZ = 30
MODBUS_REGISTERS = {
"DIRECTION": 0,
@@ -119,7 +119,7 @@ class DriveControl(object):
self.get_drive_status()
except Exception, error:
print "Error occurred:", error
pass
if (time() - self.bogie_last_seen) > BOGIE_LAST_SEEN_TIMEOUT:
print "Bogie not seen for", BOGIE_LAST_SEEN_TIMEOUT, "seconds. Exiting."

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python
#####################################
# Imports
#####################################
# Python native imports
import rospy
from time import time, sleep
import serial.rs485
import minimalmodbus
# Custom Imports
from rover_control.msg import DriveControlMessage
rospy.init_node("tester")
pub = rospy.Publisher("/drive_control/rear", DriveControlMessage, queue_size=1)
sleep(1)
while True:
speed = int(input("Enter Speed: "))
message = DriveControlMessage()
message.first_motor_speed = speed
message.first_motor_direction = 1
message.second_motor_speed = speed
pub.publish(message)
sleep(1)

View File

@@ -70,6 +70,8 @@ REGISTER_STATE_MAPPING = {
"DRIVE_VS_ARM": "SE_SWITCH"
}
IRIS_LAST_SEEN_TIMEOUT = 1 # seconds
#####################################
# IrisController Class Definition
@@ -98,6 +100,8 @@ class IrisController(object):
self.iris_connected = False
self.iris_last_seen_time = time()
self.run()
def __setup_minimalmodbus_for_485(self):
@@ -114,11 +118,16 @@ class IrisController(object):
self.broadcast_drive_if_current_mode()
self.broadcast_arm_if_current_mode()
self.broadcast_iris_status()
self.iris_last_seen_time = time()
except Exception, error:
print "Error occurred:", error
return
if (time() - self.iris_last_seen_time) > IRIS_LAST_SEEN_TIMEOUT:
print "Iris not seen for", IRIS_LAST_SEEN_TIMEOUT, "seconds. Exiting."
return # Exit so respawn can take over
time_diff = time() - start_time
sleep(max(self.wait_time - time_diff, 0))

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env python
#####################################
# Imports
#####################################
# Python native imports
import rospy
from time import time, sleep
import serial.rs485
import minimalmodbus
from std_msgs.msg import UInt8, UInt16
# Custom Imports
from rover_control.msg import TowerPanTiltControlMessage
#####################################
# Global Variables
#####################################
NODE_NAME = "pan_tilt_and_tower_control"
DEFAULT_PORT = "/dev/rover/ttyTowerAndPanTilt"
DEFAULT_BAUD = 115200
DEFAULT_INVERT = False
DEFAULT_TOWER_LIGHT_CONTROL_TOPIC = "tower/light/control"
DEFAULT_TOWER_CO2_STATUS_TOPIC = "tower/status/co2"
DEFAULT_PAN_TILT_CONTROL_TOPIC = "tower/pan_tilt/control"
TOWER_NODE_ID = 1
PAN_TILT_NODE_ID = 2
COMMUNICATIONS_TIMEOUT = 0.02 # Seconds
RX_DELAY = 0.02
TX_DELAY = 0.02
DEFAULT_HERTZ = 20
PAN_TILT_MODBUS_REGISTERS = {
"CENTER_ALL": 0,
"PAN_ADJUST_POSITIVE": 1,
"PAN_ADJUST_NEGATIVE": 2,
"TILT_ADJUST_POSITIVE": 3,
"TILT_ADJUST_NEGATIVE": 4
}
TOWER_MODBUS_REGISTERS = {
"LED_CONTROL": 0,
"CO2_READING_PPM": 1
}
PAN_TILT_CONTROL_DEFAULT_MESSAGE = [
0, # No centering
0, # No pan positive adjustment
0, # No pan negative adjustment
0, # No tilt positive adjustment
0 # No tilt negative adjustement
]
TOWER_LIGHT_STATES = {
"NO_CHANGE": 0,
"LIGHT_OFF": 1,
"LIGHT_FLASH": 2,
"LIGHT_MED": 3,
"LIGHT_HIGH": 4
}
TOWER_CONTROL_DEFAULT_MESSAGE = [
TOWER_LIGHT_STATES["LIGHT_OFF"] # Light off
]
NODE_LAST_SEEN_TIMEOUT = 2 # seconds
#####################################
# DriveControl Class Definition
#####################################
class TowerPanTiltControl(object):
def __init__(self):
rospy.init_node(NODE_NAME)
self.port = rospy.get_param("~port", DEFAULT_PORT)
self.baud = rospy.get_param("~baud", DEFAULT_BAUD)
self.tower_node_id = rospy.get_param("~tower_node_id", TOWER_NODE_ID)
self.pan_tilt_node_id = rospy.get_param("~pan_tilt_node_id", PAN_TILT_NODE_ID)
self.tower_light_control_subscriber_topic = rospy.get_param("~tower_light_control_topic",
DEFAULT_TOWER_LIGHT_CONTROL_TOPIC)
self.pan_tilt_control_subscriber_topic = rospy.get_param("~pan_tilt_control_topic",
DEFAULT_PAN_TILT_CONTROL_TOPIC)
self.tower_co2_publisher_topic = rospy.get_param("~tower_co2_status_topic",
DEFAULT_TOWER_CO2_STATUS_TOPIC)
self.wait_time = 1.0 / rospy.get_param("~hertz", DEFAULT_HERTZ)
self.pan_tilt_node = None
self.tower_node = None
self.connect_to_pan_tilt_and_tower()
self.pan_tilt_control_subscriber = rospy.Subscriber(self.pan_tilt_control_subscriber_topic,
TowerPanTiltControlMessage,
self.pan_tilt_control_callback)
self.tower_light_control_subscriber = rospy.Subscriber(self.tower_light_control_subscriber_topic, UInt8,
self.tower_light_control_callback)
self.tower_co2_publisher = rospy.Publisher(self.tower_co2_publisher_topic, UInt16, queue_size=1)
self.pan_tilt_control_message = None
self.tower_light_control_message = None
self.new_pan_tilt_control_message = False
self.new_tower_light_control_message = False
self.modbus_nodes_seen_time = time()
self.run()
def __setup_minimalmodbus_for_485(self):
self.pan_tilt_node.serial = serial.rs485.RS485(self.port, baudrate=self.baud, timeout=COMMUNICATIONS_TIMEOUT)
self.pan_tilt_node.serial.rs485_mode = serial.rs485.RS485Settings(rts_level_for_rx=1, rts_level_for_tx=0,
delay_before_rx=RX_DELAY,
delay_before_tx=TX_DELAY)
self.tower_node.serial = serial.rs485.RS485(self.port, baudrate=self.baud, timeout=COMMUNICATIONS_TIMEOUT)
self.tower_node.serial.rs485_mode = serial.rs485.RS485Settings(rts_level_for_rx=1, rts_level_for_tx=0,
delay_before_rx=RX_DELAY,
delay_before_tx=TX_DELAY)
def run(self):
self.send_startup_centering_and_lights_off_command()
while not rospy.is_shutdown():
start_time = time()
try:
self.send_pan_tilt_control_message()
self.modbus_nodes_seen_time = time()
except Exception, error:
pass
# print "Error occurred:", error
try:
self.send_tower_control_message()
self.broadcast_co2_reading_message()
self.modbus_nodes_seen_time = time()
except Exception, error:
pass
# print "Error occurred:", error
if (time() - self.modbus_nodes_seen_time) > NODE_LAST_SEEN_TIMEOUT:
print "Tower pan/tilt not seen for", NODE_LAST_SEEN_TIMEOUT, "seconds. Exiting."
return # Exit so respawn can take over
time_diff = time() - start_time
sleep(max(self.wait_time - time_diff, 0))
def connect_to_pan_tilt_and_tower(self):
self.tower_node = minimalmodbus.Instrument(self.port, int(self.tower_node_id))
self.pan_tilt_node = minimalmodbus.Instrument(self.port, int(self.pan_tilt_node_id))
self.__setup_minimalmodbus_for_485()
def send_startup_centering_and_lights_off_command(self):
try:
registers = list(PAN_TILT_CONTROL_DEFAULT_MESSAGE)
registers[PAN_TILT_MODBUS_REGISTERS["CENTER_ALL"]] = 1
self.pan_tilt_node.write_registers(0, registers)
self.tower_node.write_register(0, TOWER_LIGHT_STATES["LIGHT_OFF"])
except:
pass
def send_pan_tilt_control_message(self):
if self.new_pan_tilt_control_message:
pan_tilt_control_message = self.pan_tilt_control_message # type: TowerPanTiltControlMessage
registers = list(PAN_TILT_CONTROL_DEFAULT_MESSAGE)
registers[PAN_TILT_MODBUS_REGISTERS["CENTER_ALL"]] = int(pan_tilt_control_message.should_center)
if pan_tilt_control_message.relative_pan_adjustment >= 0:
registers[
PAN_TILT_MODBUS_REGISTERS["PAN_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_pan_adjustment
else:
registers[PAN_TILT_MODBUS_REGISTERS[
"PAN_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_pan_adjustment
if pan_tilt_control_message.relative_tilt_adjustment >= 0:
registers[PAN_TILT_MODBUS_REGISTERS[
"TILT_ADJUST_POSITIVE"]] = pan_tilt_control_message.relative_tilt_adjustment
else:
registers[PAN_TILT_MODBUS_REGISTERS[
"TILT_ADJUST_NEGATIVE"]] = -pan_tilt_control_message.relative_tilt_adjustment
self.pan_tilt_node.write_registers(0, registers)
self.new_pan_tilt_control_message = False
else:
self.pan_tilt_node.write_registers(0, PAN_TILT_CONTROL_DEFAULT_MESSAGE)
def broadcast_co2_reading_message(self):
self.tower_co2_publisher.publish(UInt16(data=self.tower_node.read_register(1)))
def send_tower_control_message(self):
if self.new_tower_light_control_message:
self.tower_node.write_register(0, self.tower_light_control_message.data)
self.new_tower_light_control_message = False
def pan_tilt_control_callback(self, pan_tilt_control):
self.pan_tilt_control_message = pan_tilt_control
self.new_pan_tilt_control_message = True
def tower_light_control_callback(self, light_control):
self.tower_light_control_message = light_control
self.new_tower_light_control_message = True
if __name__ == "__main__":
TowerPanTiltControl()

View File

@@ -6,7 +6,9 @@
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17100" />
<rosparam param="topics">
[{name: "/rover_control/command_control/ground_station_drive", compress: true, rate: 15.0}]
[{name: "/rover_control/command_control/ground_station_drive", compress: true, rate: 15.0},
{name: "/rover_control/tower/pan_tilt/control", compress: true, rate: 30.0},
{name: "/rover_control/chassis/pan_tilt/control", compress: true, rate: 30.0}]
</rosparam>
</node>
@@ -18,7 +20,7 @@
{name: "/cameras/undercarriage/camera_control", compress: false, rate: 5.0},
{name: "/cameras/main_navigation/camera_control", compress: false, rate: 5.0},
{name: "/cameras/end_effector/camera_control", compress: false, rate: 5.0},
{name: "/rover_status/update_requested", compress: false, rate: 5.0}]
{name: "/rover_status/update_requested", compress: false, rate: 5.0},]
</rosparam>
</node>
</group>

View File

@@ -12,4 +12,7 @@
<include file="$(find rover_main)/launch/rover/topic_transport_senders.launch"/>
<include file="$(find rover_main)/launch/rover/topic_transport_receivers.launch"/>
<!-- ########## Start Odometry Nodes ########## -->
<include file="$(find rover_main)/launch/rover/odometry2.launch"/>
</launch>

View File

@@ -5,17 +5,17 @@
<param name="device_path" value="/dev/rover/camera_undercarriage"/>
</node>
<!-- Start Main Navigation Camera -->
<!--&lt;!&ndash; Start Main Navigation Camera &ndash;&gt;-->
<node name="main_navigation" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 2" respawn="true" output="screen" >
<param name="device_path" value="/dev/rover/camera_main_navigation" />
</node>
<!-- Start Chassis Camera -->
<!--&lt;!&ndash; Start Chassis Camera &ndash;&gt;-->
<node name="chassis" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 3" respawn="true" output="screen" >
<param name="device_path" value="/dev/rover/camera_chassis" />
</node>
<!-- Start End Effector Camera -->
<!--&lt;!&ndash; Start End Effector Camera &ndash;&gt;-->
<node name="end_effector" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 4" respawn="true" output="screen" >
<param name="is_rtsp_camera" value="True" />
<param name="device_path" value="rtsp://192.168.1.11" />

View File

@@ -1,16 +1,18 @@
<launch>
<group ns="rover_control">
<!--<node name="iris_controller" pkg="rover_control" type="iris_controller.py" respawn="true" output="screen">-->
<!--<param name="port" value="/dev/rover/ttyIRIS"/>-->
<!--<param name="hertz" value="20"/>-->
<!--</node>-->
<node name="iris_controller" pkg="rover_control" type="iris_controller.py" respawn="true" output="screen">
<param name="port" value="/dev/rover/ttyIRIS"/>
<param name="hertz" value="20"/>
</node>
<node name="rear_bogie" pkg="rover_control" type="drive_control.py" respawn="true" output="screen">
<param name="port" value="/dev/rover/ttyREAR"/>
<param name="drive_control_topic" value="drive_control/rear"/>
<param name="drive_control_status_topic" value="drive_status/rear"/>
<param name="first_motor_id" value="2"/>
<param name="second_motor_id" value="1"/>
<param name="first_motor_id" value="1"/>
<param name="second_motor_id" value="2"/>
<param name="invert_first_motor" value="True"/>
<param name="invert_second_motor" value="True"/>
</node>
<node name="left_bogie" pkg="rover_control" type="drive_control.py" respawn="true" output="screen">
@@ -29,6 +31,10 @@
<param name="second_motor_id" value="1"/>
</node>
<node name="drive_coordinator" pkg="rover_control" type="drive_coordinator.py" output="screen"/>
<node name="drive_coordinator" pkg="rover_control" type="drive_coordinator.py" respawn="true" output="screen"/>
<node name="tower_and_pan_tilt" pkg="rover_control" type="tower_and_pan_tilt_control.py" respawn="true" output="screen"/>
<node name="chassis_pan_tilt" pkg="rover_control" type="chassis_pan_tilt_control.py" respawn="true" output="screen"/>
</group>
</launch>

View File

@@ -0,0 +1,74 @@
<launch>
<!-- https://github.com/vikiboy/AGV_Localization/blob/master/robot_localization/launch/ekf.launch -->
<!-- https://answers.ros.org/question/241222/fusing-imu-gps-with-robot_location-package/ -->
<group ns="odometry">
<!-- ########## Processes GPS and IMU Messages ########## -->
<node name="odom_record" pkg="rosbag" type="record" args="-o /home/nvidia/BAGS/odometry /odometry/gps/fix /odometry/gps/sentence /odometry/imu/data /odometry/odometry/filtered /odometry/odometry/gps /odometry/vel" output="screen" />
<!-- ########## Processes GPS and IMU Messages ########## -->
<node name="gps_and_imu" pkg="rover_odometry" type="odometry.py" respawn="true" output="screen" />
<!-- ########## Converts GPS Sentences to GPS Fix data ########## -->
<node name="navsat_driver" pkg="nmea_navsat_driver" type="nmea_topic_driver" output="screen">
<remap from="nmea_sentence" to="gps/sentence"/>
<remap from="fix" to="gps/fix"/>
</node>
<!-- ########## Performs a tranform on the GPS and IMU frames so odom can use it ########## -->
<node pkg="tf" type="static_transform_publisher" name="imu_tf" args="0 0 0 0 0 0 1 base_link imu 20"/>
<node pkg="tf" type="static_transform_publisher" name="gps_tf" args="0 0 0 0 0 0 1 base_link gps 20"/>
<!-- ########## Main Odometry Localization Node ########## -->
<node pkg="robot_localization" type="ekf_localization_node" name="ekf_localization" clear_params="true">
<remap from="/gpx/fix" to="gps/fix"/>
<param name="output_frame" value="odom"/>
<param name="frequency" value="20"/>
<param name="odom_used" value="true"/>
<param name="imu_used" value="true"/>
<param name="vo_used" value="false"/>
<param name="sensor_timeout" value="0.1"/>
<param name="two_d_mode" value="false"/>
<param name="map_frame" value="map"/>
<param name="odom_frame" value="odom"/>
<param name="base_link_frame" value="base_link"/>
<param name="world_frame" value="odom"/>
<param name="odom0" value="gps"/>
<param name="imu0" value="imu/data"/>
<rosparam param="odom0_config">[true, true, true,
false, false, false,
false , false, false,
false, false, false,
false, false, false]</rosparam>
<rosparam param="imu0_config">[false, false, false,
true , true , true,
false, false, false,
true , true , true ,
true , true , true ]</rosparam>
<param name="odom0_differential" value="false"/>
<param name="imu0_differential" value="false"/>
<param name="imu0_remove_gravitational_acceleration" value="true"/>
<param name="odom0_relative" value="false"/>
<param name="imu0_relative" value="false"/>
<param name="print_diagnostics" value="true"/>
<!-- ======== ADVANCED PARAMETERS ======== -->
<param name="odom0_queue_size" value="2"/>
<param name="imu0_queue_size" value="10"/>
</node>
<node name="navsat_transform" pkg="robot_localization" type="navsat_transform_node" output="screen">
<param name="broadcast_utm_transform" value="true"/>
</node>
</group>
</launch>

View File

@@ -0,0 +1,71 @@
<launch>
<!-- https://github.com/vikiboy/AGV_Localization/blob/master/robot_localization/launch/ekf.launch -->
<!-- https://answers.ros.org/question/241222/fusing-imu-gps-with-robot_location-package/ -->
<group ns="rover_odometry">
<!-- ########## Processes GPS and IMU Messages ########## -->
<node name="gps_and_imu" pkg="rover_odometry" type="odometry.py" respawn="true" output="screen" >
<!--<param name="gps_sentence_topic" value="/nmea_sentence"/>-->
<!--<param name="imu_data_topic" value="/imu/data"/>-->
<!--<remap from="nmea_sentence" to="gps/sentence"/>-->
<!--<remap from="imu/data" to="/imu/data"/>-->
</node>
<!-- ########## Converts GPS Sentences to GPS Fix data ########## -->
<node name="navsat_driver" pkg="nmea_navsat_driver" type="nmea_topic_driver" output="screen">
<remap from="nmea_sentence" to="gps/sentence"/>
</node>
<!--<node pkg="tf" type="static_transform_publisher" name="imu_tf" args="0 0 0 0 0 0 1 base_link imu 20"/>-->
<!--<node pkg="tf" type="static_transform_publisher" name="gps_tf" args="0 0 0 0 0 0 1 base_link gps 20"/>-->
<!--<node pkg="robot_localization" type="ekf_localization_node" name="ekf_localization" clear_params="true">-->
<!--<param name="output_frame" value="odom"/>-->
<!--<param name="frequency" value="20"/>-->
<!--<param name="odom_used" value="true"/>-->
<!--<param name="imu_used" value="true"/>-->
<!--<param name="vo_used" value="false"/>-->
<!--<param name="sensor_timeout" value="0.1"/>-->
<!--<param name="two_d_mode" value="false"/>-->
<!--<param name="map_frame" value="map"/>-->
<!--<param name="odom_frame" value="odom"/>-->
<!--<param name="base_link_frame" value="base_link"/>-->
<!--<param name="world_frame" value="odom"/>-->
<!--<param name="odom0" value="odometry/gps"/>-->
<!--<param name="imu0" value="imu/data"/>-->
<!--<rosparam param="odom0_config">[true, true, false,-->
<!--false, false, false,-->
<!--false , false, false,-->
<!--false, false, false,-->
<!--false, false, false]</rosparam>-->
<!--<rosparam param="imu0_config">[false, false, false,-->
<!--true , true , true,-->
<!--false, false, false,-->
<!--true , true , true ,-->
<!--true , true , true ]</rosparam>-->
<!--<param name="odom0_differential" value="false"/>-->
<!--<param name="imu0_differential" value="false"/>-->
<!--<param name="imu0_remove_gravitational_acceleration" value="true"/>-->
<!--<param name="odom0_relative" value="false"/>-->
<!--<param name="imu0_relative" value="false"/>-->
<!--<param name="print_diagnostics" value="true"/>-->
<!--&lt;!&ndash; ======== ADVANCED PARAMETERS ======== &ndash;&gt;-->
<!--<param name="odom0_queue_size" value="2"/>-->
<!--<param name="imu0_queue_size" value="10"/>-->
<!--</node>-->
<!--<node name="navsat_transform" pkg="robot_localization" type="navsat_transform_node" output="screen">-->
<!--<param name="broadcast_utm_transform" value="true"/>-->
<!--</node>-->
</group>
</launch>

View File

@@ -2,7 +2,7 @@
<group ns="sender_transports">
<arg name="target" default="192.168.1.15" />
<node name="chassis_1280x720" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="chassis_1280x720" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17001" />
<rosparam param="topics">
@@ -10,7 +10,7 @@
</rosparam>
</node>
<node name="undercarriage_1280x720" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="undercarriage_1280x720" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17002" />
<rosparam param="topics">
@@ -18,7 +18,7 @@
</rosparam>
</node>
<node name="main_navigation_1280x720" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="main_navigation_1280x720" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17003" />
<rosparam param="topics">
@@ -26,7 +26,7 @@
</rosparam>
</node>
<node name="end_effector_1280x720" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="end_effector_1280x720" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17004" />
<rosparam param="topics">
@@ -34,7 +34,7 @@
</rosparam>
</node>
<node name="chassis_640x360" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="chassis_640x360" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17005" />
<rosparam param="topics">
@@ -42,7 +42,7 @@
</rosparam>
</node>
<node name="undercarriage_640x360" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="undercarriage_640x360" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17006" />
<rosparam param="topics">
@@ -50,7 +50,7 @@
</rosparam>
</node>
<node name="main_navigation_640x360" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="main_navigation_640x360" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17007" />
<rosparam param="topics">
@@ -58,7 +58,7 @@
</rosparam>
</node>
<node name="end_effector_640x360" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="end_effector_640x360" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17008" />
<rosparam param="topics">
@@ -66,7 +66,7 @@
</rosparam>
</node>
<node name="chassis_256x144" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="chassis_256x144" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17009" />
<rosparam param="topics">
@@ -74,7 +74,7 @@
</rosparam>
</node>
<node name="undercarriage_256x144" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="undercarriage_256x144" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17010" />
<rosparam param="topics">
@@ -82,7 +82,7 @@
</rosparam>
</node>
<node name="main_navigation_256x144" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="main_navigation_256x144" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17011" />
<rosparam param="topics">
@@ -90,7 +90,7 @@
</rosparam>
</node>
<node name="end_effector_256x144" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="end_effector_256x144" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17012" />
<rosparam param="topics">
@@ -98,7 +98,7 @@
</rosparam>
</node>
<node name="bogie_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="bogie_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17013" />
<rosparam param="topics">
@@ -106,7 +106,7 @@
</rosparam>
</node>
<node name="camera_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="camera_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17014" />
<rosparam param="topics">
@@ -114,7 +114,7 @@
</rosparam>
</node>
<node name="frsky_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="frsky_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17015" />
<rosparam param="topics">
@@ -122,15 +122,16 @@
</rosparam>
</node>
<node name="gps_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="gps_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17016" />
<rosparam param="topics">
[{name: "/rover_status/gps_status", compress: false, rate: 1.0}]
[{name: "/rover_status/gps_status", compress: false, rate: 1.0},
{name: "/rover_odometry/fix", compress: false, rate: 5.0}]
</rosparam>
</node>
<node name="jetson_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="jetson_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17017" />
<rosparam param="topics">
@@ -138,7 +139,7 @@
</rosparam>
</node>
<node name="misc_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender" output="screen">
<node name="misc_status_tcp" pkg="nimbro_topic_transport" type="tcp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17018" />
<rosparam param="topics">
@@ -146,13 +147,13 @@
</rosparam>
</node>
<node name="udp_statuses_sender" pkg="nimbro_topic_transport" type="udp_sender" output="screen">
<node name="udp_statuses_sender" pkg="nimbro_topic_transport" type="udp_sender">
<param name="destination_addr" value="$(arg target)" />
<param name="destination_port" value="17019" />
<rosparam param="topics">
[
{name: "/rover_status/gps_status", compress: true, rate: 5.0},
{name: "/rover_status/battery_status", compress: false, rate: 1.0}
{name: "/rover_status/battery_status", compress: false, rate: 1.0},
{name: "/rover_control/tower/status/co2", compress: false, rate: 1.0}
]
</rosparam>
</node>

View File

@@ -0,0 +1,204 @@
cmake_minimum_required(VERSION 2.8.3)
project(rover_odometry)
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
################################################
## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a run_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
add_message_files(
FILES
)
## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )
## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a run_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES system_statuses
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/system_statuses.cpp
# )
## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/system_statuses_node.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
#############
## Install ##
#############
# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# install(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark executables and/or libraries for installation
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )
## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
#############
## Testing ##
#############
## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_system_statuses.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

View File

@@ -0,0 +1,68 @@
<?xml version="1.0"?>
<package format="2">
<name>rover_odometry</name>
<version>0.0.0</version>
<description>The rover_tower package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="none@none.com">none</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>BSD</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/rover_status</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<build_depend>message_generation</build_depend>
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<exec_depend>message_runtime</exec_depend>
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>

View File

@@ -0,0 +1,143 @@
#!/usr/bin/env python
#####################################
# Imports
#####################################
# Python native imports
import rospy
import serial
from time import time, sleep
import json
import re
from nmea_msgs.msg import Sentence
from sensor_msgs.msg import Imu
#####################################
# Global Variables
#####################################
NODE_NAME = "tower_odometry"
DEFAULT_PORT = "/dev/rover/ttyOdometry"
# DEFAULT_PORT = "/dev/ttyUSB0"
DEFAULT_BAUD = 115200
# DEFAULT_GPS_SENTENCE_TOPIC = "gps/sentence"
DEFAULT_GPS_SENTENCE_TOPIC = "gps/sentence"
DEFAULT_IMU_TOPIC = "imu/data"
DEFAULT_HERTZ = 100
#####################################
# DriveControl Class Definition
#####################################
class Odometry(object):
def __init__(self):
rospy.init_node(NODE_NAME)
self.port = rospy.get_param("~port", DEFAULT_PORT)
self.baud = rospy.get_param("~baud", DEFAULT_BAUD)
self.gps_sentence_topic = rospy.get_param("~gps_sentence_topic", DEFAULT_GPS_SENTENCE_TOPIC)
self.imu_data_topic = rospy.get_param("~imu_data_topic", DEFAULT_IMU_TOPIC)
self.wait_time = 1.0 / rospy.get_param("~hertz", DEFAULT_HERTZ)
self.odom_serial = serial.Serial(port=self.port, baudrate=self.baud)
self.odom_serial.setRTS(0)
self.sentence_publisher = rospy.Publisher(self.gps_sentence_topic, Sentence, queue_size=1)
self.imu_data_publisher = rospy.Publisher(self.imu_data_topic, Imu, queue_size=1)
self.run()
def run(self):
while not rospy.is_shutdown():
start_time = time()
try:
self.process_messages()
except Exception, error:
print "Error occurred:", error
time_diff = time() - start_time
sleep(max(self.wait_time - time_diff, 0))
def process_messages(self):
if self.odom_serial.inWaiting():
line = self.odom_serial.readline()
temp = json.loads(line)
gps = temp.get('gps', None)
imu = temp.get('imu', None)
if gps:
# ###### THIS IS HERE TO DEAL WITH UBLOX GPS #####
if "GNGGA" in gps:
gps = gps.replace("GNGGA", "GPGGA")
gps = gps[:-2] + str(self.chksum_nmea(gps))[2:]
# print gps
# #####
self.broadcast_gps(gps)
if imu:
# print imu
self.broadcast_imu(imu)
@staticmethod
def chksum_nmea(sentence):
# String slicing: Grabs all the characters
# between '$' and '*' and nukes any lingering
# newline or CRLF
chksumdata = re.sub("(\n|\r\n)", "", sentence[sentence.find("$") + 1:sentence.find("*")])
# Initializing our first XOR value
csum = 0
# For each char in chksumdata, XOR against the previous
# XOR'd char. The final XOR of the last char will be our
# checksum to verify against the checksum we sliced off
# the NMEA sentence
for c in chksumdata:
# XOR'ing value of csum against the next char in line
# and storing the new XOR value in csum
csum ^= ord(c)
# Do we have a validated sentence?
return hex(csum)
def broadcast_gps(self, gps):
message = Sentence()
message.header.frame_id = "gps"
message.header.stamp = rospy.get_rostime()
message.sentence = gps
self.sentence_publisher.publish(message)
def broadcast_imu(self, imu):
message = Imu()
message.header.frame_id = "imu"
message.header.stamp = rospy.get_rostime()
message.orientation.x = imu["ox"]
message.orientation.y = imu["oy"]
message.orientation.z = imu["oz"]
message.orientation.w = imu["ow"]
message.angular_velocity.x = imu["avx"]
message.angular_velocity.y = imu["avy"]
message.angular_velocity.z = imu["avz"]
message.linear_acceleration.x = imu["lax"]
message.linear_acceleration.y = imu["lay"]
message.linear_acceleration.z = imu["laz"]
self.imu_data_publisher.publish(message)
if __name__ == "__main__":
Odometry()

View File

@@ -36,7 +36,7 @@ DEFAULT_IRIS_STATUS_TOPIC_NAME = "/rover_control/iris_status"
DEFAULT_BOGIE_LEFT_TOPIC_NAME = '/rover_control/drive_status/left'
DEFAULT_BOGIE_RIGHT_TOPIC_NAME = '/rover_control/drive_status/right'
DEFAULT_BOGIE_REAR_TOPIC_NAME = '/rover_control/drive_status/rear'
DEFAULT_GPS_NMEA_TOPIC_NAME = '/nmea_sentence'
DEFAULT_GPS_NMEA_TOPIC_NAME = '/rover_odometry/gps/sentence'
#####################################
@@ -130,7 +130,11 @@ class SystemStatuses:
# Pulls the UTC GPS Information (WIP v2.0)
def __set_gps_info(self, data):
self.GPS_msg.gps_connected = True
self.Nmea_Message = pynmea2.parse(data.sentence)
try:
self.Nmea_Message = pynmea2.parse(data.sentence)
except:
return
if self.Nmea_Message.sentence_type == 'GGA':
if int(self.Nmea_Message.gps_qual) != 0:
self.GPS_msg.gps_fix = True

View File

@@ -14,6 +14,7 @@ folders_to_link=(
zed_wrapper
nimbro_topic_transport
rover_main
rover_odometry
)
# Print heading

View File

@@ -1,6 +1,6 @@
import paramiko
import json
from pprint import pprint
import time
# ath0 21 channels in total; available frequencies :
# Channel 01 : 2.412 GHz
@@ -43,12 +43,25 @@ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
# Before anyone complains, I'm not worried about this password being online.
# We only set one because the web interfaces HAVE to have one
ssh.connect("192.168.1.20", username="ubnt", password="rover4lyfe^", compress=True)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(get_general_info)
pprint( json.loads(ssh_stdout.read()) )
while True:
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(get_general_info)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(set_wireless_frequency)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(get_wireless_info)
output_json = json.loads(ssh_stdout.read())[0]
successful_transmit_percent = output_json["ccq"]
quality = output_json["airmax"]["quality"]
capacity = output_json["airmax"]["capacity"]
rx_rate = output_json["rx"]
tx_rate = output_json["tx"]
ground_tx_latency = output_json["tx_latency"]
rover_tx_latency = output_json["remote"]["tx_latency"]
print successful_transmit_percent, " | ", quality, " | ", capacity, " | ", rx_rate, " | ", tx_rate, " | ", ground_tx_latency, " | ", rover_tx_latency
time.sleep(0.25)
# ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(set_wireless_frequency)
# ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(get_wireless_info)
#
# print ssh_stdout.read()
print ssh_stdout.read()